Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 12 additions & 17 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Project Overview

This is a RESTful Web Service proof-of-concept built with **Spring Boot 3** targeting **JDK 21 (LTS)**. The application demonstrates a clean, layered architecture implementing a CRUD API for managing books. It uses an in-memory H2 database for data persistence and includes comprehensive test coverage.
This is a RESTful Web Service proof-of-concept built with **Spring Boot 4** targeting **JDK 25 (LTS)**. The application demonstrates a clean, layered architecture implementing a CRUD API for managing books. It uses an in-memory H2 database for data persistence and includes comprehensive test coverage.

**Key URLs:**

Expand All @@ -14,8 +14,8 @@ This is a RESTful Web Service proof-of-concept built with **Spring Boot 3** targ

### Core Framework & Runtime

- **Java**: JDK 21 (LTS) - use modern Java features where appropriate
- **Spring Boot**: 3.4.4 with starter dependencies (Web, Data JPA, Validation, Cache, Actuator)
- **Java**: JDK 25 (LTS) - use modern Java features where appropriate
- **Spring Boot**: 4.0.0 with modular starter dependencies (WebMVC, Data JPA, Validation, Cache, Actuator)
- **Build Tool**: Maven 3.9+ (use `./mvnw` wrapper, NOT system Maven)
- **Database**: H2 in-memory database (runtime scope)

Expand All @@ -31,15 +31,14 @@ This is a RESTful Web Service proof-of-concept built with **Spring Boot 3** targ

- **JUnit 5** (Jupiter): Test framework
- **Mockito**: Mocking framework
- **Spring Boot Test**: `@WebMvcTest`, `@DataJpaTest`, etc.
- **Spring Boot Test**: `@WebMvcTest`, `@DataJpaTest`, `@AutoConfigureCache`, etc.
- **AssertJ**: Preferred assertion library (use over standard JUnit assertions)

### DevOps & CI/CD

- **Docker**: Multi-stage build with Eclipse Temurin Alpine images
- **Docker Compose**: Local containerized deployment
- **GitHub Actions**: CI pipeline with coverage reporting (Codecov, Codacy)
- **Azure Pipelines**: Alternative CI configuration

## Project Structure

Expand Down Expand Up @@ -90,17 +89,18 @@ scripts/

- **Lombok**: Prefer `@Data`, `@RequiredArgsConstructor`, `@NoArgsConstructor`, `@AllArgsConstructor` over manual code
- **Streams**: Use Java Streams API for collection processing (see `BooksService.retrieveAll()`)
- **Modern Java**: Leverage JDK 21 features (records, pattern matching, etc.) where beneficial
- **Modern Java**: Leverage JDK 25 features (records, pattern matching, sealed classes, etc.) where beneficial
- **Comments**: Section dividers used in controllers/services (e.g., `/* HTTP POST */`)

### Testing Conventions

- **Test Class Naming**: `<ClassName>Tests` (plural, e.g., `BooksControllerTests`)
- **Test Method Naming**: `given<Condition>_when<Action>_then<Expected>` (BDD style)
- **Assertions**: Use AssertJ fluent assertions (`assertThat(...).isEqualTo(...)`)
- **Mocking**: Use `@MockBean` for Spring beans, verify interactions with `verify()`
- **Mocking**: Use `@MockitoBean` for Spring beans (new in Spring Boot 4.0), verify interactions with `verify()`
- **Test Data**: Use fake data factories (`BookFakes`, `BookDTOFakes`) for consistent test data
- **Display Names**: Use `@DisplayName` for readable test descriptions
- **Caching in Tests**: Add `@AutoConfigureCache` to slice tests (`@WebMvcTest`, `@DataJpaTest`) when caching is needed

### Coverage Exclusions

Expand Down Expand Up @@ -137,8 +137,8 @@ JaCoCo excludes from coverage (see `pom.xml` and `codecov.yml`):
**Critical Requirements**:

- **ALWAYS use `./mvnw` wrapper** (Unix/macOS) or `mvnw.cmd` (Windows), NOT `mvn`
- **JDK 21 is REQUIRED**: The project targets JDK 21 (LTS). Using newer JDKs (22+) will cause Mockito/ByteBuddy compatibility issues in tests
- **JAVA_HOME must be set**: Maven wrapper requires JAVA_HOME pointing to JDK 21 installation
- **JDK 25 is REQUIRED**: The project targets JDK 25 (LTS)
- **JAVA_HOME must be set**: Maven wrapper requires JAVA_HOME pointing to JDK 25 installation

### Docker Commands

Expand Down Expand Up @@ -205,8 +205,8 @@ docker compose logs -f
- **Lombok not working**: Ensure annotation processor is enabled in IDE and `maven-compiler-plugin` includes Lombok path
- **Tests failing**: Check if H2 database is properly initialized; review `BooksDataInitializer.seed()`
- **Port already in use**: Change `server.port` in `application.properties` or kill process using ports 9000/9001
- **JAVA_HOME not set**: Run `export JAVA_HOME=$(/usr/libexec/java_home -v 21)` on macOS or set to JDK 21 path on other systems
- **JDK version errors**: Project requires JDK 21 (LTS). Using JDK 22+ causes Mockito/ByteBuddy failures like "Java 25 (69) is not supported"
- **JAVA_HOME not set**: Run `export JAVA_HOME=$(/usr/libexec/java_home -v 25)` on macOS or set to JDK 25 path on other systems
- **CacheManager errors in tests**: Add `@AutoConfigureCache` annotation to slice tests (`@WebMvcTest`, `@DataJpaTest`)

### Docker Issues

Expand All @@ -216,10 +216,10 @@ docker compose logs -f
### Common Pitfalls

- **Don't use system Maven**: Always use `./mvnw` wrapper
- **Don't use newer JDKs**: Stick to JDK 21 - newer versions break Mockito in tests
- **Don't modify `Application.java` coverage**: It's excluded by design
- **Don't test Lombok-generated code**: Focus on business logic
- **Repository interfaces**: Custom query methods may not show in coverage (JaCoCo limitation)
- **Spring Boot 4.0 modular packages**: Test annotations like `@WebMvcTest`, `@DataJpaTest`, and `@AutoConfigureCache` are now in modular packages (e.g., `org.springframework.boot.webmvc.test.autoconfigure.WebMvcTest`)

## CI/CD Pipeline

Expand All @@ -235,11 +235,6 @@ docker compose logs -f
- `CODACY_PROJECT_TOKEN`: Codacy integration token
- `GITHUB_TOKEN`: Automatically provided for GHCR push

### Azure Pipelines (`azure-pipelines.yml`)

- Runs on `ubuntu-latest` with JDK 21
- Executes `mvn package` and publishes test results

## Contributing

Follow [Conventional Commits](https://www.conventionalcommits.org/):
Expand Down
180 changes: 90 additions & 90 deletions .github/workflows/maven.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,99 +4,99 @@
name: Java CI

permissions:
contents: read
contents: read

on:
push:
branches: [master]
pull_request:
branches: [master]
push:
branches: [master]
pull_request:
branches: [master]

env:
JAVA_VERSION: 21
JAVA_VERSION: 25

jobs:
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.0

- name: Set up OpenJDK ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v5.0.0
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: "temurin"
cache: "maven"

- name: Compile and verify with Maven
run: mvn verify --file pom.xml

- name: Upload JaCoCo coverage report artifact
uses: actions/upload-artifact@v5.0.0
with:
name: jacoco.xml
path: ./target/site/jacoco/jacoco.xml

coverage:
needs: verify
runs-on: ubuntu-latest
strategy:
matrix:
service: [codecov, codacy]
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.0

- name: Download JaCoCo coverage report artifact
uses: actions/download-artifact@v6.0.0
with:
name: jacoco.xml

- name: Upload JaCoCo coverage report to Codecov
if: matrix.service == 'codecov'
uses: codecov/codecov-action@v5.5.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: jacoco.xml

- name: Upload JaCoCo coverage report to Codacy
if: matrix.service == 'codacy'
uses: codacy/codacy-coverage-reporter-action@v1.3.0
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
force-coverage-parser: jacoco -r jacoco.xml

container:
needs: coverage
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}

permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v6.0.0

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

- name: Build and push Docker image to GitHub Container Registry
uses: docker/build-push-action@v6.18.0
with:
context: .
push: true
platforms: linux/amd64
provenance: false
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:sha-${{ github.sha }}
verify:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.0

- name: Set up OpenJDK ${{ env.JAVA_VERSION }}
uses: actions/setup-java@v5.0.0
with:
java-version: ${{ env.JAVA_VERSION }}
distribution: "temurin"
cache: "maven"

- name: Compile and verify with Maven
run: mvn verify --file pom.xml

- name: Upload JaCoCo coverage report artifact
uses: actions/upload-artifact@v5.0.0
with:
name: jacoco.xml
path: ./target/site/jacoco/jacoco.xml

coverage:
needs: verify
runs-on: ubuntu-latest
strategy:
matrix:
service: [codecov, codacy]
steps:
- name: Checkout repository
uses: actions/checkout@v6.0.0

- name: Download JaCoCo coverage report artifact
uses: actions/download-artifact@v6.0.0
with:
name: jacoco.xml

- name: Upload JaCoCo coverage report to Codecov
if: matrix.service == 'codecov'
uses: codecov/codecov-action@v5.5.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: jacoco.xml

- name: Upload JaCoCo coverage report to Codacy
if: matrix.service == 'codacy'
uses: codacy/codacy-coverage-reporter-action@v1.3.0
with:
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
force-coverage-parser: jacoco -r jacoco.xml

container:
needs: coverage
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/master' }}

permissions:
contents: read
packages: write

steps:
- name: Checkout repository
uses: actions/checkout@v6.0.0

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3.6.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3.11.1

- name: Build and push Docker image to GitHub Container Registry
uses: docker/build-push-action@v6.18.0
with:
context: .
push: true
platforms: linux/amd64
provenance: false
tags: |
ghcr.io/${{ github.repository }}:latest
ghcr.io/${{ github.repository }}:sha-${{ github.sha }}
2 changes: 1 addition & 1 deletion .java-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
21
25
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Stage 1: Builder
# This stage builds the application and its dependencies.
# ------------------------------------------------------------------------------
FROM maven:3.9-eclipse-temurin-21-alpine AS builder
FROM maven:3.9-eclipse-temurin-25-alpine AS builder

WORKDIR /app

Expand All @@ -19,7 +19,7 @@ RUN mvn clean package -DskipTests
# Stage 2: Runtime
# This stage creates the final, minimal image to run the application.
# ------------------------------------------------------------------------------
FROM eclipse-temurin:21-jdk-alpine AS runtime
FROM eclipse-temurin:25-jdk-alpine AS runtime

WORKDIR /app

Expand All @@ -28,7 +28,7 @@ RUN apk add --no-cache curl

# Metadata labels for the image. These are useful for registries and inspection.
LABEL org.opencontainers.image.title="🧪 RESTful Web Service with Spring Boot"
LABEL org.opencontainers.image.description="Proof of Concept for a RESTful Web Service made with JDK 21 (LTS) and Spring Boot 3"
LABEL org.opencontainers.image.description="Proof of Concept for a RESTful Web Service made with JDK 25 (LTS) and Spring Boot 4"
LABEL org.opencontainers.image.licenses="MIT"
LABEL org.opencontainers.image.source="https://github.com/nanotaboada/java.samples.spring.boot"

Expand Down
15 changes: 7 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
[![CodeFactor](https://www.codefactor.io/repository/github/nanotaboada/java.samples.spring.boot/badge)](https://www.codefactor.io/repository/github/nanotaboada/java.samples.spring.boot)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

> A production-ready RESTful Web Service demonstrating clean architecture patterns with Spring Boot 3, comprehensive testing, and containerized deployment.
> A production-ready RESTful Web Service demonstrating clean architecture patterns with Spring Boot 4, comprehensive testing, and containerized deployment.

## Table of Contents

Expand All @@ -26,7 +26,7 @@

## About

A proof-of-concept RESTful Web Service built with **Spring Boot 3** targeting **JDK 21 (LTS)**. This project demonstrates best practices for building a layered, testable, and maintainable API implementing CRUD operations for a Books resource.
A proof-of-concept RESTful Web Service built with **Spring Boot 4** targeting **JDK 25 (LTS)**. This project demonstrates best practices for building a layered, testable, and maintainable API implementing CRUD operations for a Books resource.

The service showcases:

Expand All @@ -48,7 +48,7 @@ The service showcases:
- ✅ **Health Monitoring** - Spring Boot Actuator endpoints
- ✅ **Test Coverage** - JaCoCo reports with Codecov/Codacy integration
- ✅ **Docker Support** - Multi-stage builds with Eclipse Temurin Alpine images
- ✅ **CI/CD Ready** - GitHub Actions and Azure Pipelines configurations
- ✅ **CI/CD Ready** - GitHub Actions with automated testing and container builds

## Architecture

Expand All @@ -66,16 +66,15 @@ _Figure: Simplified, conceptual project structure and main application flow. Not

Before you begin, ensure you have the following installed:

- **Java Development Kit (JDK) 21** - [Download](https://adoptium.net/temurin/releases/?version=21)
- ⚠️ **Critical**: JDK 21 is required. Newer versions (22+) cause Mockito/ByteBuddy compatibility issues.
- **Java Development Kit (JDK) 25** - [Download](https://adoptium.net/temurin/releases/?version=25)
- Verify with: `java -version`
- **Maven 3.9+** (optional) - Project includes Maven wrapper (`./mvnw`)
- **Docker** (optional) - For containerized deployment

**macOS Users**: Set `JAVA_HOME` if needed:

```bash
export JAVA_HOME=$(/usr/libexec/java_home -v 21)
export JAVA_HOME=$(/usr/libexec/java_home -v 25)
```

## Getting Started
Expand Down Expand Up @@ -180,8 +179,8 @@ open target/site/jacoco/index.html

**Test Structure:**

- **Unit Tests** - `@WebMvcTest`, `@DataJpaTest` for isolated layer testing
- **Mocking** - Mockito for dependency mocking
- **Unit Tests** - `@WebMvcTest`, `@DataJpaTest` for isolated layer testing (with `@AutoConfigureCache` for caching support)
- **Mocking** - Mockito with `@MockitoBean` for dependency mocking
- **Assertions** - AssertJ fluent assertions
- **Naming Convention** - BDD style: `given<Condition>_when<Action>_then<Expected>`

Expand Down
Loading