From 5936c65fb3a9c80c966f4c3e9a9d0682a2752772 Mon Sep 17 00:00:00 2001 From: Nano Taboada Date: Wed, 3 Dec 2025 00:29:12 -0300 Subject: [PATCH] feat: upgrade to Spring Boot 4.0 and JDK 25 LTS - Upgrade Spring Boot from 3.4.4 to 4.0.0 - Upgrade JDK from 21 to 25 (LTS) - Migrate spring-boot-starter-web to spring-boot-starter-webmvc - Add modular test starters (webmvc-test, data-jpa-test, cache-test) - Update @WebMvcTest import to org.springframework.boot.webmvc.test.autoconfigure - Update @DataJpaTest import to org.springframework.boot.data.jpa.test.autoconfigure - Add @AutoConfigureCache to slice tests for CacheManager support - Update Docker images to eclipse-temurin:25-jdk/jre-alpine - Update GitHub Actions to use JDK 25 --- .github/copilot-instructions.md | 29 ++- .github/workflows/maven.yml | 180 ++++++++--------- .java-version | 2 +- Dockerfile | 6 +- README.md | 15 +- azure-pipelines.yml | 22 --- pom.xml | 181 +++++++++++++----- .../controllers/BooksControllerTests.java | 10 +- .../repositories/BooksRepositoryTests.java | 4 +- 9 files changed, 251 insertions(+), 198 deletions(-) delete mode 100644 azure-pipelines.yml diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ffd1883..41281d1 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -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:** @@ -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) @@ -31,7 +31,7 @@ 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 @@ -39,7 +39,6 @@ This is a RESTful Web Service proof-of-concept built with **Spring Boot 3** targ - **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 @@ -90,7 +89,7 @@ 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 @@ -98,9 +97,10 @@ scripts/ - **Test Class Naming**: `Tests` (plural, e.g., `BooksControllerTests`) - **Test Method Naming**: `given_when_then` (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 @@ -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 @@ -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 @@ -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 @@ -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/): diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 9b0192a..e874f8b 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -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 }} diff --git a/.java-version b/.java-version index aabe6ec..7273c0f 100644 --- a/.java-version +++ b/.java-version @@ -1 +1 @@ -21 +25 diff --git a/Dockerfile b/Dockerfile index 7cea3e1..0a083b1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -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 @@ -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 @@ -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" diff --git a/README.md b/README.md index 6bf8986..66d32bd 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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: @@ -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 @@ -66,8 +66,7 @@ _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 @@ -75,7 +74,7 @@ Before you begin, ensure you have the following installed: **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 @@ -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_when_then` diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index 310b84c..0000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,22 +0,0 @@ -# Maven -# Build your Java project and run tests with Apache Maven. -# Add steps that analyze code, save build artifacts, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/languages/java - -trigger: -- master - -pool: - vmImage: ubuntu-latest - -steps: -- task: Maven@3 - inputs: - mavenPomFile: 'pom.xml' - mavenOptions: '-Xmx3072m' - javaHomeOption: 'JDKVersion' - jdkVersionOption: '21' - jdkArchitectureOption: 'x64' - publishJUnitResults: true - testResultsFiles: '**/surefire-reports/TEST-*.xml' - goals: 'package' diff --git a/pom.xml b/pom.xml index 3de1195..479447c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,87 +6,147 @@ 4.0.0 - + org.springframework.boot spring-boot-starter-parent - 3.4.4 + 4.0.0 ar.com.nanotaboada java.samples.spring.boot - 3.2.2024 + 4.0.2025 java.samples.spring.boot - PoC for a REST API made with Spring Boot using Initializr + A RESTful Web Service demonstrating clean architecture patterns with Spring Boot 4 - 21 + 25 1.18.42 + - + + + + + + org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc - + org.springframework.boot - spring-boot-starter-test + spring-boot-starter-webmvc-test test - + org.springframework.boot - spring-boot-starter-data-jpa + spring-boot-starter-validation - + - org.springframework.boot - spring-boot-starter-validation + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.8.14 - + + + + + + org.springframework.boot spring-boot-starter-cache - + org.springframework.boot - spring-boot-starter-actuator + spring-boot-starter-cache-test + test + + + + + org.modelmapper + modelmapper + 3.2.6 + + + + + + + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + org.springframework.boot + spring-boot-starter-data-jpa-test + test - + + + + + + + - org.assertj - assertj-core - 3.27.6 - test + org.springframework.boot + spring-boot-starter-actuator - + + + + + + - org.modelmapper - modelmapper - 3.2.6 + org.springframework.boot + spring-boot-starter-test + test - + - org.springdoc - springdoc-openapi-starter-webmvc-ui - 2.8.14 + org.assertj + assertj-core + 3.27.6 + test + + + + + + @@ -172,7 +249,7 @@ - + @@ -180,7 +257,7 @@ org.springframework.boot spring-boot-maven-plugin - +