From 78368418cd2451f22cf78cb8d336d904e49001f4 Mon Sep 17 00:00:00 2001 From: Thomas Juul Date: Wed, 4 Nov 2026 11:23:46 +0100 Subject: [PATCH 1/2] Upgrade application to Java 25 and Spring Boot 4 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../.metadata/.plan-content-hash | 2 + .../.metadata/tasks.json | 35 ++++++ .../modernization-summary.md | 6 + .../001-upgrade-java-spring-boot/plan.md | 115 ++++++++++++++++++ .../001-upgrade-java-spring-boot/progress.md | 107 ++++++++++++++++ .../upgrade-to-lts-20261104100839/plan.md | 7 ++ Dockerfile | 8 +- README.md | 6 +- pom.xml | 20 +-- .../com/photoalbum/config/SecurityConfig.java | 14 +-- src/main/java/com/photoalbum/model/Photo.java | 10 +- .../PhotoAlbumApplicationTests.java | 29 +++++ 12 files changed, 333 insertions(+), 26 deletions(-) create mode 100644 .github/modernize/upgrade-to-lts-20261104100839/.metadata/.plan-content-hash create mode 100644 .github/modernize/upgrade-to-lts-20261104100839/.metadata/tasks.json create mode 100644 .github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/modernization-summary.md create mode 100644 .github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/plan.md create mode 100644 .github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/progress.md create mode 100644 .github/modernize/upgrade-to-lts-20261104100839/plan.md diff --git a/.github/modernize/upgrade-to-lts-20261104100839/.metadata/.plan-content-hash b/.github/modernize/upgrade-to-lts-20261104100839/.metadata/.plan-content-hash new file mode 100644 index 000000000..86eb03c44 --- /dev/null +++ b/.github/modernize/upgrade-to-lts-20261104100839/.metadata/.plan-content-hash @@ -0,0 +1,2 @@ +995d5e6acfd21f99f07dd44bca1c59d6cb51882c101f677ac6ddae4948f39d8d +e8c7660abb6d8d810a4e4d9fc723da88c50aa76639f80e09afca4c6ac3011957 diff --git a/.github/modernize/upgrade-to-lts-20261104100839/.metadata/tasks.json b/.github/modernize/upgrade-to-lts-20261104100839/.metadata/tasks.json new file mode 100644 index 000000000..b71133b5f --- /dev/null +++ b/.github/modernize/upgrade-to-lts-20261104100839/.metadata/tasks.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "description": "Upgrade plan to migrate project to latest LTS versions", + "tasks": [ + { + "type": "upgrade", + "id": "001-upgrade-java-spring-boot", + "description": "Upgrade to Java 25 and Spring Boot 4.x", + "requirements": "Upgrade the application to Java 25, Spring Boot 4.x, and Spring Framework 7.x. Migrate Java EE APIs from javax.* to jakarta.* where required.", + "skills": [ + { + "name": "java-version-upgrade", + "location": "builtin" + } + ], + "successCriteria": { + "passBuild": "true", + "passUnitTests": "true" + }, + "status": "success", + "taskSummary": "Upgraded the project to Java 25, Spring Boot 4.1.2, and Spring Framework 7.0.10; migrated persistence and validation APIs to Jakarta; updated the security configuration, Oracle JDBC driver, Docker images, and documentation; and added security regression tests. Clean Java 25 verification passed with all 4 tests passing. A full CVE scan was unavailable.", + "successCriteriaStatus": { + "passBuild": "true", + "passUnitTests": "true" + } + } + ], + "metadata": { + "planName": "upgrade-to-lts-20261104100839", + "projectName": "PhotoAlbum-Java", + "language": "java", + "createdAt": "2026-11-04T10:08:43.921Z", + "version": "1.0" + } +} diff --git a/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/modernization-summary.md b/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/modernization-summary.md new file mode 100644 index 000000000..c405eeee8 --- /dev/null +++ b/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/modernization-summary.md @@ -0,0 +1,6 @@ +finalStatus: success +successCriteriaStatus: + passBuild: true + generateNewUnitTests: true + passUnitTests: true +summary: "Upgraded PhotoAlbum-Java to Java 25, Spring Boot 4.1.2, and Spring Framework 7.0.10; migrated persistence/validation APIs to Jakarta, updated security and Oracle JDBC compatibility, refreshed Docker/docs, and added security regression tests. Clean Java 25 verification passed and all 4 tests pass." \ No newline at end of file diff --git a/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/plan.md b/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/plan.md new file mode 100644 index 000000000..3ac153df6 --- /dev/null +++ b/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/plan.md @@ -0,0 +1,115 @@ +# Upgrade Plan: PhotoAlbum-Java (001-upgrade-java-spring-boot) + +- **Generated**: 2026-11-04 11:15 +01:00 +- **HEAD Branch**: N/A (version-control MCP operation unavailable) +- **HEAD Commit ID**: N/A (version-control MCP operation unavailable) + +## Available Tools + +**JDKs** +- JDK 8: not available locally (baseline may be attempted in Docker image `maven:3.9.6-eclipse-temurin-8`) +- JDK 25: use Docker image `maven:3.9.11-eclipse-temurin-25` (not installed locally) + +**Build Tools** +- Docker CLI 29.8.1: available; Docker image supplies Maven 3.9.11 +- Maven Wrapper: absent + +## Guidelines +- Upgrade to Java 25, Spring Boot 4.x, Spring Framework 7.x, and use Jakarta APIs where required. +- Fully automatic execution; do not pause for approval. +> Note: You can add any specific guidelines or constraints for the upgrade process here if needed, bullet points are preferred. +- Version-control MCP is unavailable in this environment; source changes will remain uncommitted. + +## Options + +- Working branch: N/A (version-control MCP operation unavailable) +- Run tests before and after the upgrade: true + +## Upgrade Goals +- Java 25 +- Spring Boot 4.x (selected stable release 4.1.2) +- Spring Framework 7.x (managed by Spring Boot 4.1.2) +- Migrate Java EE APIs from `javax.*` to `jakarta.*` where applicable + +## Technology Stack + +| Technology/Dependency | Current | Min Compatible Version | Why Incompatible | +| --------------------- | ------- | ---------------------- | ---------------- | +| Java | 8 | 25 | User requested; Boot 4 requires Java 17+ | +| Spring Boot parent | 2.7.18 | 4.1.2 | User requested; Boot 4 aligns Jakarta EE and Spring Framework 7 | +| Spring Framework | 5.3.x (Boot-managed) | 7.x (Boot 4.1.2 managed) | Framework 7 is requested | +| `javax.persistence` / `javax.validation` | Java EE namespaces | `jakarta.persistence` / `jakarta.validation` | Boot 4/Hibernate 7 use Jakarta APIs | +| Spring Security config | legacy chained DSL | Security 7 lambda DSL | Legacy `authorizeRequests`/`antMatchers` APIs removed | +| Oracle JDBC `ojdbc8` | Boot-managed | `ojdbc11` | Java 11+ driver appropriate for Java 25 | +| Maven | not installed locally; no wrapper | 3.9.11 in Docker | Reproducible Java 25 build environment | +| Docker build/runtime base | Temurin 8 | Temurin 25 | Runtime/build image must match application target | +| Documentation | states Boot 2.7.18 / Java 8 | Boot 4.1.2 / Java 25 | Prevent stale usage guidance | + +## Derived Upgrades +- Spring Boot 4.1.2 manages Spring Framework 7.x, Security 7, Jakarta EE APIs, and Hibernate 7; use its dependency management. +- Java 25 requires updating Maven compiler release and the container build/runtime images. +- Replace Oracle's Java 8 JDBC driver with the Java 11+ `ojdbc11` driver for the Java 25 target. +- Security configuration must move to the supported Security 7 lambda DSL while preserving stateless Basic authentication and the same endpoint authorization. + +## Impact Analysis + +### Dependency Changes + +| File | Dependency | Current | Action | Target | Reason | +|------|------------|---------|--------|--------|--------| +| `pom.xml` | `spring-boot-starter-parent` | 2.7.18 | upgrade | 4.1.2 | User requested Boot 4.x; stable version verified in Maven Central metadata | +| `pom.xml` | `spring-boot-starter-web` | Boot 2-era starter | replace | `spring-boot-starter-webmvc` | Boot 4 modular web starter | +| `pom.xml` | `spring-boot-starter-webmvc-test` | absent | add | 4.1.2 managed | Boot 4 separates MVC test support; required for `MockMvc` integration tests | +| `pom.xml` | `java.version`, `maven.compiler.source`, `maven.compiler.target` | 1.8 / 8 / 8 | upgrade | 25 / release 25 | User target | +| `pom.xml` | `ojdbc8` | Boot-managed | replace | `ojdbc11` 23.26.3.0.0 | Java 25 runtime uses JDBC 11+ driver line; Boot 4.1.2 manages this coordinate | +| `pom.xml` | `commons-io` explicit 2.11.0 | 2.11.0 | upgrade | 2.22.0 | Update explicit old pin to the current stable release | + +### Source Code Changes + +| File | Location | Current | Required Change | Reason | +|------|----------|---------|----------------|--------| +| `src/main/java/com/photoalbum/model/Photo.java` | imports | `javax.persistence.*`, `javax.validation.constraints.*` | Replace with `jakarta.persistence.*`, `jakarta.validation.constraints.*` | Jakarta migration | +| `src/main/java/com/photoalbum/service/impl/PhotoServiceImpl.java` | imports | `javax.imageio.*` | No change | `javax.imageio` is Java SE, not migrated Java EE | +| `src/main/java/com/photoalbum/config/SecurityConfig.java` | filter chain | chained `.csrf().disable()`, `authorizeRequests`, `antMatchers`, `.and()` | Use lambda DSL, `authorizeHttpRequests` and `requestMatchers` | Security 7 API compatibility; preserve endpoint rules and stateless Basic auth | +| `src/test/java/com/photoalbum/PhotoAlbumApplicationTests.java` | security behavior tests | context load only | Add MockMvc checks for authenticated mutation requests and public unmatched GET | Regression coverage for preserved access policy | + +### Configuration Changes + +No application property renames found in checked config files. JPA and servlet property namespaces are already Spring Boot names and remain applicable; validate at runtime via context test. + +### CI/CD Changes + +| File | Location | Current | Required Change | +|------|----------|---------|----------------| +| `Dockerfile` | build and runtime stages | Maven/Temurin 8 images | Use Maven 3.9.11 with Temurin 25 and Temurin 25 JRE | +| `README.md` | technology stack and application version references | Boot 2.7.18, Java 8 | Update to Boot 4.1.2 and Java 25 | + +### Risks & Warnings +- **Boot 4 modular starter/dependency changes**: Starter artifacts and managed transitive versions may differ from Boot 2.7. **Mitigation**: compile/test in the Java 25 Maven container; use Boot-managed versions and make only compatibility-driven edits. +- **Security behavior**: DSL rewrite could alter authorization. **Mitigation**: preserve exact POST matchers, public read access, stateless sessions, HTTP Basic and CSRF setting; regression tests exercise the authorization behavior. +- **Oracle 11+ JDBC driver availability**: The Boot 4.1.2 BOM manages `ojdbc11`; resolved runtime version was verified as 23.26.3.0.0. +- Baseline verification ran in the Maven/Temurin 8 Docker image because the host has no local JDK 8 or Maven executable. +- CVE validation MCP tool is unavailable; inspect resolved dependencies and report that limitation rather than claiming a security scan. + +## Upgrade Steps + +- Step 1: Setup Environment + - **Rationale**: Establish Docker-based Maven/JDK 25 build environment; local Java and Maven are absent. + - **Changes to Make**: Pull/validate the Java 25 Maven image; no project files change. + - **Verification**: `docker run --rm maven:3.9.11-eclipse-temurin-25 mvn -version`; expect Maven 3.9.11 and Java 25. +- Step 2: Setup Baseline + - **Rationale**: Record pre-upgrade compilation/test status before edits. + - **Changes to Make**: None. + - **Verification**: In Docker, `mvn clean test-compile` then `mvn clean test` using Maven/Temurin 3.9.6/8; unavailable image/build means baseline skipped. +- Step 3: Upgrade Runtime, Dependencies, APIs, and Documentation + - **Rationale**: Apply the coordinated Boot 4, Java 25, and Jakarta changes so the source remains compilable as a single step. + - **Changes to Make**: Apply all Dependency, Source Code, Configuration, and CI/CD changes above. + - **Verification**: `mvn clean test-compile -q` in Java 25 Docker container; expect all main and test code compiles. +- Step 4: Dependency and CVE Review + - **Rationale**: Inspect final resolved direct/transitive dependency set after BOM migration. + - **Changes to Make**: Fix any incompatible or vulnerable dependency versions identified; no dedicated CVE MCP scanner is exposed here. + - **Verification**: `mvn dependency:tree` and `mvn clean test-compile -q`; report limitations of CVE scan. +- Step 5: Final Validation + - **Rationale**: Demonstrate Java 25 compilation and full test pass. + - **Changes to Make**: Resolve all compile/test failures. + - **Verification**: `mvn clean test` (and packaging) in Java 25 container; all tests pass. \ No newline at end of file diff --git a/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/progress.md b/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/progress.md new file mode 100644 index 000000000..86803a046 --- /dev/null +++ b/.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/progress.md @@ -0,0 +1,107 @@ +# Upgrade Progress: PhotoAlbum-Java (001-upgrade-java-spring-boot) + +- **Started**: 2026-11-04 11:16 +01:00 +- **Plan Location**: `.github/modernize/upgrade-to-lts-20261104100839/001-upgrade-java-spring-boot/plan.md` +- **Total Steps**: 5 + +## Step Details + +- **Step 1: Setup Environment** + - **Status**: ✅ Completed + - **Changes Made**: + - Validated the Maven 3.9.11 and Eclipse Temurin Java 25 container. + - **Review Code Changes**: + - Sufficiency: ✅ Environment available for Java 25 build and tests. + - Necessity: ✅ No unnecessary project changes. + - Functional Behavior: ✅ Preserved. + - Security Controls: ✅ Preserved. + - **Verification**: + - Command: `docker run --rm maven:3.9.11-eclipse-temurin-25 mvn -version` + - JDK: Docker image `maven:3.9.11-eclipse-temurin-25` (Java 25.0.1) + - Build tool: Docker CLI 29.8.1; Maven 3.9.11 in image + - Result: SUCCESS — Java 25 and Maven 3.9.11 detected. + - Notes: Host Java and Maven are not installed; Docker provided the build toolchain. + - **Deferred Work**: None + - **Commit**: N/A - version-control MCP unavailable + +- **Step 2: Setup Baseline** + - **Status**: ✅ Completed + - **Changes Made**: + - Recorded pre-upgrade compile and test results from the Java 8 Maven container. + - **Review Code Changes**: + - Sufficiency: ✅ Baseline compilation and tests were executed before migration edits. + - Necessity: ✅ No project changes. + - Functional Behavior: ✅ Baseline behavior retained as comparison. + - Security Controls: ✅ Baseline security configuration unchanged during run. + - **Verification**: + - Command: `mvn clean test-compile -q` then `mvn clean test -q` + - JDK: Docker image `maven:3.9.6-eclipse-temurin-8` + - Build tool: Maven 3.9.6 in Docker image + - Result: SUCCESS — compile and test commands exited successfully; baseline test suite contained 1 test (1 passed). + - Notes: Existing H2 schema DDL warnings were logged, but the context-load test passed. + - **Deferred Work**: None + - **Commit**: N/A - version-control MCP unavailable + +- **Step 3: Upgrade Runtime, Dependencies, APIs, and Documentation** + - **Status**: ✅ Completed + - **Changes Made**: + - Upgraded Spring Boot to 4.1.2 and Java compilation release to 25; Spring Framework 7.0.10 is BOM-managed. + - Replaced the legacy web starter with Boot 4 `spring-boot-starter-webmvc` and added its MVC test starter. + - Migrated JPA and validation imports to Jakarta; updated the Security 7 lambda DSL. + - Switched Oracle JDBC to managed `ojdbc11` and Commons IO to 2.22.0. + - Updated Docker build/runtime images and README; added three security regression tests. + - **Review Code Changes**: + - Sufficiency: ✅ All planned dependency, namespace, security, runtime, and documentation updates applied. + - Necessity: ✅ Compatibility changes only; old Java EE namespaces and incompatible legacy Security DSL removed. + - Functional Behavior: ✅ Same POST mutation authentication, stateless sessions, HTTP Basic, public read access, and CSRF policy verified by tests. + - Security Controls: ✅ Existing authentication and endpoint restriction controls preserved and regression-tested. + - **Verification**: + - Command: `mvn clean test-compile -q`; subsequent `mvn clean test -q` + - JDK: Docker image `maven:3.9.11-eclipse-temurin-25` (Java 25.0.1) + - Build tool: Maven 3.9.11 in Docker image + - Result: SUCCESS — production and test code compile; 4 tests passed, 0 failures. + - Notes: Boot 4 test APIs required `spring-boot-starter-webmvc-test` and the relocated `AutoConfigureMockMvc` package; added both and reran successfully. + - **Deferred Work**: None + - **Commit**: N/A - version-control MCP unavailable + +- **Step 4: Dependency and CVE Review** + - **Status**: ✅ Completed + - **Changes Made**: + - Verified resolved Boot, Hibernate, Spring Security, Oracle JDBC, and Commons IO runtime versions. + - **Review Code Changes**: + - Sufficiency: ✅ Checked the resolved runtime dependency tree for expected managed versions. + - Necessity: ✅ No extra dependency overrides added beyond required Commons IO security maintenance. + - Functional Behavior: ✅ Dependency updates passed application tests. + - Security Controls: ✅ Security dependencies remain Boot-managed. + - **Verification**: + - Command: `mvn dependency:tree -Dscope=runtime` with targeted dependency includes + - JDK: Docker image `maven:3.9.11-eclipse-temurin-25` (Java 25.0.1) + - Build tool: Maven 3.9.11 in Docker image + - Result: SUCCESS — Boot 4.1.2, Hibernate 7.4.13.Final, Spring Security 7.1.2, ojdbc11 23.26.3.0.0, Commons IO 2.22.0 resolved. + - Notes: Dedicated CVE-validation MCP scanner was not available; dependency resolution was inspected, but this is not a full CVE scan. + - **Deferred Work**: Dedicated vulnerability scan not performed because the scanner tool is unavailable in this environment. + - **Commit**: N/A - version-control MCP unavailable + +- **Step 5: Final Validation** + - **Status**: ✅ Completed + - **Changes Made**: + - Resolved the Boot 4 MVC test module/package changes and reran the full validation. + - **Review Code Changes**: + - Sufficiency: ✅ Target versions and Jakarta migration confirmed; stale Java 8 and Boot 2.7 references absent from project docs/config. + - Necessity: ✅ No unrelated behavior changes. + - Functional Behavior: ✅ `mvn clean verify` passed; security regression tests passed. + - Security Controls: ✅ Mutation endpoints remain authenticated; read-only unmatched route is public. + - **Verification**: + - Command: `mvn clean verify -q`; `docker build --quiet -t photo-album:java25 .` + - JDK: Docker build image `maven:3.9.11-eclipse-temurin-25` (Java 25.0.1) + - Build tool: Maven 3.9.11 in Docker image; Docker CLI 29.8.1 + - Result: SUCCESS — clean verification passed; test suite 4/4; production image built successfully. + - Notes: Docker image ID `sha256:01fe6279d96593a7989bf601c5351b74b8c07736314f7170a3a3ff902f0fcaf6`. + - **Deferred Work**: Dedicated CVE scan unavailable; see Step 4 note. + - **Commit**: N/A - version-control MCP unavailable + +--- + +## Notes + +No version-control or telemetry MCP tools were exposed, so the upgrade remains uncommitted. Baseline on Java 8 passed 1/1 test; upgraded Java 25 validation passed 4/4 tests. The host emitted Maven Guice `sun.misc.Unsafe` deprecation notices; these originate in Maven tooling and did not affect compilation, tests, or packaging. \ No newline at end of file diff --git a/.github/modernize/upgrade-to-lts-20261104100839/plan.md b/.github/modernize/upgrade-to-lts-20261104100839/plan.md new file mode 100644 index 000000000..a3a3d1ff8 --- /dev/null +++ b/.github/modernize/upgrade-to-lts-20261104100839/plan.md @@ -0,0 +1,7 @@ +# Upgrade Plan + +## Overview +Upgrade to the latest LTS versions: Java 25 and Spring Boot 4.x, including Spring Framework 7.x and the required Jakarta EE API migration. + +## Tasks +See `.metadata/tasks.json` for detailed task breakdown. diff --git a/Dockerfile b/Dockerfile index 8cfce5858..be440098f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -# Use Maven with OpenJDK 8 for building -FROM maven:3.9.6-eclipse-temurin-8 AS build +# Build with Maven and Java 25 +FROM maven:3.9.11-eclipse-temurin-25 AS build WORKDIR /app @@ -13,8 +13,8 @@ COPY src ./src # Build the application RUN mvn clean package -DskipTests -# Use OpenJDK 8 runtime for the final image -FROM eclipse-temurin:8-jre +# Use Java 25 runtime for the final image +FROM eclipse-temurin:25-jre WORKDIR /app diff --git a/README.md b/README.md index 82dcaf1ee..d083a64fc 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A photo gallery application built with Spring Boot and Oracle Database, featurin ## Technology Stack -- **Framework**: Spring Boot 2.7.18 (Java 8) +- **Framework**: Spring Boot 4.1.2 (Java 25) - **Database**: Oracle Database 21c Express Edition - **Templating**: Thymeleaf - **Build Tool**: Maven @@ -73,8 +73,8 @@ A photo gallery application built with Spring Boot and Oracle Database, featurin ## Photo Album Java Application - **Port**: `8080` (mapped to host port 8080) -- **Framework**: Spring Boot 2.7.18 -- **Java Version**: 8 +- **Framework**: Spring Boot 4.1.2 +- **Java Version**: 25 - **Database**: Connects to Oracle container - **Photo Storage**: All photos stored as BLOBs in database (no file system storage) - **UUID System**: Each photo gets a globally unique identifier for cache-busting diff --git a/pom.xml b/pom.xml index 398340a31..63268fb27 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.18 + 4.1.2 @@ -21,9 +21,8 @@ A simple photo storage and gallery application built with Spring Boot and Oracle DB - 1.8 - 8 - 8 + 25 + 25 UTF-8 @@ -31,7 +30,7 @@ org.springframework.boot - spring-boot-starter-web + spring-boot-starter-webmvc @@ -49,7 +48,7 @@ com.oracle.database.jdbc - ojdbc8 + ojdbc11 runtime @@ -69,7 +68,7 @@ commons-io commons-io - 2.11.0 + 2.22.0 @@ -85,6 +84,13 @@ test + + + org.springframework.boot + spring-boot-starter-webmvc-test + test + + com.h2database diff --git a/src/main/java/com/photoalbum/config/SecurityConfig.java b/src/main/java/com/photoalbum/config/SecurityConfig.java index c9a6fb271..5f8502250 100644 --- a/src/main/java/com/photoalbum/config/SecurityConfig.java +++ b/src/main/java/com/photoalbum/config/SecurityConfig.java @@ -12,6 +12,7 @@ import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.config.Customizer; /** * Security configuration. @@ -48,16 +49,15 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti // Stateless HTTP Basic auth: credentials are sent per request, so a // session-based CSRF token is not applicable. CSRF is disabled to // keep this security fix minimal without breaking the JSON upload API. - .csrf().disable() - .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .authorizeRequests() + .csrf(csrf -> csrf.disable()) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .authorizeHttpRequests(authorize -> authorize // Default deny for state-changing operations (CWE-862 / CWE-306). - .antMatchers(HttpMethod.POST, "/upload", "/detail/*/delete").authenticated() + .requestMatchers(HttpMethod.POST, "/upload", "/detail/*/delete").authenticated() // Public, read-only photo gallery. .anyRequest().permitAll() - .and() - .httpBasic(); + ) + .httpBasic(Customizer.withDefaults()); return http.build(); } } diff --git a/src/main/java/com/photoalbum/model/Photo.java b/src/main/java/com/photoalbum/model/Photo.java index d1cea3ec5..1852ad7aa 100644 --- a/src/main/java/com/photoalbum/model/Photo.java +++ b/src/main/java/com/photoalbum/model/Photo.java @@ -1,10 +1,10 @@ package com.photoalbum.model; -import javax.persistence.*; -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Positive; -import javax.validation.constraints.Size; +import jakarta.persistence.*; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; import java.time.LocalDateTime; import java.util.UUID; diff --git a/src/test/java/com/photoalbum/PhotoAlbumApplicationTests.java b/src/test/java/com/photoalbum/PhotoAlbumApplicationTests.java index 6f710babd..378a7fcd4 100644 --- a/src/test/java/com/photoalbum/PhotoAlbumApplicationTests.java +++ b/src/test/java/com/photoalbum/PhotoAlbumApplicationTests.java @@ -1,15 +1,44 @@ package com.photoalbum; import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.ActiveProfiles; +import org.springframework.boot.webmvc.test.autoconfigure.AutoConfigureMockMvc; +import org.springframework.test.web.servlet.MockMvc; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @SpringBootTest +@AutoConfigureMockMvc @ActiveProfiles("test") class PhotoAlbumApplicationTests { + @Autowired + private MockMvc mockMvc; + @Test void contextLoads() { // This test ensures that the Spring context loads correctly } + + @Test + void uploadRequiresAuthentication() throws Exception { + mockMvc.perform(post("/upload")) + .andExpect(status().isUnauthorized()); + } + + @Test + void photoDeletionRequiresAuthentication() throws Exception { + mockMvc.perform(post("/detail/test-id/delete")) + .andExpect(status().isUnauthorized()); + } + + @Test + void unconfiguredReadOnlyRouteRemainsPublic() throws Exception { + mockMvc.perform(get("/security-public-access-check")) + .andExpect(status().isNotFound()); + } } \ No newline at end of file From 6571e9425f79f126b30b004f5d175033b6297bbc Mon Sep 17 00:00:00 2001 From: Thomas Juul Date: Wed, 4 Nov 2026 11:28:22 +0100 Subject: [PATCH 2/2] upgrade java --- .github/modernize/.gitignore | 1 + .../engines/appcat/result/report.json | 1173 +++++++++++++ .../engines/facts/api-service-contracts.md | 93 ++ .../engines/facts/architecture-diagram.md | 117 ++ .../engines/facts/business-workflows.md | 82 + .../engines/facts/configuration-inventory.md | 135 ++ .../engines/facts/data-architecture.md | 70 + .../engines/facts/dependency-map.md | 72 + .../aggregate-report.json | 1196 ++++++++++++++ .../application-topology-graph.md | 16 + .../reports-20261104093550/index.html | 887 ++++++++++ .../facts/api-service-contracts.md | 93 ++ .../facts/architecture-diagram.md | 117 ++ .../facts/assessment-overview.md | 42 + .../facts/business-workflows.md | 82 + .../facts/configuration-inventory.md | 135 ++ .../facts/data-architecture.md | 70 + .../PhotoAlbum-Java/facts/dependency-map.md | 72 + .../repos/PhotoAlbum-Java/report.html | 1451 +++++++++++++++++ .../repos/PhotoAlbum-Java/report.json | 1173 +++++++++++++ .../repos/PhotoAlbum-Java/report.md | 148 ++ .../facts/api-service-contracts.md | 93 ++ .../facts/architecture-diagram.md | 117 ++ .../facts/assessment-overview.md | 42 + .../facts/business-workflows.md | 82 + .../facts/configuration-inventory.md | 135 ++ .../facts/data-architecture.md | 70 + .../facts/dependency-map.md | 72 + .../facts/rearchitect-assessment.json | 3 + .../reports/report-20261104093052/report.json | 1173 +++++++++++++ .../reports/report-20261104093052/report.md | 148 ++ 31 files changed, 9160 insertions(+) create mode 100644 .github/modernize/.gitignore create mode 100644 .github/modernize/assessment/engines/appcat/result/report.json create mode 100644 .github/modernize/assessment/engines/facts/api-service-contracts.md create mode 100644 .github/modernize/assessment/engines/facts/architecture-diagram.md create mode 100644 .github/modernize/assessment/engines/facts/business-workflows.md create mode 100644 .github/modernize/assessment/engines/facts/configuration-inventory.md create mode 100644 .github/modernize/assessment/engines/facts/data-architecture.md create mode 100644 .github/modernize/assessment/engines/facts/dependency-map.md create mode 100644 .github/modernize/assessment/reports-20261104093550/aggregate-report.json create mode 100644 .github/modernize/assessment/reports-20261104093550/apps/PhotoAlbum-Java/application-topology-graph.md create mode 100644 .github/modernize/assessment/reports-20261104093550/index.html create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/api-service-contracts.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/architecture-diagram.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/assessment-overview.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/business-workflows.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/configuration-inventory.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/data-architecture.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/dependency-map.md create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.html create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.json create mode 100644 .github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/api-service-contracts.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/architecture-diagram.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/assessment-overview.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/business-workflows.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/configuration-inventory.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/data-architecture.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/dependency-map.md create mode 100644 .github/modernize/assessment/reports/report-20261104093052/facts/rearchitect-assessment.json create mode 100644 .github/modernize/assessment/reports/report-20261104093052/report.json create mode 100644 .github/modernize/assessment/reports/report-20261104093052/report.md diff --git a/.github/modernize/.gitignore b/.github/modernize/.gitignore new file mode 100644 index 000000000..94d0e7b9e --- /dev/null +++ b/.github/modernize/.gitignore @@ -0,0 +1 @@ +*!assessment/ diff --git a/.github/modernize/assessment/engines/appcat/result/report.json b/.github/modernize/assessment/engines/appcat/result/report.json new file mode 100644 index 000000000..0b5e96d95 --- /dev/null +++ b/.github/modernize/assessment/engines/appcat/result/report.json @@ -0,0 +1,1173 @@ +{ + "version": "1.0.0", + "producer": "Java AppCAT CLI", + "metadata": { + "analysisStartTime": "2026-11-04T09:30:52.775351Z", + "analysisEndTime": "2026-11-04T09:34:32.6470284Z", + "status": "Complete", + "privacyMode": "Protected", + "privacyModeHelpUrl": "https://aka.ms/appcat-privacy-mode", + "targetIds": [ + "azure-appservice", + "azure-aks", + "azure-container-apps" + ], + "targetDisplayNames": [ + "Azure App Service", + "Azure Kubernetes Service", + "Azure Container Apps" + ], + "capabilities": [ + "openjdk25" + ], + "os": [ + "linux", + "windows" + ], + "domains": [ + "java-upgrade", + "cloud-readiness" + ], + "mode": "full", + "minimumCveSeverity": null + }, + "summary": { + "totalProjects": 1, + "totalIssues": 10, + "totalIncidents": 31, + "totalEffort": 196, + "charts": { + "severity": { + "mandatory": 13, + "optional": 2, + "potential": 16, + "information": 0 + }, + "category": { + "auth-migration": 1, + "database-migration": 6, + "framework-upgrade": 12, + "jakarta-migration": 1, + "java-version-upgrade": 3, + "local-credential": 4, + "spring-migration": 4 + } + } + }, + "projects": [ + { + "path": ".", + "issues": 10, + "storyPoints": 196, + "properties": { + "appName": "photo-album", + "jdkVersion": "1.8", + "frameworks": [ + "Spring Boot", + "Spring" + ], + "languages": [ + "Java", + "JavaScript" + ], + "tools": [ + "Maven" + ] + }, + "incidents": [ + { + "ruleId": "auth-02000", + "incidentId": "b845d4f9-3276-4be7-a051-635b67d9259a", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1bdf6e21-f623-4f14-9339-ae1a0ba204d2", + "location": "pom.xml", + "locationKind": "File", + "line": 52, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "44b171b1-2ca1-4627-a673-8060fdce5f29", + "location": "docker-compose.yml", + "locationKind": "File", + "line": 32, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "40510a44-6c1f-49c5-9103-c5d2a17a7682", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 3, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "5f157c91-507e-4794-86f9-6799a711160c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 12, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "bc62976f-e22d-4434-9046-b0836c0e6b7c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 15, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1004341d-8548-4a06-afba-e56fdf194ba1", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 6, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "995a2b3e-4870-4e04-8d6d-a5701427008a", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "97097182-5728-42f1-b69e-5b16a66937ff", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "154a6aea-7e34-49c7-8d83-8897ac979f65", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "433cc960-b5ae-4a76-a45b-2c34d446e0a3", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "a95a682d-dad5-4275-8d6d-6d43ed4188cd", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b3814a33-4403-4442-a2f0-a119b0f6248a", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 23, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "1ac607ee-8338-440c-b110-79d433327062", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b9c9e02a-c269-4137-9d82-f8671fcb2130", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 14, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-01000", + "incidentId": "36436d2b-df81-40eb-a0fe-d3f8ede23005", + "location": "pom.xml", + "locationKind": "File", + "line": 24, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "927cfd73-3a17-4888-99f0-b00ccd7d84ce", + "location": "pom.xml", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "9b3a9911-d9d3-4190-a82b-6f28e3a0ad14", + "location": "pom.xml", + "locationKind": "File", + "line": 26, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "6c645761-fb02-4d39-a079-1389acb57081", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "8474445f-4d9f-4410-ba08-4eea0a5f407b", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "615ff34c-58b8-44ac-850a-0760d5125fe9", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "2c17f79e-3853-464c-8de3-42ffb30fb98e", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c61300ea-5005-4813-a64d-41cc57d2d16f", + "location": "pom.xml", + "locationKind": "File", + "line": 40, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "4264b153-88a3-4faf-a76f-a4c4b13bf6d1", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "3507d6d7-3397-428f-883f-aa62f6d638de", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "7cd37e0b-2862-40da-b295-7cfecdf85b33", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "69e09b4a-1b8a-4a8c-b2f6-61f53bf7a6cf", + "location": "pom.xml", + "locationKind": "File", + "line": 78, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "b1800a7a-6a4b-4b66-8e98-bbbff27bafa4", + "location": "pom.xml", + "locationKind": "File", + "line": 59, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "bcf5c6b6-36a4-45ae-ac0c-c941c264a286", + "location": "pom.xml", + "locationKind": "File", + "line": 98, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c5ff81c6-2bdf-417a-b7a5-6943bdb72930", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "jakarta-database-00002", + "incidentId": "73c943cd-017a-4dbe-965e-8128b10fdc4e", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=cloud-readiness" + ] + } + ] + } + ], + "rules": { + "auth-02000": { + "id": "auth-02000", + "description": "The application embeds a Spring Security library. Ensure that the application is configured to access the identity provider.\n\nConsider using Microsoft Entra ID as an identity provider.", + "title": "Embedded framework - Spring Security", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-app-service#identity-providers", + "title": "Spring Security-related dependencies found in the project" + }, + { + "url": "https://docs.microsoft.com/azure/active-directory/external-identities/azure-ad-account", + "title": "Azure Active Directory (Azure AD) identity provider for External Identities" + }, + { + "url": "https://docs.spring.io/spring-security/reference/index.html", + "title": "Spring Security" + }, + { + "url": "https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization", + "title": "Spring Boot API: Authorization" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=auth-migration", + "os=windows", + "os=linux" + ] + }, + "azure-database-microsoft-oracle-07000": { + "id": "azure-database-microsoft-oracle-07000", + "description": "Oracle database found. To migrate a Java application that uses an Oracle database to Azure, you can follow these recommendations:\n\n * **Migrate to Azure Database for PostgreSQL**: Azure recommends migrating Oracle databases to Azure Database for PostgreSQL Flexible Server as it provides better cost-effectiveness and performance. Create a managed PostgreSQL Flexible Server database in Azure and choose the appropriate pricing tier based on your application\u0027s requirements.\n\n * **Use migration tools**: Utilize the Azure Database Migration Service (DMS) or third-party tools to migrate your Oracle database schema and data to PostgreSQL. Consider using ora2pg or similar tools to convert Oracle-specific SQL to PostgreSQL-compatible SQL.\n\n * **Update database drivers and connection strings**: Replace Oracle JDBC drivers with PostgreSQL drivers in your Java application. Update connection strings from Oracle format (jdbc:oracle:thin:) to PostgreSQL format (jdbc:postgresql:).\n\n * **Review and convert Oracle-specific code**: Identify and convert Oracle-specific SQL functions, stored procedures, and PL/SQL code to PostgreSQL equivalents. Pay attention to data types, syntax differences, and built-in functions.\n\n * Enable **monitoring and diagnostics**: Utilize Azure Monitor to gain insights into the performance and health of your Java application and the underlying PostgreSQL database. Set up metrics, alerts, and log analytics to proactively identify and resolve issues.\n\n * Implement **security** measures: Apply security best practices to protect your Java application and the PostgreSQL database. This includes implementing authentication and authorization mechanisms with passwordless connections and leveraging Microsoft Defender for Cloud for threat detection and vulnerability assessments.\n\n * **Backup** your data: Azure Database for PostgreSQL provides automated backups by default. You can configure the retention period for backups based on your requirements. You can also enable geo-redundant backups, if needed, to enhance data durability and availability.", + "title": "Oracle database found", + "severity": "potential", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/postgresql", + "title": "Azure Database for PostgreSQL documentation" + }, + { + "url": "https://learn.microsoft.com/azure/postgresql/migrate/how-to-migrate-oracle-ora2pg", + "title": "Oracle to PostgreSQL migration guide" + }, + { + "url": "https://learn.microsoft.com/azure/dms", + "title": "Azure Database Migration Service documentation" + }, + { + "url": "https://learn.microsoft.com/azure/azure-monitor", + "title": "Azure Monitor documentation" + }, + { + "url": "https://learn.microsoft.com/azure/defender-for-cloud", + "title": "Microsoft Defender for Cloud" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=database-migration", + "database", + "oracle", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-01000": { + "id": "azure-java-version-01000", + "description": "The application is using a Java version that has reached the end of support. It is strongly recommended to plan and execute a migration strategy to upgrade your application to a supported Java version.\nSupported Java versions receive long-term support (LTS) from the Java community, including bug fixes and updates. Migrating to a supported version provides you with a stable and well-maintained platform for your application.", + "title": "Java Version Has Reached the End of Support", + "severity": "mandatory", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-02000": { + "id": "azure-java-version-02000", + "description": "The application is not using the latest LTS Java version. It is recommended to consider upgrading to the latest LTS version to take advantage of the newest language features, performance improvements, and extended support timelines.\nUpgrading to the latest LTS version ensures your application benefits from the most recent security enhancements and a longer support lifecycle.", + "title": "Java Version is not the latest LTS", + "severity": "optional", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-password-01000": { + "id": "azure-password-01000", + "description": "Using clear passwords in property files is a security risk, as they can be easily compromised if the files are accessed by unauthorized individuals. To enhance the security of your application, it is recommended to employ secure credential management practices.\n\n * **Azure Key Vault**: Utilize Azure Key Vault to securely store and manage your application\u0027s passwords and other sensitive credentials. Azure Key Vault provides a centralized and highly secure location for storing secrets, keys, and certificates.\n\n * **Passwordless connections**: You can provide an additional layer of security and convenience for accessing resources in Azure by eliminating the need for passwords. This way you can reduce the risk of password-related vulnerabilities, such as weak passwords or password theft.", + "title": "Password found in configuration file", + "severity": "potential", + "effort": 3, + "links": [ + { + "url": "https://learn.microsoft.com/azure/key-vault", + "title": "Azure Key Vault documentation" + }, + { + "url": "https://learn.microsoft.com/azure/developer/intro/passwordless-overview", + "title": "Passwordless connections for Azure services" + }, + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#inventory-configuration-sources-and-secrets", + "title": "Password found in configuration file" + }, + { + "url": "https://docs.microsoft.com/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-key-vault", + "title": "Read a secret from Azure Key Vault in a Spring Boot application" + }, + { + "url": "https://search.maven.org/artifact/com.azure.spring/azure-spring-boot-starter-keyvault-secrets", + "title": "Azure Spring Boot Starter for Azure Key Vault Secrets" + } + ], + "labels": [ + "source", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=local-credential", + "password", + "security", + "os=windows", + "os=linux" + ] + }, + "jakarta-database-00002": { + "id": "jakarta-database-00002", + "description": "The application depends on **Jakarta Persistence (JPA)** APIs (\u0060jakarta.persistence.*\u0060 or legacy \u0060javax.persistence.*\u0060), which are used for object-relational mapping (ORM) and database interaction in Jakarta EE or Java EE applications.\n\nWhen migrating to Azure:\n- Ensure that the database connection, JPA provider (e.g., Hibernate, EclipseLink), and dialect are compatible with your target Azure database service.\n- Recommended database services include **Azure Database for PostgreSQL**, **Azure Database for MySQL**, or **Azure SQL Database**.\n- For containerized deployments, these JPA-based applications can run on **Azure Kubernetes Service (AKS)** or **Azure App Service for Linux/Windows**.\n- If using **Spring Data JPA**, verify that connection pool settings and environment variables are properly configured for the cloud environment.\n- Consider leveraging **Azure Key Vault** for secure storage of database credentials and connection strings.", + "title": "Detects usage of Jakarta Persistence (JPA) APIs", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://jakarta.ee/specifications/persistence/", + "title": "Jakarta Persistence Specification" + }, + { + "url": "https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/data-stores-getting-started#common-database-scenarios", + "title": "Prepare to choose a data store in Azure" + } + ], + "labels": [ + "source=java", + "source=java-ee", + "target=azure-aks", + "target=azure-container-apps", + "target=azure-appservice", + "domain=cloud-readiness", + "category=jakarta-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-port-01000": { + "id": "spring-boot-to-azure-port-01000", + "description": "The application is setting the server port. To migrate a Java application that sets the server port to Azure Container Apps:\n\n * **Azure Container Apps allows you to expose port according to your Azure Container Apps resource configuration. For instance, a Spring Boot application listens to port of 8080 by default, but it can be set with server.port or environment variable SERVER_PORT as you need.", + "title": "Server port configuration found", + "severity": "potential", + "effort": 1, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#identify-any-clients-relying-on-a-non-standard-port", + "title": "Identify any clients relying on a non-standard port" + } + ], + "labels": [ + "source=springboot", + "target=azure-aks", + "target=azure-appservice", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=spring-migration", + "port", + "server port", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-restricted-config-01000": { + "id": "spring-boot-to-azure-restricted-config-01000", + "description": "The application uses restricted configurations for Azure Container Apps.\n These properties can be automatically injected into your application environment by Azure Container Apps to access managed Config Server and managed Eureka Server.\n Please remove them from your application, including configuration files, config server files, command line parameters, Java system attributes, and environment variables.\n\n If configured in **configuration files**: they will be ignored and overrided by Azure Container Apps.\n \n If configured in **Config Server files**, **command line parameters**, **Java system attribute**, **environment variable**: they need to be removed or you might experience conflicts and unexpected behavior.", + "title": "Restricted configurations found", + "severity": "potential", + "effort": 2, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-cloud-to-azure-container-apps#remove-restricted-configurations", + "title": "Migrate Spring Boot applications to Azure Container Apps - Remove restricted configurations" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-config-server?tabs=azure-cli", + "title": "Connect to a managed Config Server for Spring in Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-eureka-server?tabs=azure-cli", + "title": "Connect to a managed Eureka Server for Spring in Azure Container Apps" + } + ], + "labels": [ + "target=azure-container-apps", + "source=springboot", + "domain=cloud-readiness", + "category=spring-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-spring-boot-version-01000": { + "id": "spring-boot-to-azure-spring-boot-version-01000", + "description": "The application is using a Spring Boot version that has reached its End of OSS Support.\nWith the officially supported new versions from Spring, you can get the best experience. Here are some steps you can take to update your application to the latest version of Spring Boot:\n\n* Choose a **supported Spring Boot version**: Check out Spring Boot Support Versions and determine the most suitable supported Spring Boot version.\n\n* **Update Spring Boot version**: Update the Spring Boot version of your application. There are automated tools like Rewrite to help you with the migration.\n\n* **Address code compatibility**: Review your application\u0027s codebase for any potential compatibility issues with the target Spring Boot version. Update deprecated APIs or features, address any language or library changes, and ensure that your code follows best practices and standards.\n\n* **Test thoroughly**: Execute a comprehensive testing process to verify the compatibility and functionality of your application with the new Spring Boot version. Perform unit tests, integration tests, and system tests to validate that all components and dependencies work as expected.", + "title": "Spring Boot Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps", + "title": "Migrate Spring Boot applications to Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-microservice-get-started?tabs=azure-cli", + "title": "Launch your first Java microservice application with managed Java components in Azure Container Apps" + }, + { + "url": "https://spring.io/projects/spring-boot/#support", + "title": "Spring Boot Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-boot/wiki/Supported-Versions", + "title": "Spring Boot Support Policy" + } + ], + "labels": [ + "source=springboot", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "spring-framework-version-01000": { + "id": "spring-framework-version-01000", + "description": "Your application is using a Spring Framework version that has reached its End of OSS Support.\nUpgrading to a supported version ensures better performance, security, and compatibility with modern tools.\n 1. Pick a Supported Version: Review the Spring Framework support policy and choose an actively supported version.\n 2. Update Your Project: Change the Spring Framework version in your pom.xml or build.gradle.\n 3. Fix Compatibility Issues: Update deprecated code, replace removed features, and ensure dependencies are compatible with the new Spring Framework version.\n 4. Thoroughly Test: Run unit, integration, and end-to-end tests to make sure everything still works after the upgrade.", + "title": "Spring Framework Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://spring.io/projects/spring-framework#support", + "title": "Spring Framework Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions", + "title": "Spring Framework Support Policy" + } + ], + "labels": [ + "source=spring", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + } + }, + "rearchitect": [] +} \ No newline at end of file diff --git a/.github/modernize/assessment/engines/facts/api-service-contracts.md b/.github/modernize/assessment/engines/facts/api-service-contracts.md new file mode 100644 index 000000000..786b3443c --- /dev/null +++ b/.github/modernize/assessment/engines/facts/api-service-contracts.md @@ -0,0 +1,93 @@ +# API & Service Communication Contracts + +The application exposes five HTTP routes: server-rendered gallery/detail pages, a multipart upload endpoint with a JSON response, and an endpoint that serves photo bytes. Requests are handled synchronously within one application and synchronously persisted to the database; no inter-service API or asynchronous messaging was found. + +## Service Catalog + +| Service | Port | Category | Purpose | +|---|---:|---|---| +| Photo Album Java (`photo-album`) | 8080 | API Layer | Source-built Spring MVC and Thymeleaf web application providing the photo gallery, upload, detail, delete, and image-serving routes. | +| Oracle Database (`oracle-db`) | 1521 | Infrastructure | Third-party Oracle Free container storing photo metadata and image BLOBs; reached by the application over the Compose network. | + +The Maven build contains one application module. Docker Compose defines the application and database containers; the database is an image-based dependency, not a source-built service. + +## API Endpoints Inventory + +| Service | Method | Path | Request Type | Response Type | +|---|---|---|---|---| +| Photo Album Java — `HomeController` | GET | `/` | No body; optional MVC `Model` populated by the controller | HTTP 200, rendered `index` HTML view with photo data; on retrieval error, renders the view with an empty list | +| Photo Album Java — `HomeController` | POST | `/upload` | Multipart form field `files` containing one or more uploaded files | JSON map containing `success`, `uploadedPhotos`, and `failedUploads`; HTTP 400 for no files, HTTP 200 for processed uploads, including partial failures; HTTP 401 when unauthenticated | +| Photo Album Java — `DetailController` | GET | `/detail/{id}` | Path parameter `id` | HTTP 200, rendered `detail` HTML view; redirects to `/` when the photo is absent or an error occurs | +| Photo Album Java — `DetailController` | POST | `/detail/{id}/delete` | Path parameter `id`; no body | Redirect to `/` with a flash success/error message; HTTP 401 when unauthenticated | +| Photo Album Java — `PhotoFileController` | GET | `/photo/{id}` | Path parameter `id` | HTTP 200 with image bytes and the stored media type; HTTP 404 if unavailable; HTTP 500 on an unexpected error | + +No explicit API versioning scheme was found. The routes are implemented with Spring MVC controllers; `/upload` is the only route returning a JSON response rather than an HTML view or binary content. + +## Management & Observability Endpoints + +| Service | Endpoint | Custom Metrics (if any) | +|---|---|---| +| Photo Album Java | No Actuator, health, metrics, or management endpoint was found. | None found | +| Oracle Database | Container health check is configured in Docker Compose; no HTTP management endpoint is exposed by this application. | None found | + +## DTOs & Contracts + +- `Photo` is the mutable JPA entity and is also added to MVC models for the gallery and detail views. Its persistence structure is documented separately in `data-architecture.md`. +- `UploadResult` is a mutable service-layer result object used to communicate per-file upload success or failure to the controller; it is not directly serialized as the HTTP response. +- The upload response is assembled from Java `Map` instances rather than a dedicated response DTO. Each successful item contains photo metadata, and each failed item contains a filename and error message. +- No dedicated request DTO is defined; upload input is Spring's `MultipartFile` binding. The `id` values are path parameters. +- No immutable DTOs were found. JSON responses use Spring Boot/Jackson serialization, while gallery and detail responses use Thymeleaf templates. +- No OpenAPI/Swagger specification, protobuf schema, or GraphQL schema was found. + +## Communication Patterns + +- **Synchronous:** The browser calls the Spring MVC application over HTTP. Controllers call `PhotoService` directly; `PhotoServiceImpl` calls `PhotoRepository` via Spring Data JPA/JDBC to Oracle. Photo metadata and binary content are stored in the database. +- **Asynchronous:** No message broker, event-driven communication, or asynchronous service calls were found. +- **Resilience:** No retry, circuit-breaker, timeout, or bulkhead policy was found. Errors are handled locally in some controller/service paths (including upload result errors, redirects, and HTTP 404/500 responses); no downstream-service fallback policy exists. +- **Discovery and gateway:** There is no service registry, client-side load balancing, or API gateway. Compose's internal `oracle-db` hostname is used for database connectivity. +- **Startup dependency:** Compose starts the application after the Oracle container reports healthy. This affects whether the application can start and access the database; operational probe and wait details are maintained in `configuration-inventory.md`. +- **Security:** Spring Security configures stateless HTTP Basic authentication for `POST /upload` and `POST /detail/*/delete`; other routes are permitted anonymously. There is no application-level role check beyond requiring authentication on those state-changing routes. CSRF is disabled. No TLS/HTTPS configuration was found, so transport encryption is not configured in the application. + +## Service Technology Matrix + +| Service | Web | Data Access | Discovery | Gateway | Actuator | Cache | Metrics | +|---|---|---|---|---|---|---|---| +| Photo Album Java | Spring MVC, Thymeleaf | Spring Data JPA | None | None | None found | None found | None found | +| Oracle Database | None | Oracle Database | None | None | Compose health check only | N/A | None found | + +## Service Communication Sequence + +The upload path illustrates the main state-changing request flow. Authentication is enforced before the controller receives the request; successful uploads are saved through the local service/repository chain. + +```mermaid +sequenceDiagram + participant Client as "Browser" + participant Security as "Spring Security" + participant Controller as "HomeController" + participant Service as "PhotoService" + participant Repository as "PhotoRepository" + participant DB as "Oracle Database" + + Client->>Security: POST /upload with multipart files and HTTP Basic + alt Authenticated + Security->>Controller: Forward upload request + loop Each uploaded file + Controller->>Service: uploadPhoto(file) + Service->>Service: Check file type and size; read image metadata + Service->>Repository: Save Photo and image bytes + Repository->>DB: Synchronous database write + DB-->>Repository: Persisted Photo + Repository-->>Service: Photo with ID + Service-->>Controller: UploadResult + Controller->>Service: getPhotoById(photoId) + Service->>Repository: findById(photoId) + Repository->>DB: Synchronous database read + DB-->>Repository: Photo + Repository-->>Service: Optional Photo + Service-->>Controller: Optional Photo + end + Controller-->>Client: 200 JSON with uploadedPhotos and failedUploads + else Unauthenticated + Security-->>Client: 401 Unauthorized + end +``` diff --git a/.github/modernize/assessment/engines/facts/architecture-diagram.md b/.github/modernize/assessment/engines/facts/architecture-diagram.md new file mode 100644 index 000000000..4d9a02f71 --- /dev/null +++ b/.github/modernize/assessment/engines/facts/architecture-diagram.md @@ -0,0 +1,117 @@ +# Architecture Diagram + +Photo Album is a server-rendered Spring Boot application for browsing, uploading, serving, and deleting photos. It persists photo data and metadata in Oracle Database and can be run with the database using Docker Compose. + +## Application Architecture + +```mermaid +flowchart TD + subgraph ClientA["Client Layer"] + BrowserA["Web Browser"] + end + subgraph AppA["Application Layer - Spring Boot 2.7.18"] + WebA["Spring MVC Controllers"] + ViewA["Thymeleaf Views"] + SecurityA["Spring Security HTTP Basic"] + ServiceA["Photo Service"] + end + subgraph DataA["Data Layer"] + JpaA["Spring Data JPA and Hibernate"] + OracleA[("Oracle Database Free 23ai")] + end + subgraph RuntimeA["Container Runtime"] + ComposeA["Docker Compose"] + end + + BrowserA -->|"HTTP gallery and photo requests"| WebA + WebA -->|"renders pages"| ViewA + ViewA -->|"HTML responses"| BrowserA + BrowserA -->|"upload and delete requests"| SecurityA + SecurityA -->|"authorizes state changes"| WebA + WebA -->|"photo operations"| ServiceA + ServiceA -->|"CRUD and BLOB access"| JpaA + JpaA -->|"Oracle SQL"| OracleA + ComposeA -->|"runs application and database containers"| AppA + ComposeA -->|"runs database container"| OracleA +``` + +### Technology Stack Summary + +| Layer | Technology | Version | Purpose | +|---|---|---|---| +| Application runtime | Java | 8 | Runs the server-side application | +| Application framework | Spring Boot | 2.7.18 | Application configuration and runtime | +| Presentation | Spring MVC and Thymeleaf | Spring Boot managed | Handles HTTP requests and renders server-side HTML | +| Security | Spring Security | Spring Boot managed | HTTP Basic authentication for photo upload and deletion | +| Data access | Spring Data JPA and Hibernate | Spring Boot managed | Persists photo entities and performs database queries | +| Database driver | Oracle JDBC ojdbc8 | Maven-managed | Connects the application to Oracle Database | +| Database | Oracle Database Free | 23ai container image | Stores photo metadata and image BLOBs | +| Container orchestration | Docker Compose | Not specified | Runs the application and Oracle database containers | + +### Data Storage & External Services + +Oracle Database is the application's only identified data store. Spring Data JPA and Hibernate access Oracle through the Oracle JDBC driver; photo metadata and image bytes are stored together, with image bytes held as BLOB data. Docker Compose provisions the database alongside the application. No cache, message broker, or external API integration was identified. + +### Key Architectural Decisions + +- Uses Spring MVC with Thymeleaf for server-rendered gallery and detail pages, with a JSON response for uploads. +- Separates photo operations behind a service interface and Spring Data JPA repository. +- Stores uploaded images in Oracle rather than on the application filesystem; Spring Security requires HTTP Basic authentication for uploads and deletion. + +## Component Relationships + +```mermaid +flowchart LR + subgraph PresentationC["Presentation"] + HomeC["HomeController"] + DetailC["DetailController"] + FileC["PhotoFileController"] + ViewsC["Thymeleaf Views"] + end + subgraph BusinessC["Business Logic"] + ServiceApiC["PhotoService"] + ServiceImplC["PhotoServiceImpl"] + end + subgraph DataC["Data Access"] + RepoC["PhotoRepository"] + EntityC["Photo Entity"] + DatabaseC[("Oracle Database")] + end + subgraph InfraC["Infrastructure"] + SecurityConfigC["SecurityConfig"] + SecurityFilterC["Spring Security Filter Chain"] + end + subgraph ModelsC["Models"] + UploadC["UploadResult"] + end + + HomeC -->|"gallery and upload operations"| ServiceApiC + DetailC -->|"detail, navigation, and delete operations"| ServiceApiC + FileC -->|"retrieves image data"| ServiceApiC + ServiceApiC -->|"implemented by"| ServiceImplC + ServiceImplC -->|"queries and persists"| RepoC + RepoC -->|"maps"| EntityC + RepoC -->|"executes database operations"| DatabaseC + HomeC -->|"returns upload status"| UploadC + HomeC -->|"selects gallery template"| ViewsC + DetailC -->|"selects detail template"| ViewsC + SecurityConfigC -->|"configures"| SecurityFilterC + SecurityFilterC -.->|"protects upload and delete requests"| HomeC + SecurityFilterC -.->|"protects delete requests"| DetailC +``` + +### Component Inventory + +| Component | Layer | Type | Responsibility | +|---|---|---|---| +| HomeController | Presentation | Spring MVC Controller | Displays the gallery and handles multi-file upload requests | +| DetailController | Presentation | Spring MVC Controller | Displays a photo, provides previous and next navigation, and handles deletion | +| PhotoFileController | Presentation | Spring MVC Controller | Serves image bytes from the database with the stored media type | +| Thymeleaf Views | Presentation | Server-rendered templates | Render the gallery and photo detail pages | +| PhotoService | Business Logic | Service interface | Defines photo retrieval, upload, navigation, and deletion operations | +| PhotoServiceImpl | Business Logic | Spring service | Validates uploaded images and coordinates photo persistence and retrieval | +| PhotoRepository | Data Access | Spring Data JPA repository | Performs entity CRUD and photo-specific Oracle queries | +| Photo | Data Access | JPA entity | Maps photo metadata and image BLOB data to the database | +| SecurityConfig | Infrastructure | Spring configuration | Defines password encoding, in-memory admin credentials, and HTTP authorization | +| Spring Security Filter Chain | Infrastructure | Security filters | Enforces HTTP Basic authentication for upload and delete endpoints | +| UploadResult | Models | Response model | Carries upload success, photo identifier, filename, and error information | diff --git a/.github/modernize/assessment/engines/facts/business-workflows.md b/.github/modernize/assessment/engines/facts/business-workflows.md new file mode 100644 index 000000000..53a3d2528 --- /dev/null +++ b/.github/modernize/assessment/engines/facts/business-workflows.md @@ -0,0 +1,82 @@ +# Core Business Workflows + +Photo Album lets users upload, browse, inspect, navigate, and remove photos from a shared gallery. Each accepted photo and its image data are kept together as a persistent gallery item. + +## Domain Entities + +| Entity | Service / Bounded Context | Description | Key Relationships | +|---|---|---|---| +| Photo | Photo Album / Gallery Management | A user-contributed image managed as a gallery item, with information used to display and navigate the collection. | Each photo is independently addressable in the gallery and is the unit of upload, viewing, navigation, and deletion. | +| UploadResult | Photo Album / Gallery Management | The outcome of processing an individual uploaded file, including its success or failure and any associated photo identifier. | Produced for each file submitted to the upload workflow; a successful result identifies the persisted Photo. | + +## Service-to-Domain Mapping + +The application is a single deployable Spring MVC application, not a set of independently deployable business services. Its presentation, business operations, and persistence components share the Photo Album bounded context. + +| Service | Domain Context | Owned Entities | External Dependencies | +|---|---|---|---| +| HomeController and DetailController | Gallery presentation and workflows | None; coordinates gallery operations | PhotoService | +| PhotoFileController | Photo delivery | None; retrieves image data for display | PhotoService | +| PhotoServiceImpl | Gallery management | Photo lifecycle and UploadResult outcomes | PhotoRepository; Oracle database through JPA | +| PhotoRepository | Gallery persistence | Persists and queries Photo records | Oracle database | + +All photo records, including their image data, are owned by this application and stored in Oracle. There are no cross-service APIs, events, or shared cross-context identifiers. + +## Primary Workflows + +### Workflow 1: Upload Photos + +An authenticated user submits one or more files through the upload interface to `POST /upload`. The controller processes each file independently through PhotoService. For each file, the service checks the supported MIME type, configured per-file size limit, non-empty content, and image pixel limit when dimensions can be read. Invalid files receive an individual failure result; accepted data is stored as a Photo in Oracle and returned with its identifier. The controller gathers successful photo summaries and per-file failures, allowing partial success when a batch contains both accepted and rejected files. + +### Workflow 2: Browse and View a Photo + +The gallery page at `GET /` requests photos ordered newest-first and renders each photo with its metadata. Selecting an item opens `GET /detail/{id}`; the controller retrieves that photo and asks the service for the adjacent older and newer photos by upload time. The image is served separately from `GET /photo/{id}` using the Photo's stored image data. If a photo cannot be found, the detail and image handlers return the user to the gallery or return not found, respectively. + +### Workflow 3: Delete a Photo + +An authenticated user submits the delete form to `POST /detail/{id}/delete`. PhotoService looks up the photo; if present, it deletes that record (including the database-stored image) and the controller reports success. If no photo exists, the controller reports that it was not found. The user is redirected to the gallery. + +## Cross-Service Data Flows + +There are no cross-service composition or aggregation flows: all business operations run within one application. The PhotoService coordinates with the PhotoRepository, which reads or writes the Photo and its image data in Oracle. Gallery, detail, and image responses are assembled from this same source of truth; no remote-service fallback path is implemented. + +## Business Workflow Sequence + +The upload workflow is the primary business process. Each submitted file is handled independently, so a failed validation or save for one file does not prevent the controller from processing the remaining files. + +```mermaid +sequenceDiagram + participant User + participant Gallery as "Gallery Controller" + participant PhotoService as "Photo Service" + participant Photo as "Photo" + participant Repository as "Photo Repository" + participant Oracle as "Oracle Database" + + User->>Gallery: Submit photo files to POST /upload + Gallery->>PhotoService: Process each file + PhotoService->>PhotoService: Check file type, size, content, and image dimensions + alt File passes upload rules + PhotoService->>Photo: Create gallery item with image and metadata + PhotoService->>Repository: Save photo + Repository->>Oracle: Persist photo record and image data + Oracle-->>Repository: Saved photo + Repository-->>PhotoService: Photo with identifier + PhotoService-->>Gallery: Successful upload result + else File fails a rule or cannot be saved + PhotoService-->>Gallery: Failed upload result with reason + end + Gallery-->>User: Report uploaded and failed files +``` + +## Business Rules & Decision Logic + +- **Supported file types:** Upload accepts MIME types configured as JPEG, PNG, GIF, or WebP. +- **Per-file size and content:** Each file must be non-empty and no larger than the configured 10 MiB limit. The HTTP multipart request also has a configured maximum request size. +- **Image dimensions:** When image dimensions can be read, images exceeding 40 million pixels are rejected. If dimensions cannot be extracted, upload continues without dimension metadata. +- **Batch outcomes:** Each file produces an independent outcome. The response includes successful photo summaries and individual failures; its overall success flag is true when at least one uploaded photo is included. +- **Gallery ordering and navigation:** Gallery entries are ordered newest-first. Detail navigation finds older and newer items using upload timestamps. +- **Photo lifecycle:** A successful upload creates a persistent Photo; deletion removes it from the gallery and database. No additional business lifecycle states are modeled. +- **Authorization:** Gallery and photo reads are public. Upload and delete POST operations require HTTP Basic authentication. +- **Persistence boundary:** PhotoService operations use transaction management for database changes. There are no distributed transactions, event-driven side effects, or compensating workflows. +- **Failure handling:** Missing photos yield not-found behavior for image retrieval and a not-found message for deletion. Upload validation and persistence failures are represented as per-file errors; no external-service fallback applies. diff --git a/.github/modernize/assessment/engines/facts/configuration-inventory.md b/.github/modernize/assessment/engines/facts/configuration-inventory.md new file mode 100644 index 000000000..cb59ba4bc --- /dev/null +++ b/.github/modernize/assessment/engines/facts/configuration-inventory.md @@ -0,0 +1,135 @@ +# Configuration & Externalized Settings Inventory + +The project uses Spring properties, a Docker Compose environment, and a git-ignored `.env` file populated from an example for database and administrator credentials. It has separate Docker and test runtime profiles; no remote configuration service or secret manager is configured. + +## Configuration Sources + +| Source | Type | Path/Location | Notes | +|---|---|---|---| +| Spring application configuration | Properties | `src/main/resources/application.properties` | Base settings, including Oracle connection placeholders and application defaults. | +| Spring Docker profile configuration | Properties | `src/main/resources/application-docker.properties` | Overrides logging levels; also repeats several base settings. | +| Spring test profile configuration | Properties | `src/test/resources/application-test.properties` | H2 in-memory database, test-only credentials, and test upload path. | +| Docker Compose | YAML | `docker-compose.yml` | Sets container environment variables, port mappings, persistent volume, network, health check, and database dependency. | +| Docker image build/runtime | Dockerfile | `Dockerfile` | Maven/Java image versions, exposed port, and JVM options. | +| Local deployment environment | Environment file template | `.env.example` (copied to git-ignored `.env`) | Supplies Oracle administrator, application schema, and application administrator credentials. Actual `.env` values are not inventoried. | +| Oracle initialization | SQL/shell | `oracle-init/01-create-user.sql`, `oracle-init/create-user.sh`, `oracle-init/02-verify-user.sql`, `oracle-init/healthcheck.sql` | Initialization scripts; credential-bearing values are passed through environment variables rather than embedded in the SQL source. | +| Maven project configuration | XML | `pom.xml` | Declares Spring Boot parent, Java version, dependencies, and packaging plugin. | + +No Spring Cloud Config repository, Azure/AWS configuration service, Vault, or dedicated secret store reference was found. GitHub Actions repository secrets are used by repository automation workflows, not by the application's runtime configuration. + +## Build Profiles + +| Profile | Activation | Purpose | Key Dependencies/Plugins | +|---|---|---|---| +| Default Maven build | Automatic; no Maven profiles are declared in `pom.xml` | Compiles and packages the executable application JAR. | Spring Boot Maven plugin; application dependencies are declared in the main dependency set. | +| Docker build | Docker build invocation; `Dockerfile` runs `mvn clean package -DskipTests` | Builds the application in a Maven builder image before copying the JAR into the runtime image. | `maven:3.9.6-eclipse-temurin-8`; no profile-specific dependencies. | + +## Runtime Profiles + +| Profile | Activation Method | Config Files | Key Overrides | +|---|---|---|---| +| Default | Spring default when no profile is active | `src/main/resources/application.properties` | Oracle connection defaults and base logging settings. | +| `docker` | `SPRING_PROFILES_ACTIVE=docker` in Compose | `application.properties`, `application-docker.properties` | Compose-provided Oracle URL/user/password; application log level `INFO`, web log level `WARN`, Hibernate SQL log level `DEBUG`. | +| `test` | `@ActiveProfiles("test")` on `PhotoAlbumApplicationTests` | `application.properties`, `src/test/resources/application-test.properties` | H2 in-memory database; test-only admin credentials and upload path; SQL display disabled. | + +Spring combines the active profile with base configuration. Compose activates only `docker`; the test class activates only `test`. No profile-specific config for production or profile combinations was found. + +## Properties Inventory + +### Application + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `server.port` | `8080` | default, docker | `application.properties`; repeated in `application-docker.properties` | +| `server.servlet.encoding.charset` | `UTF-8` | default, docker | Base and Docker properties | +| `server.servlet.encoding.enabled` | `true` | default, docker | Base and Docker properties | +| `server.servlet.encoding.force` | `true` | default, docker | Base and Docker properties | +| `spring.servlet.multipart.max-file-size` | `10MB` | default, docker | Base and Docker properties | +| `spring.servlet.multipart.max-request-size` | `50MB` | default, docker | Base and Docker properties | +| `app.file-upload.max-file-size-bytes` | `10485760` (10 MiB) | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.allowed-mime-types` | `image/jpeg,image/png,image/gif,image/webp` | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.max-files-per-upload` | `10` | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.upload-path` | Not set in base/Docker configuration | test | `application-test.properties`: `target/test-uploads` | +| `app.admin.username` | `admin` via `@Value` default; Compose can override with `APP_ADMIN_USERNAME` | default, docker | `SecurityConfig`; Compose variable defaults to `admin`; test profile sets `admin` | +| `app.admin.password` | Required in application configuration; no source-code default | default, docker | `SecurityConfig`; Compose requires `APP_ADMIN_PASSWORD`; test profile uses a test-only value | + +### Database + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `spring.datasource.url` | `jdbc:oracle:thin:@oracle-db:1521/FREEPDB1`, overridden by `SPRING_DATASOURCE_URL` | default, docker | Base and Docker properties; Compose supplies the container hostname URL | +| `spring.datasource.username` | `photoalbum`, overridden by `SPRING_DATASOURCE_USERNAME` | default, docker | Base and Docker properties; Compose uses `APP_USER` (default `photoalbum`) | +| `spring.datasource.password` | Required from `SPRING_DATASOURCE_PASSWORD` | default, docker | Base and Docker properties; Compose maps `APP_USER_PASSWORD` to the Spring variable | +| `spring.datasource.driver-class-name` | `oracle.jdbc.OracleDriver` | default, docker | Base and Docker properties | +| `spring.jpa.database-platform` | `org.hibernate.dialect.OracleDialect` | default, docker | Base and Docker properties | +| `spring.jpa.hibernate.ddl-auto` | `create` | default, docker | Base and Docker properties | +| `spring.jpa.show-sql` | `true` | default, docker | Base and Docker properties | +| `spring.jpa.properties.hibernate.format_sql` | `true` | default, docker | Base and Docker properties | +| `spring.datasource.url` | `jdbc:h2:mem:testdb` | test | `application-test.properties` | +| `spring.datasource.driver-class-name` | `org.h2.Driver` | test | `application-test.properties` | +| `spring.datasource.username` | `sa` | test | `application-test.properties` | +| `spring.datasource.password` | Empty | test | `application-test.properties` | +| `spring.jpa.database-platform` | `org.hibernate.dialect.H2Dialect` | test | `application-test.properties` | +| `spring.jpa.hibernate.ddl-auto` | `create-drop` | test | `application-test.properties` | +| `spring.jpa.show-sql` | `false` | test | `application-test.properties` | + +### Logging + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `logging.level.com.photoalbum` | `DEBUG` | default, test; `INFO` in docker | Base, test, and Docker properties | +| `logging.level.org.springframework.web` | `DEBUG` | default; `WARN` in docker | Base and Docker properties | +| `logging.level.org.hibernate.SQL` | Not set in base; `DEBUG` in docker | docker | `application-docker.properties` | + +## Startup Parameters & Resource Requirements + +| Service | JVM/Runtime Options | Memory | Instance Count | +|---|---|---|---| +| `photoalbum-java-app` | `JAVA_OPTS="-Xmx512m -Xms256m"`; Compose sets `SPRING_PROFILES_ACTIVE=docker` and database/admin environment overrides | JVM heap initial 256 MiB, maximum 512 MiB; container memory limit not specified | One Compose service/container declared; scaling not configured | +| `oracle-db` | Oracle container environment variables; no JVM options specified | Not specified | One Compose service/container declared; scaling not configured | + +No CPU allocation, container memory limit, or cloud resource sizing configuration was found. + +## Startup Dependency Chain + +1. `oracle-db` starts first; Compose health check runs `healthcheck.sh` every 30 seconds, with a 10-second timeout, 15 retries, and a 180-second start period. +2. `photoalbum-java-app` waits for `oracle-db` to be healthy through Compose `depends_on: condition: service_healthy`; it then starts with the `docker` Spring profile. + +No application-container health check or explicit Spring database connection retry/startup timeout was found. + +## Secrets & Sensitive Configuration + +| Secret Reference | Type | Storage (masked) | +|---|---|---| +| `ORACLE_PASSWORD` | Oracle administrator password | Local git-ignored `.env`, passed to Oracle container; `[MASKED]` | +| `APP_USER_PASSWORD` | Application database schema password | Local git-ignored `.env`, passed to Oracle and mapped to `SPRING_DATASOURCE_PASSWORD`; `[MASKED]` | +| `APP_ADMIN_PASSWORD` | Application administrator password | Local git-ignored `.env`, passed as an application environment variable; `[MASKED]` | +| `spring.datasource.password` / `SPRING_DATASOURCE_PASSWORD` | Spring datasource credential | Environment-variable reference; required, `[MASKED]` | +| Test `spring.datasource.password` | H2 test credential | Empty test-only value | +| Test `app.admin.password` | Test administrator credential | `application-test.properties`; test-only value, `[MASKED]` | + +### Secrets Provisioning Workflow + +Copy `.env.example` to `.env`, replace all placeholder values with strong unique credentials, and run Docker Compose. Compose requires `ORACLE_PASSWORD`, `APP_USER_PASSWORD`, and `APP_ADMIN_PASSWORD`; it forwards the Oracle administrator and schema credentials to the database container and maps the schema/admin credentials into the application container. `.env` is documented as git-ignored and must not be committed. No managed identity, service principal, RBAC setup, encrypted property mechanism, or external secret manager is configured. Test credentials are isolated in test resources. + +## Feature Flags + +No feature-flag framework, application feature toggle, conditional Spring configuration, or rollout flag was found. + +| Flag Name | Default | Controlled By | +|---|---|---| +| None identified | N/A | N/A | + +## Framework & Runtime Versions + +| Component | Version | Source | +|---|---|---| +| Spring Boot | `2.7.18` | `pom.xml` parent | +| Java source/target | `8` | `pom.xml` | +| Maven | `3.9.6` in Docker build image | Dockerfile base image | +| Java build runtime | Eclipse Temurin 8 | `maven:3.9.6-eclipse-temurin-8` Docker image | +| Java application runtime | Eclipse Temurin 8 JRE | `eclipse-temurin:8-jre` Docker image | +| Oracle JDBC driver | `ojdbc8`; resolved version managed by Spring Boot dependency management | `pom.xml`; no explicit version declared | +| H2 | Version managed by Spring Boot dependency management | `pom.xml`, test scope | +| Commons IO | `2.11.0` | `pom.xml` | +| Oracle Database Free image | `latest` tag (version not pinned) | `docker-compose.yml` | diff --git a/.github/modernize/assessment/engines/facts/data-architecture.md b/.github/modernize/assessment/engines/facts/data-architecture.md new file mode 100644 index 000000000..b92000ba3 --- /dev/null +++ b/.github/modernize/assessment/engines/facts/data-architecture.md @@ -0,0 +1,70 @@ +# Data Architecture & Persistence Layer + +The application has one JPA entity, `Photo`, persisted in a relational database through Hibernate. Oracle is configured for the normal and Docker profiles, while tests use an in-memory H2 database. + +## Database Configuration + +| Service/Module | DB Type | Profile | Driver | Connection | Migration Tool | +|---|---|---|---|---|---| +| Photo Album | Oracle | Default (no explicit profile) | Oracle JDBC (`ojdbc8`) | JDBC URL defaults to `jdbc:oracle:thin:@oracle-db:1521/FREEPDB1`; URL and credentials can be supplied through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured. | None. Hibernate creates the schema at startup; no versioned migrations or seed scripts were found. | +| Photo Album | Oracle | `docker` | Oracle JDBC (`ojdbc8`) | Same Oracle service URL by default; Docker supplies the URL and credentials through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured. | None. Hibernate creates the schema at startup. | +| Photo Album | H2 (in-memory) | `test` | H2 (`org.h2.Driver`, test-scoped dependency) | `jdbc:h2:mem:testdb` | None. Hibernate creates the schema for tests and drops it when the test context closes. | + +The database and schema-generation settings are defined in the application property files; see `configuration-inventory.md` for the property-level inventory. No database seed data was found. + +## Data Ownership per Service + +| Service | Tables Owned | ORM Framework | Caching | Notes | +|---|---|---|---|---| +| Photo Album | `PHOTOS` (`Photo`) | Spring Data JPA / Hibernate | None found | Single application-owned table; photo bytes are stored in a BLOB. | + +## Entity Model + +`Photo` maps to the `photos` table and owns its upload metadata and image bytes. The table has an index on `uploaded_at`. Its identifier is a UUID string generated in Java. There are no entity relationships or join tables. `UploadResult` is a service response model, not a persisted entity. + +Source: `src/main/java/com/photoalbum/model/Photo.java`. + +The service implementation applies transaction boundaries at class level, with read-only transactions for retrieval methods: `src/main/java/com/photoalbum/service/impl/PhotoServiceImpl.java`. The model's `photo_data` mapping is nullable even though the normal upload path supplies the photo bytes. + +```mermaid +erDiagram + Photo { + string Id PK + string OriginalFileName + bytes PhotoData "nullable BLOB" + string StoredFileName + string FilePath + long FileSize + string MimeType + datetime UploadedAt + int Width + int Height + } +``` + +## Key Repository Methods + +| Service | Repository | Notable Methods | Purpose | +|---|---|---|---| +| Photo Album | `PhotoRepository` — `src/main/java/com/photoalbum/repository/PhotoRepository.java`, entity `Photo` | `findAllOrderByUploadedAtDesc(): List` | Native query returns photos newest first. | +| Photo Album | `PhotoRepository` | `findPhotosUploadedBefore(LocalDateTime): List` | Native Oracle query selects up to ten older photos for navigation. | +| Photo Album | `PhotoRepository` | `findPhotosUploadedAfter(LocalDateTime): List` | Native Oracle query selects newer photos for navigation. | +| Photo Album | `PhotoRepository` | `findPhotosByUploadMonth(String, String): List` | Native Oracle query filters by year and month. | +| Photo Album | `PhotoRepository` | `findPhotosWithPagination(int, int): List` | Native Oracle `ROWNUM` query returns a row range. | +| Photo Album | `PhotoRepository` | `findPhotosWithStatistics(): List` | Native Oracle analytical query returns photos with ranking and running-total columns. | + +The repository extends `JpaRepository`, inheriting standard CRUD operations. The custom queries are native SQL, several of which use Oracle-specific features; no cross-service aggregation methods were found. + +## Caching Strategy + +No application cache provider, cache annotations, cache manager, JCache binding, or Hibernate second-level cache configuration was found. Data access uses repository/database queries without an identified application-level caching pattern or TTL/eviction policy. + +## Data Ownership Boundaries + +The project has one application and one logical data owner. The default and Docker profiles connect to Oracle; the test profile uses an isolated in-memory H2 database. The application reads and writes its own `PHOTOS` table through Spring Data JPA, with transaction management in the service layer. There are no other services, cross-service database access patterns, or CQRS/read-model boundaries identified. + +### Data Classification & Sensitivity + +| Entity | Sensitive Fields | Classification (PII/PHI/PCI/None) | Controls in Place | +|---|---|---|---| +| `Photo` | `original_file_name`, `photo_data` (image content may identify people) | Potential PII | No field-level encryption, data masking, or field-level access controls were found in the application configuration. Encryption at rest is not established by the project configuration. No PHI or PCI fields were identified. | diff --git a/.github/modernize/assessment/engines/facts/dependency-map.md b/.github/modernize/assessment/engines/facts/dependency-map.md new file mode 100644 index 000000000..f26460dfc --- /dev/null +++ b/.github/modernize/assessment/engines/facts/dependency-map.md @@ -0,0 +1,72 @@ +# Dependency Map + +Photo Album declares 11 direct dependencies: 9 non-test dependencies and 2 test-scoped dependencies. Versions not explicitly set in `pom.xml` are managed by Spring Boot Starter Parent 2.7.18. + +## Dependencies + +```mermaid +flowchart LR + App["Photo Album"] + BootParent["Spring Boot Starter Parent 2.7.18"] + + subgraph Web["Web Frameworks"] + WebStarter["Spring Boot Web Starter v2.7.18"] + Thymeleaf["Spring Boot Thymeleaf Starter v2.7.18"] + end + subgraph Database["Database / ORM"] + Jpa["Spring Boot Data JPA Starter v2.7.18"] + Oracle["Oracle JDBC ojdbc8 v21.5.0.0"] + end + subgraph Security["Security"] + SecurityStarter["Spring Boot Security Starter v2.7.18"] + end + subgraph Utilities["Utilities"] + Validation["Spring Boot Validation Starter v2.7.18"] + Json["Spring Boot JSON Starter v2.7.18"] + CommonsIO["Apache Commons IO v2.11.0"] + DevTools["Spring Boot DevTools v2.7.18"] + end + + App -->|"web"| Web + App -->|"persistence"| Database + App -->|"security"| Security + App -->|"utilities"| Utilities + BootParent -.->|"manages versions"| WebStarter + BootParent -.->|"manages versions"| Thymeleaf + BootParent -.->|"manages versions"| Jpa + BootParent -.->|"manages version"| Oracle + BootParent -.->|"manages versions"| SecurityStarter + BootParent -.->|"manages versions"| Validation + BootParent -.->|"manages versions"| Json + BootParent -.->|"manages versions"| DevTools +``` + +### Dependency Summary + +| Category | Count | Key Libraries | Notes | +|---|---:|---|---| +| Web Frameworks | 2 | Spring Boot Web, Thymeleaf | Spring MVC application with server-rendered templates | +| Database / ORM | 2 | Spring Data JPA, Oracle JDBC | ORM and Oracle runtime driver | +| Security | 1 | Spring Boot Security | Spring-managed security stack | +| Utilities | 4 | Validation, JSON, Commons IO, DevTools | DevTools is optional; Commons IO is explicitly pinned | + +### Version & Compatibility Risks + +Spring Boot 2.7.18 is the final 2.x release and is outside the community support window; migration to a supported Spring Boot line will also require reviewing Java 8 compatibility, since newer Spring Boot generations require a newer Java baseline. The Oracle JDBC driver is selected through the Boot parent dependency management, so its effective version should be re-evaluated as part of that upgrade. + +### Notable Observations + +- The parent POM manages versions for all direct dependencies except Commons IO, which is explicitly set to 2.11.0. +- The Spring Boot Web Starter already provides JSON support; the separately declared JSON starter may duplicate that functionality. +- The Oracle JDBC driver is runtime-scoped, while H2 is confined to tests. + +## Test Dependencies + +| Framework | Version | Notes | +|---|---|---| +| Spring Boot Test Starter | 2.7.18 | Test-scope starter; its underlying test libraries are transitive and not individually declared in `pom.xml` | +| H2 Database | 2.1.214 | Test-scope in-memory database; version managed by Spring Boot 2.7.18 | + +Total test-scope dependencies: 2 + +The project has a Spring Boot test starter and an in-memory database for tests. The individual versions of transitive test libraries are not pinned in the available build file. diff --git a/.github/modernize/assessment/reports-20261104093550/aggregate-report.json b/.github/modernize/assessment/reports-20261104093550/aggregate-report.json new file mode 100644 index 000000000..5c2803d17 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/aggregate-report.json @@ -0,0 +1,1196 @@ +{ + "version": "1.0.0", + "producer": "GitHub Copilot Modernization CLI", + "metadata": { + "analysisStartTime": "2026-11-04T09:35:50.0837979Z", + "analysisEndTime": "2026-11-04T09:35:50.0837979Z", + "status": "Complete", + "privacyMode": "Protected", + "privacyModeHelpUrl": "https://aka.ms/appcat-privacy-mode", + "targetIds": [ + "azure-appservice", + "azure-aks", + "azure-container-apps" + ], + "targetDisplayNames": [ + "Azure App Service", + "Azure Kubernetes Service", + "Azure Container Apps" + ], + "capabilities": [ + "openjdk25" + ], + "os": [ + "linux", + "windows" + ], + "domains": [ + "java-upgrade", + "cloud-readiness" + ], + "mode": "full", + "minimumCveSeverity": null, + "repos": [ + { + "identity": "PhotoAlbum-Java", + "name": "PhotoAlbum-Java", + "path": "C:\\Users\\ThomasJuul\\source\\repos\\PhotoAlbum-Java", + "sourceRepo": "PhotoAlbum-Java", + "appIdentifiers": [ + "PhotoAlbum-Java" + ] + } + ] + }, + "summary": { + "totalProjects": 1, + "totalIssues": 10, + "totalIncidents": 31, + "totalEffort": 196, + "charts": { + "severity": { + "mandatory": 13, + "optional": 2, + "potential": 16, + "information": 0 + }, + "category": { + "auth-migration": 1, + "database-migration": 6, + "framework-upgrade": 12, + "local-credential": 4, + "java-version-upgrade": 3, + "spring-migration": 4, + "jakarta-migration": 1 + } + } + }, + "projects": [ + { + "path": ".", + "issues": 10, + "storyPoints": 196, + "properties": { + "appName": "photo-album", + "jdkVersion": "1.8", + "frameworks": [ + "Spring Boot", + "Spring" + ], + "languages": [ + "Java", + "JavaScript" + ], + "tools": [ + "Maven" + ], + "repo": "PhotoAlbum-Java", + "securityMandatory": 0, + "securityPotential": 0, + "securityOptional": 0, + "rearchitectCount": 0 + }, + "incidents": [ + { + "ruleId": "auth-02000", + "incidentId": "b845d4f9-3276-4be7-a051-635b67d9259a", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1bdf6e21-f623-4f14-9339-ae1a0ba204d2", + "location": "pom.xml", + "locationKind": "File", + "line": 52, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "44b171b1-2ca1-4627-a673-8060fdce5f29", + "location": "docker-compose.yml", + "locationKind": "File", + "line": 32, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "40510a44-6c1f-49c5-9103-c5d2a17a7682", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 3, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "5f157c91-507e-4794-86f9-6799a711160c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 12, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "bc62976f-e22d-4434-9046-b0836c0e6b7c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 15, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1004341d-8548-4a06-afba-e56fdf194ba1", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 6, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "995a2b3e-4870-4e04-8d6d-a5701427008a", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "97097182-5728-42f1-b69e-5b16a66937ff", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "154a6aea-7e34-49c7-8d83-8897ac979f65", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "433cc960-b5ae-4a76-a45b-2c34d446e0a3", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "a95a682d-dad5-4275-8d6d-6d43ed4188cd", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b3814a33-4403-4442-a2f0-a119b0f6248a", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 23, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "1ac607ee-8338-440c-b110-79d433327062", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b9c9e02a-c269-4137-9d82-f8671fcb2130", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 14, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-01000", + "incidentId": "36436d2b-df81-40eb-a0fe-d3f8ede23005", + "location": "pom.xml", + "locationKind": "File", + "line": 24, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "927cfd73-3a17-4888-99f0-b00ccd7d84ce", + "location": "pom.xml", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "9b3a9911-d9d3-4190-a82b-6f28e3a0ad14", + "location": "pom.xml", + "locationKind": "File", + "line": 26, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "6c645761-fb02-4d39-a079-1389acb57081", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "8474445f-4d9f-4410-ba08-4eea0a5f407b", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "615ff34c-58b8-44ac-850a-0760d5125fe9", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "2c17f79e-3853-464c-8de3-42ffb30fb98e", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c61300ea-5005-4813-a64d-41cc57d2d16f", + "location": "pom.xml", + "locationKind": "File", + "line": 40, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "4264b153-88a3-4faf-a76f-a4c4b13bf6d1", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "3507d6d7-3397-428f-883f-aa62f6d638de", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "7cd37e0b-2862-40da-b295-7cfecdf85b33", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "69e09b4a-1b8a-4a8c-b2f6-61f53bf7a6cf", + "location": "pom.xml", + "locationKind": "File", + "line": 78, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "b1800a7a-6a4b-4b66-8e98-bbbff27bafa4", + "location": "pom.xml", + "locationKind": "File", + "line": 59, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "bcf5c6b6-36a4-45ae-ac0c-c941c264a286", + "location": "pom.xml", + "locationKind": "File", + "line": 98, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c5ff81c6-2bdf-417a-b7a5-6943bdb72930", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "jakarta-database-00002", + "incidentId": "73c943cd-017a-4dbe-965e-8128b10fdc4e", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=cloud-readiness" + ] + } + ] + } + ], + "rules": { + "auth-02000": { + "id": "auth-02000", + "description": "The application embeds a Spring Security library. Ensure that the application is configured to access the identity provider.\n\nConsider using Microsoft Entra ID as an identity provider.", + "title": "Embedded framework - Spring Security", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-app-service#identity-providers", + "title": "Spring Security-related dependencies found in the project" + }, + { + "url": "https://docs.microsoft.com/azure/active-directory/external-identities/azure-ad-account", + "title": "Azure Active Directory (Azure AD) identity provider for External Identities" + }, + { + "url": "https://docs.spring.io/spring-security/reference/index.html", + "title": "Spring Security" + }, + { + "url": "https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization", + "title": "Spring Boot API: Authorization" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=auth-migration", + "os=windows", + "os=linux" + ] + }, + "azure-database-microsoft-oracle-07000": { + "id": "azure-database-microsoft-oracle-07000", + "description": "Oracle database found. To migrate a Java application that uses an Oracle database to Azure, you can follow these recommendations:\n\n * **Migrate to Azure Database for PostgreSQL**: Azure recommends migrating Oracle databases to Azure Database for PostgreSQL Flexible Server as it provides better cost-effectiveness and performance. Create a managed PostgreSQL Flexible Server database in Azure and choose the appropriate pricing tier based on your application\u0027s requirements.\n\n * **Use migration tools**: Utilize the Azure Database Migration Service (DMS) or third-party tools to migrate your Oracle database schema and data to PostgreSQL. Consider using ora2pg or similar tools to convert Oracle-specific SQL to PostgreSQL-compatible SQL.\n\n * **Update database drivers and connection strings**: Replace Oracle JDBC drivers with PostgreSQL drivers in your Java application. Update connection strings from Oracle format (jdbc:oracle:thin:) to PostgreSQL format (jdbc:postgresql:).\n\n * **Review and convert Oracle-specific code**: Identify and convert Oracle-specific SQL functions, stored procedures, and PL/SQL code to PostgreSQL equivalents. Pay attention to data types, syntax differences, and built-in functions.\n\n * Enable **monitoring and diagnostics**: Utilize Azure Monitor to gain insights into the performance and health of your Java application and the underlying PostgreSQL database. Set up metrics, alerts, and log analytics to proactively identify and resolve issues.\n\n * Implement **security** measures: Apply security best practices to protect your Java application and the PostgreSQL database. This includes implementing authentication and authorization mechanisms with passwordless connections and leveraging Microsoft Defender for Cloud for threat detection and vulnerability assessments.\n\n * **Backup** your data: Azure Database for PostgreSQL provides automated backups by default. You can configure the retention period for backups based on your requirements. You can also enable geo-redundant backups, if needed, to enhance data durability and availability.", + "title": "Oracle database found", + "severity": "potential", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/postgresql", + "title": "Azure Database for PostgreSQL documentation" + }, + { + "url": "https://learn.microsoft.com/azure/postgresql/migrate/how-to-migrate-oracle-ora2pg", + "title": "Oracle to PostgreSQL migration guide" + }, + { + "url": "https://learn.microsoft.com/azure/dms", + "title": "Azure Database Migration Service documentation" + }, + { + "url": "https://learn.microsoft.com/azure/azure-monitor", + "title": "Azure Monitor documentation" + }, + { + "url": "https://learn.microsoft.com/azure/defender-for-cloud", + "title": "Microsoft Defender for Cloud" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=database-migration", + "database", + "oracle", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-01000": { + "id": "azure-java-version-01000", + "description": "The application is using a Java version that has reached the end of support. It is strongly recommended to plan and execute a migration strategy to upgrade your application to a supported Java version.\nSupported Java versions receive long-term support (LTS) from the Java community, including bug fixes and updates. Migrating to a supported version provides you with a stable and well-maintained platform for your application.", + "title": "Java Version Has Reached the End of Support", + "severity": "mandatory", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-02000": { + "id": "azure-java-version-02000", + "description": "The application is not using the latest LTS Java version. It is recommended to consider upgrading to the latest LTS version to take advantage of the newest language features, performance improvements, and extended support timelines.\nUpgrading to the latest LTS version ensures your application benefits from the most recent security enhancements and a longer support lifecycle.", + "title": "Java Version is not the latest LTS", + "severity": "optional", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-password-01000": { + "id": "azure-password-01000", + "description": "Using clear passwords in property files is a security risk, as they can be easily compromised if the files are accessed by unauthorized individuals. To enhance the security of your application, it is recommended to employ secure credential management practices.\n\n * **Azure Key Vault**: Utilize Azure Key Vault to securely store and manage your application\u0027s passwords and other sensitive credentials. Azure Key Vault provides a centralized and highly secure location for storing secrets, keys, and certificates.\n\n * **Passwordless connections**: You can provide an additional layer of security and convenience for accessing resources in Azure by eliminating the need for passwords. This way you can reduce the risk of password-related vulnerabilities, such as weak passwords or password theft.", + "title": "Password found in configuration file", + "severity": "potential", + "effort": 3, + "links": [ + { + "url": "https://learn.microsoft.com/azure/key-vault", + "title": "Azure Key Vault documentation" + }, + { + "url": "https://learn.microsoft.com/azure/developer/intro/passwordless-overview", + "title": "Passwordless connections for Azure services" + }, + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#inventory-configuration-sources-and-secrets", + "title": "Password found in configuration file" + }, + { + "url": "https://docs.microsoft.com/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-key-vault", + "title": "Read a secret from Azure Key Vault in a Spring Boot application" + }, + { + "url": "https://search.maven.org/artifact/com.azure.spring/azure-spring-boot-starter-keyvault-secrets", + "title": "Azure Spring Boot Starter for Azure Key Vault Secrets" + } + ], + "labels": [ + "source", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=local-credential", + "password", + "security", + "os=windows", + "os=linux" + ] + }, + "jakarta-database-00002": { + "id": "jakarta-database-00002", + "description": "The application depends on **Jakarta Persistence (JPA)** APIs (\u0060jakarta.persistence.*\u0060 or legacy \u0060javax.persistence.*\u0060), which are used for object-relational mapping (ORM) and database interaction in Jakarta EE or Java EE applications.\n\nWhen migrating to Azure:\n- Ensure that the database connection, JPA provider (e.g., Hibernate, EclipseLink), and dialect are compatible with your target Azure database service.\n- Recommended database services include **Azure Database for PostgreSQL**, **Azure Database for MySQL**, or **Azure SQL Database**.\n- For containerized deployments, these JPA-based applications can run on **Azure Kubernetes Service (AKS)** or **Azure App Service for Linux/Windows**.\n- If using **Spring Data JPA**, verify that connection pool settings and environment variables are properly configured for the cloud environment.\n- Consider leveraging **Azure Key Vault** for secure storage of database credentials and connection strings.", + "title": "Detects usage of Jakarta Persistence (JPA) APIs", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://jakarta.ee/specifications/persistence/", + "title": "Jakarta Persistence Specification" + }, + { + "url": "https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/data-stores-getting-started#common-database-scenarios", + "title": "Prepare to choose a data store in Azure" + } + ], + "labels": [ + "source=java", + "source=java-ee", + "target=azure-aks", + "target=azure-container-apps", + "target=azure-appservice", + "domain=cloud-readiness", + "category=jakarta-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-port-01000": { + "id": "spring-boot-to-azure-port-01000", + "description": "The application is setting the server port. To migrate a Java application that sets the server port to Azure Container Apps:\n\n * **Azure Container Apps allows you to expose port according to your Azure Container Apps resource configuration. For instance, a Spring Boot application listens to port of 8080 by default, but it can be set with server.port or environment variable SERVER_PORT as you need.", + "title": "Server port configuration found", + "severity": "potential", + "effort": 1, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#identify-any-clients-relying-on-a-non-standard-port", + "title": "Identify any clients relying on a non-standard port" + } + ], + "labels": [ + "source=springboot", + "target=azure-aks", + "target=azure-appservice", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=spring-migration", + "port", + "server port", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-restricted-config-01000": { + "id": "spring-boot-to-azure-restricted-config-01000", + "description": "The application uses restricted configurations for Azure Container Apps.\n These properties can be automatically injected into your application environment by Azure Container Apps to access managed Config Server and managed Eureka Server.\n Please remove them from your application, including configuration files, config server files, command line parameters, Java system attributes, and environment variables.\n\n If configured in **configuration files**: they will be ignored and overrided by Azure Container Apps.\n \n If configured in **Config Server files**, **command line parameters**, **Java system attribute**, **environment variable**: they need to be removed or you might experience conflicts and unexpected behavior.", + "title": "Restricted configurations found", + "severity": "potential", + "effort": 2, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-cloud-to-azure-container-apps#remove-restricted-configurations", + "title": "Migrate Spring Boot applications to Azure Container Apps - Remove restricted configurations" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-config-server?tabs=azure-cli", + "title": "Connect to a managed Config Server for Spring in Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-eureka-server?tabs=azure-cli", + "title": "Connect to a managed Eureka Server for Spring in Azure Container Apps" + } + ], + "labels": [ + "target=azure-container-apps", + "source=springboot", + "domain=cloud-readiness", + "category=spring-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-spring-boot-version-01000": { + "id": "spring-boot-to-azure-spring-boot-version-01000", + "description": "The application is using a Spring Boot version that has reached its End of OSS Support.\nWith the officially supported new versions from Spring, you can get the best experience. Here are some steps you can take to update your application to the latest version of Spring Boot:\n\n* Choose a **supported Spring Boot version**: Check out Spring Boot Support Versions and determine the most suitable supported Spring Boot version.\n\n* **Update Spring Boot version**: Update the Spring Boot version of your application. There are automated tools like Rewrite to help you with the migration.\n\n* **Address code compatibility**: Review your application\u0027s codebase for any potential compatibility issues with the target Spring Boot version. Update deprecated APIs or features, address any language or library changes, and ensure that your code follows best practices and standards.\n\n* **Test thoroughly**: Execute a comprehensive testing process to verify the compatibility and functionality of your application with the new Spring Boot version. Perform unit tests, integration tests, and system tests to validate that all components and dependencies work as expected.", + "title": "Spring Boot Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps", + "title": "Migrate Spring Boot applications to Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-microservice-get-started?tabs=azure-cli", + "title": "Launch your first Java microservice application with managed Java components in Azure Container Apps" + }, + { + "url": "https://spring.io/projects/spring-boot/#support", + "title": "Spring Boot Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-boot/wiki/Supported-Versions", + "title": "Spring Boot Support Policy" + } + ], + "labels": [ + "source=springboot", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "spring-framework-version-01000": { + "id": "spring-framework-version-01000", + "description": "Your application is using a Spring Framework version that has reached its End of OSS Support.\nUpgrading to a supported version ensures better performance, security, and compatibility with modern tools.\n 1. Pick a Supported Version: Review the Spring Framework support policy and choose an actively supported version.\n 2. Update Your Project: Change the Spring Framework version in your pom.xml or build.gradle.\n 3. Fix Compatibility Issues: Update deprecated code, replace removed features, and ensure dependencies are compatible with the new Spring Framework version.\n 4. Thoroughly Test: Run unit, integration, and end-to-end tests to make sure everything still works after the upgrade.", + "title": "Spring Framework Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://spring.io/projects/spring-framework#support", + "title": "Spring Framework Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions", + "title": "Spring Framework Support Policy" + } + ], + "labels": [ + "source=spring", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + } + }, + "apps": [ + { + "identifier": "PhotoAlbum-Java", + "repos": [ + "PhotoAlbum-Java" + ] + } + ] +} \ No newline at end of file diff --git a/.github/modernize/assessment/reports-20261104093550/apps/PhotoAlbum-Java/application-topology-graph.md b/.github/modernize/assessment/reports-20261104093550/apps/PhotoAlbum-Java/application-topology-graph.md new file mode 100644 index 000000000..a567cf56d --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/apps/PhotoAlbum-Java/application-topology-graph.md @@ -0,0 +1,16 @@ +# Topology Graph: PhotoAlbum-Java + +Analyzed 1 service(s) across 1 repository for application "PhotoAlbum-Java" on 2026-11-04 09:35 UTC. + +## Services + +```mermaid +flowchart TD + photoalbumJava["PhotoAlbum-Java\n(WebApi)"] +``` + +## Service Details + +| Service | Role | Language | Source Repository | +|---------|------|----------|------------------| +| PhotoAlbum-Java | WebApi | java | PhotoAlbum-Java | diff --git a/.github/modernize/assessment/reports-20261104093550/index.html b/.github/modernize/assessment/reports-20261104093550/index.html new file mode 100644 index 000000000..d421293be --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/index.html @@ -0,0 +1,887 @@ + + + +Consolidated Assessment Report + + + + +
+
+

Aggregated Assessment Report

+
Created on Nov 04, 2026
+
+ +
+
+
+
Executive insights
+
+

The modernization work is concentrated: PhotoAlbum-Java carries 100% of the estimated effort (56 SP out of 56 SP total).

+

Across 1 component(s) in 1 business app(s), the assessment found 9 issues: 3 mandatory blockers, 5 potential risks, and 1 optional improvements.

+

3 mandatory blocker(s) must be resolved before migration can proceed.

+
+
+
+
+
+
Issue summary
+
+ 1 of 1 + scanned business apps need modernization +
+
+ View all assessment results +
+
+
+ PhotoAlbum-Java +
+
+
+
+
+ 9 issues +
Large
+
+
+ +
+
+
+
+
+ Effort distribution + Relative modernization effort across 1 business apps +
+View more +
+
+
+
Effort size
+
Extra largeLargeMediumSmall
+
+
+ Large
+
+
+
+PhotoAlbum-Java +
+
Business Apps
+
+
+
+
+ Technologies + Detected languages across the portfolio +
+Tech details +
+
+
+
Value
+
+1 +0 +
+
+
+ 1
+
+
+
+Java +
+
Languages
+
+
+
+
+
+
+
+
+
Recommendation insights
+
+

4 Azure service recommendation(s) identified to support the target architecture.

+

Migration is sequenced into waves ordered by ascending effort to deliver early wins and build team confidence.

+
+
+
+

Suggested modernization sequence

+
+

Reference sequence for creating the modernization plan. Use it with the full assessment report before deciding execution work.

+
+
+ +
+
+
Wave 1 — Quick wins
+
+
0 business apps·0 components·0 SP estimated effort
+

Use the smallest in-scope services to define deployment and validation patterns before larger workstreams.

+
—
+
+
+
+ +
+
+
Wave 2 — Core modernization
+
+
1 business app·1 component·56 SP estimated effort
+

Include integration refactoring as a contained modernization workstream.

+
PhotoAlbum-Java
+
+
+
+ +
+
+
Wave 3 — Complex upgrades
+
+
0 business apps·0 components·0 SP estimated effort
+

Plan after shared patterns are established; this wave carries the largest upgrade, validation, and packaging scope.

+
—
+
+
+
+
+
+

Migration Strategy Recommendation

+
+ + + +
Business AppRepositoryComponentStrategyRationaleEffort
PhotoAlbum-JavaPhotoAlbum-Javaphoto-albumReplatformFramework upgrade needed; replatform to Azure with targeted changes.Large
+
+

Recommended Azure Services

+
+ + + + + + +
Target ServiceBusiness AppRepositoryComponentLanguageFrameworkRationale
Microsoft Entra IDPhotoAlbum-JavaPhotoAlbum-Javaphoto-albumJava, JavaScriptSpring Boot, SpringMicrosoft Entra ID integrates with Spring Security via Azure AD Spring Boot Starter for cloud-native authentication and authorization.
Azure Database for PostgreSQLPhotoAlbum-JavaPhotoAlbum-Javaphoto-albumJava, JavaScriptSpring Boot, SpringAzure Database for PostgreSQL is a modern, fully managed open-source alternative to Oracle with lower licensing costs and Azure-native integration.
Azure Key Vault with Managed IdentityPhotoAlbum-JavaPhotoAlbum-Javaphoto-albumJava, JavaScriptSpring Boot, SpringStoring credentials in Azure Key Vault with managed identity eliminates plaintext secrets from code, improving security and compliance.
Azure Kubernetes ServicePhotoAlbum-JavaPhotoAlbum-Javaphoto-albumJava, JavaScriptSpring Boot, SpringTarget compute platform for application hosting.
+
+

Recommended Upgrade Paths

+
+ + + + +
Business AppRepositoryComponentCurrentTargetUpgrade Path
PhotoAlbum-JavaPhotoAlbum-Javaphoto-albumJava 8Upgrade to Java 25Java 25 is the latest LTS version supported by GitHub Copilot Modernization, enabling automated upgrades with minimal manual effort.
PhotoAlbum-Javaphoto-albumSpring Boot 2.7.xUpgrade to Spring Boot 4.0Spring Boot 4.0 is the latest LTS version supported by GitHub Copilot Modernization, enabling automated upgrades with minimal manual effort.
+
+
+
+
+
Assessment insights
+
+

Detailed per-business-app assessment showing 1 component(s) across 1 business app(s).

+

1 component(s) have mandatory blockers that must be resolved before migration.

+
+
+

PhotoAlbum-Java (56 SP)

+
+ + + + + + + + + + + + +
RepositoryComponentRuntime / FrameworkTargetUpgrade PathModernization scopeIssuesEffort
PhotoAlbum-Javaphoto-albumJava 8, Spring Boot 2.7.xAzure Kubernetes ServiceUpgrade to Java 25, Upgrade to Spring Boot 4.0Replatform
+
Mandatory (3)
+
Potential (5)
+
Optional (1)
+
Large
+
+
+
+ + + 1 business app +
+
+
+

PhotoAlbum-Java

+ 1 service · 1 repository · 0 dependencies +
+
+

Service graph

+

Arrows point from a service to the service it depends on.

+
+
flowchart TD
+    photoalbumJava["PhotoAlbum-Java\n(WebApi)"]
+
+
+
+

Service details

+
+ + + +
ServiceRoleLanguageSource Repository
PhotoAlbum-JavaWebApijavaPhotoAlbum-Java
+
+
+
+
+
+
+
Architecture & cost insights
+
+
+
+ Portfolio monthly estimate + ~$95/month +
+
+
+ +

These cost estimates are directional and based on Azure retail pricing using pre-selected SKUs aligned to a baseline dev/test environment. Consumption-based services use assumed default usage volumes. Actual production costs will vary and are likely to change based on application-specific sizing, performance requirements, real-world usage patterns, selected region, reserved instance or savings plan commitments, and enterprise agreement discounts.

+
+
+ How are these estimates calculated? +
+

Each recommended Azure service is mapped to a specific SKU suitable for a dev/test baseline. Prices are retrieved from the Azure Retail Prices API for the East US region.

+

Normalization rules:

+
    +
  • Hourly services (e.g. App Service B1, VMs) — unit price × 730 hours/month
  • +
  • Daily services (e.g. SQL Database DTU) — unit price × 30 days/month
  • +
  • Consumption services (e.g. Container Apps, Functions) — fixed assumed monthly usage volume
  • +
  • Monthly services (e.g. Service Bus Standard) — listed price as-is
  • +
+

No reserved instance, savings plan, or enterprise agreement discounts are applied. The lowest non-zero retail price matching the SKU filter is selected when multiple meter options exist.

+
+
+
+
+

photo-album cost breakdown (~$95/month, 56 SP)

+
+ + + + + + + +
ServiceSKUMonthly CostPricing Model
Microsoft Entra IDP1~$6Per user/month
Azure Database for PostgreSQLBurstable B1ms$14Per vCore/hour
Azure Key Vault with Managed IdentityStandard~$5Per transaction
Azure Kubernetes ServiceStandard D2s v3$70Per node/hour
Total~$95
+
+
diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/api-service-contracts.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/api-service-contracts.md new file mode 100644 index 000000000..786b3443c --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/api-service-contracts.md @@ -0,0 +1,93 @@ +# API & Service Communication Contracts + +The application exposes five HTTP routes: server-rendered gallery/detail pages, a multipart upload endpoint with a JSON response, and an endpoint that serves photo bytes. Requests are handled synchronously within one application and synchronously persisted to the database; no inter-service API or asynchronous messaging was found. + +## Service Catalog + +| Service | Port | Category | Purpose | +|---|---:|---|---| +| Photo Album Java (`photo-album`) | 8080 | API Layer | Source-built Spring MVC and Thymeleaf web application providing the photo gallery, upload, detail, delete, and image-serving routes. | +| Oracle Database (`oracle-db`) | 1521 | Infrastructure | Third-party Oracle Free container storing photo metadata and image BLOBs; reached by the application over the Compose network. | + +The Maven build contains one application module. Docker Compose defines the application and database containers; the database is an image-based dependency, not a source-built service. + +## API Endpoints Inventory + +| Service | Method | Path | Request Type | Response Type | +|---|---|---|---|---| +| Photo Album Java — `HomeController` | GET | `/` | No body; optional MVC `Model` populated by the controller | HTTP 200, rendered `index` HTML view with photo data; on retrieval error, renders the view with an empty list | +| Photo Album Java — `HomeController` | POST | `/upload` | Multipart form field `files` containing one or more uploaded files | JSON map containing `success`, `uploadedPhotos`, and `failedUploads`; HTTP 400 for no files, HTTP 200 for processed uploads, including partial failures; HTTP 401 when unauthenticated | +| Photo Album Java — `DetailController` | GET | `/detail/{id}` | Path parameter `id` | HTTP 200, rendered `detail` HTML view; redirects to `/` when the photo is absent or an error occurs | +| Photo Album Java — `DetailController` | POST | `/detail/{id}/delete` | Path parameter `id`; no body | Redirect to `/` with a flash success/error message; HTTP 401 when unauthenticated | +| Photo Album Java — `PhotoFileController` | GET | `/photo/{id}` | Path parameter `id` | HTTP 200 with image bytes and the stored media type; HTTP 404 if unavailable; HTTP 500 on an unexpected error | + +No explicit API versioning scheme was found. The routes are implemented with Spring MVC controllers; `/upload` is the only route returning a JSON response rather than an HTML view or binary content. + +## Management & Observability Endpoints + +| Service | Endpoint | Custom Metrics (if any) | +|---|---|---| +| Photo Album Java | No Actuator, health, metrics, or management endpoint was found. | None found | +| Oracle Database | Container health check is configured in Docker Compose; no HTTP management endpoint is exposed by this application. | None found | + +## DTOs & Contracts + +- `Photo` is the mutable JPA entity and is also added to MVC models for the gallery and detail views. Its persistence structure is documented separately in `data-architecture.md`. +- `UploadResult` is a mutable service-layer result object used to communicate per-file upload success or failure to the controller; it is not directly serialized as the HTTP response. +- The upload response is assembled from Java `Map` instances rather than a dedicated response DTO. Each successful item contains photo metadata, and each failed item contains a filename and error message. +- No dedicated request DTO is defined; upload input is Spring's `MultipartFile` binding. The `id` values are path parameters. +- No immutable DTOs were found. JSON responses use Spring Boot/Jackson serialization, while gallery and detail responses use Thymeleaf templates. +- No OpenAPI/Swagger specification, protobuf schema, or GraphQL schema was found. + +## Communication Patterns + +- **Synchronous:** The browser calls the Spring MVC application over HTTP. Controllers call `PhotoService` directly; `PhotoServiceImpl` calls `PhotoRepository` via Spring Data JPA/JDBC to Oracle. Photo metadata and binary content are stored in the database. +- **Asynchronous:** No message broker, event-driven communication, or asynchronous service calls were found. +- **Resilience:** No retry, circuit-breaker, timeout, or bulkhead policy was found. Errors are handled locally in some controller/service paths (including upload result errors, redirects, and HTTP 404/500 responses); no downstream-service fallback policy exists. +- **Discovery and gateway:** There is no service registry, client-side load balancing, or API gateway. Compose's internal `oracle-db` hostname is used for database connectivity. +- **Startup dependency:** Compose starts the application after the Oracle container reports healthy. This affects whether the application can start and access the database; operational probe and wait details are maintained in `configuration-inventory.md`. +- **Security:** Spring Security configures stateless HTTP Basic authentication for `POST /upload` and `POST /detail/*/delete`; other routes are permitted anonymously. There is no application-level role check beyond requiring authentication on those state-changing routes. CSRF is disabled. No TLS/HTTPS configuration was found, so transport encryption is not configured in the application. + +## Service Technology Matrix + +| Service | Web | Data Access | Discovery | Gateway | Actuator | Cache | Metrics | +|---|---|---|---|---|---|---|---| +| Photo Album Java | Spring MVC, Thymeleaf | Spring Data JPA | None | None | None found | None found | None found | +| Oracle Database | None | Oracle Database | None | None | Compose health check only | N/A | None found | + +## Service Communication Sequence + +The upload path illustrates the main state-changing request flow. Authentication is enforced before the controller receives the request; successful uploads are saved through the local service/repository chain. + +```mermaid +sequenceDiagram + participant Client as "Browser" + participant Security as "Spring Security" + participant Controller as "HomeController" + participant Service as "PhotoService" + participant Repository as "PhotoRepository" + participant DB as "Oracle Database" + + Client->>Security: POST /upload with multipart files and HTTP Basic + alt Authenticated + Security->>Controller: Forward upload request + loop Each uploaded file + Controller->>Service: uploadPhoto(file) + Service->>Service: Check file type and size; read image metadata + Service->>Repository: Save Photo and image bytes + Repository->>DB: Synchronous database write + DB-->>Repository: Persisted Photo + Repository-->>Service: Photo with ID + Service-->>Controller: UploadResult + Controller->>Service: getPhotoById(photoId) + Service->>Repository: findById(photoId) + Repository->>DB: Synchronous database read + DB-->>Repository: Photo + Repository-->>Service: Optional Photo + Service-->>Controller: Optional Photo + end + Controller-->>Client: 200 JSON with uploadedPhotos and failedUploads + else Unauthenticated + Security-->>Client: 401 Unauthorized + end +``` diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/architecture-diagram.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/architecture-diagram.md new file mode 100644 index 000000000..4d9a02f71 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/architecture-diagram.md @@ -0,0 +1,117 @@ +# Architecture Diagram + +Photo Album is a server-rendered Spring Boot application for browsing, uploading, serving, and deleting photos. It persists photo data and metadata in Oracle Database and can be run with the database using Docker Compose. + +## Application Architecture + +```mermaid +flowchart TD + subgraph ClientA["Client Layer"] + BrowserA["Web Browser"] + end + subgraph AppA["Application Layer - Spring Boot 2.7.18"] + WebA["Spring MVC Controllers"] + ViewA["Thymeleaf Views"] + SecurityA["Spring Security HTTP Basic"] + ServiceA["Photo Service"] + end + subgraph DataA["Data Layer"] + JpaA["Spring Data JPA and Hibernate"] + OracleA[("Oracle Database Free 23ai")] + end + subgraph RuntimeA["Container Runtime"] + ComposeA["Docker Compose"] + end + + BrowserA -->|"HTTP gallery and photo requests"| WebA + WebA -->|"renders pages"| ViewA + ViewA -->|"HTML responses"| BrowserA + BrowserA -->|"upload and delete requests"| SecurityA + SecurityA -->|"authorizes state changes"| WebA + WebA -->|"photo operations"| ServiceA + ServiceA -->|"CRUD and BLOB access"| JpaA + JpaA -->|"Oracle SQL"| OracleA + ComposeA -->|"runs application and database containers"| AppA + ComposeA -->|"runs database container"| OracleA +``` + +### Technology Stack Summary + +| Layer | Technology | Version | Purpose | +|---|---|---|---| +| Application runtime | Java | 8 | Runs the server-side application | +| Application framework | Spring Boot | 2.7.18 | Application configuration and runtime | +| Presentation | Spring MVC and Thymeleaf | Spring Boot managed | Handles HTTP requests and renders server-side HTML | +| Security | Spring Security | Spring Boot managed | HTTP Basic authentication for photo upload and deletion | +| Data access | Spring Data JPA and Hibernate | Spring Boot managed | Persists photo entities and performs database queries | +| Database driver | Oracle JDBC ojdbc8 | Maven-managed | Connects the application to Oracle Database | +| Database | Oracle Database Free | 23ai container image | Stores photo metadata and image BLOBs | +| Container orchestration | Docker Compose | Not specified | Runs the application and Oracle database containers | + +### Data Storage & External Services + +Oracle Database is the application's only identified data store. Spring Data JPA and Hibernate access Oracle through the Oracle JDBC driver; photo metadata and image bytes are stored together, with image bytes held as BLOB data. Docker Compose provisions the database alongside the application. No cache, message broker, or external API integration was identified. + +### Key Architectural Decisions + +- Uses Spring MVC with Thymeleaf for server-rendered gallery and detail pages, with a JSON response for uploads. +- Separates photo operations behind a service interface and Spring Data JPA repository. +- Stores uploaded images in Oracle rather than on the application filesystem; Spring Security requires HTTP Basic authentication for uploads and deletion. + +## Component Relationships + +```mermaid +flowchart LR + subgraph PresentationC["Presentation"] + HomeC["HomeController"] + DetailC["DetailController"] + FileC["PhotoFileController"] + ViewsC["Thymeleaf Views"] + end + subgraph BusinessC["Business Logic"] + ServiceApiC["PhotoService"] + ServiceImplC["PhotoServiceImpl"] + end + subgraph DataC["Data Access"] + RepoC["PhotoRepository"] + EntityC["Photo Entity"] + DatabaseC[("Oracle Database")] + end + subgraph InfraC["Infrastructure"] + SecurityConfigC["SecurityConfig"] + SecurityFilterC["Spring Security Filter Chain"] + end + subgraph ModelsC["Models"] + UploadC["UploadResult"] + end + + HomeC -->|"gallery and upload operations"| ServiceApiC + DetailC -->|"detail, navigation, and delete operations"| ServiceApiC + FileC -->|"retrieves image data"| ServiceApiC + ServiceApiC -->|"implemented by"| ServiceImplC + ServiceImplC -->|"queries and persists"| RepoC + RepoC -->|"maps"| EntityC + RepoC -->|"executes database operations"| DatabaseC + HomeC -->|"returns upload status"| UploadC + HomeC -->|"selects gallery template"| ViewsC + DetailC -->|"selects detail template"| ViewsC + SecurityConfigC -->|"configures"| SecurityFilterC + SecurityFilterC -.->|"protects upload and delete requests"| HomeC + SecurityFilterC -.->|"protects delete requests"| DetailC +``` + +### Component Inventory + +| Component | Layer | Type | Responsibility | +|---|---|---|---| +| HomeController | Presentation | Spring MVC Controller | Displays the gallery and handles multi-file upload requests | +| DetailController | Presentation | Spring MVC Controller | Displays a photo, provides previous and next navigation, and handles deletion | +| PhotoFileController | Presentation | Spring MVC Controller | Serves image bytes from the database with the stored media type | +| Thymeleaf Views | Presentation | Server-rendered templates | Render the gallery and photo detail pages | +| PhotoService | Business Logic | Service interface | Defines photo retrieval, upload, navigation, and deletion operations | +| PhotoServiceImpl | Business Logic | Spring service | Validates uploaded images and coordinates photo persistence and retrieval | +| PhotoRepository | Data Access | Spring Data JPA repository | Performs entity CRUD and photo-specific Oracle queries | +| Photo | Data Access | JPA entity | Maps photo metadata and image BLOB data to the database | +| SecurityConfig | Infrastructure | Spring configuration | Defines password encoding, in-memory admin credentials, and HTTP authorization | +| Spring Security Filter Chain | Infrastructure | Security filters | Enforces HTTP Basic authentication for upload and delete endpoints | +| UploadResult | Models | Response model | Carries upload success, photo identifier, filename, and error information | diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/assessment-overview.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/assessment-overview.md new file mode 100644 index 000000000..496a9ddfa --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/assessment-overview.md @@ -0,0 +1,42 @@ +# 📊 Project Assessment Overview + +> This documentation provides a comprehensive assessment of the project, generated by the AI-powered modernization assessment tool. Use this page as your starting point to navigate through all assessment artifacts. + +--- + +## Quick Start Guide + +Follow this recommended reading order for the most effective understanding: + +1. **[Architecture Diagram](architecture-diagram.md)** — Understand the big picture: system layers and component relationships +2. **[Dependency Map](dependency-map.md)** — Know what the project depends on and where the risks are +3. **[API & Service Contracts](api-service-contracts.md)** — See how services communicate and what contracts they expose +4. **[Data Architecture](data-architecture.md)** — Explore data models, storage, and data flow patterns +5. **[Configuration Inventory](configuration-inventory.md)** — Review how the application is configured across environments +6. **[Business Workflows](business-workflows.md)** — Trace end-to-end business processes and domain logic + +--- + +## Documentation Structure + +### 🏛️ Architecture & Dependencies + +| Document | Description | +|----------|-------------| +| [Architecture Diagram](architecture-diagram.md) | High-level application architecture and detailed component relationship diagrams with Mermaid visualizations | +| [Dependency Map](dependency-map.md) | External dependencies grouped by functional category — frameworks, databases, messaging, security, and more | + +### 🔌 APIs & Data + +| Document | Description | +|----------|-------------| +| [API & Service Contracts](api-service-contracts.md) | Service inventory, API endpoints, communication patterns (sync/async), DTOs, and resilience policies | +| [Data Architecture](data-architecture.md) | Database configuration, entity models, data ownership boundaries, repository interfaces, and caching strategies | + +### ⚙️ Configuration & Business Logic + +| Document | Description | +|----------|-------------| +| [Configuration Inventory](configuration-inventory.md) | Configuration sources, build/runtime profiles, externalized properties, secrets management, and feature flags | +| [Business Workflows](business-workflows.md) | Business processes end-to-end, domain entities, business rules, cross-service data flows, and decision logic | + diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/business-workflows.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/business-workflows.md new file mode 100644 index 000000000..53a3d2528 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/business-workflows.md @@ -0,0 +1,82 @@ +# Core Business Workflows + +Photo Album lets users upload, browse, inspect, navigate, and remove photos from a shared gallery. Each accepted photo and its image data are kept together as a persistent gallery item. + +## Domain Entities + +| Entity | Service / Bounded Context | Description | Key Relationships | +|---|---|---|---| +| Photo | Photo Album / Gallery Management | A user-contributed image managed as a gallery item, with information used to display and navigate the collection. | Each photo is independently addressable in the gallery and is the unit of upload, viewing, navigation, and deletion. | +| UploadResult | Photo Album / Gallery Management | The outcome of processing an individual uploaded file, including its success or failure and any associated photo identifier. | Produced for each file submitted to the upload workflow; a successful result identifies the persisted Photo. | + +## Service-to-Domain Mapping + +The application is a single deployable Spring MVC application, not a set of independently deployable business services. Its presentation, business operations, and persistence components share the Photo Album bounded context. + +| Service | Domain Context | Owned Entities | External Dependencies | +|---|---|---|---| +| HomeController and DetailController | Gallery presentation and workflows | None; coordinates gallery operations | PhotoService | +| PhotoFileController | Photo delivery | None; retrieves image data for display | PhotoService | +| PhotoServiceImpl | Gallery management | Photo lifecycle and UploadResult outcomes | PhotoRepository; Oracle database through JPA | +| PhotoRepository | Gallery persistence | Persists and queries Photo records | Oracle database | + +All photo records, including their image data, are owned by this application and stored in Oracle. There are no cross-service APIs, events, or shared cross-context identifiers. + +## Primary Workflows + +### Workflow 1: Upload Photos + +An authenticated user submits one or more files through the upload interface to `POST /upload`. The controller processes each file independently through PhotoService. For each file, the service checks the supported MIME type, configured per-file size limit, non-empty content, and image pixel limit when dimensions can be read. Invalid files receive an individual failure result; accepted data is stored as a Photo in Oracle and returned with its identifier. The controller gathers successful photo summaries and per-file failures, allowing partial success when a batch contains both accepted and rejected files. + +### Workflow 2: Browse and View a Photo + +The gallery page at `GET /` requests photos ordered newest-first and renders each photo with its metadata. Selecting an item opens `GET /detail/{id}`; the controller retrieves that photo and asks the service for the adjacent older and newer photos by upload time. The image is served separately from `GET /photo/{id}` using the Photo's stored image data. If a photo cannot be found, the detail and image handlers return the user to the gallery or return not found, respectively. + +### Workflow 3: Delete a Photo + +An authenticated user submits the delete form to `POST /detail/{id}/delete`. PhotoService looks up the photo; if present, it deletes that record (including the database-stored image) and the controller reports success. If no photo exists, the controller reports that it was not found. The user is redirected to the gallery. + +## Cross-Service Data Flows + +There are no cross-service composition or aggregation flows: all business operations run within one application. The PhotoService coordinates with the PhotoRepository, which reads or writes the Photo and its image data in Oracle. Gallery, detail, and image responses are assembled from this same source of truth; no remote-service fallback path is implemented. + +## Business Workflow Sequence + +The upload workflow is the primary business process. Each submitted file is handled independently, so a failed validation or save for one file does not prevent the controller from processing the remaining files. + +```mermaid +sequenceDiagram + participant User + participant Gallery as "Gallery Controller" + participant PhotoService as "Photo Service" + participant Photo as "Photo" + participant Repository as "Photo Repository" + participant Oracle as "Oracle Database" + + User->>Gallery: Submit photo files to POST /upload + Gallery->>PhotoService: Process each file + PhotoService->>PhotoService: Check file type, size, content, and image dimensions + alt File passes upload rules + PhotoService->>Photo: Create gallery item with image and metadata + PhotoService->>Repository: Save photo + Repository->>Oracle: Persist photo record and image data + Oracle-->>Repository: Saved photo + Repository-->>PhotoService: Photo with identifier + PhotoService-->>Gallery: Successful upload result + else File fails a rule or cannot be saved + PhotoService-->>Gallery: Failed upload result with reason + end + Gallery-->>User: Report uploaded and failed files +``` + +## Business Rules & Decision Logic + +- **Supported file types:** Upload accepts MIME types configured as JPEG, PNG, GIF, or WebP. +- **Per-file size and content:** Each file must be non-empty and no larger than the configured 10 MiB limit. The HTTP multipart request also has a configured maximum request size. +- **Image dimensions:** When image dimensions can be read, images exceeding 40 million pixels are rejected. If dimensions cannot be extracted, upload continues without dimension metadata. +- **Batch outcomes:** Each file produces an independent outcome. The response includes successful photo summaries and individual failures; its overall success flag is true when at least one uploaded photo is included. +- **Gallery ordering and navigation:** Gallery entries are ordered newest-first. Detail navigation finds older and newer items using upload timestamps. +- **Photo lifecycle:** A successful upload creates a persistent Photo; deletion removes it from the gallery and database. No additional business lifecycle states are modeled. +- **Authorization:** Gallery and photo reads are public. Upload and delete POST operations require HTTP Basic authentication. +- **Persistence boundary:** PhotoService operations use transaction management for database changes. There are no distributed transactions, event-driven side effects, or compensating workflows. +- **Failure handling:** Missing photos yield not-found behavior for image retrieval and a not-found message for deletion. Upload validation and persistence failures are represented as per-file errors; no external-service fallback applies. diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/configuration-inventory.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/configuration-inventory.md new file mode 100644 index 000000000..cb59ba4bc --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/configuration-inventory.md @@ -0,0 +1,135 @@ +# Configuration & Externalized Settings Inventory + +The project uses Spring properties, a Docker Compose environment, and a git-ignored `.env` file populated from an example for database and administrator credentials. It has separate Docker and test runtime profiles; no remote configuration service or secret manager is configured. + +## Configuration Sources + +| Source | Type | Path/Location | Notes | +|---|---|---|---| +| Spring application configuration | Properties | `src/main/resources/application.properties` | Base settings, including Oracle connection placeholders and application defaults. | +| Spring Docker profile configuration | Properties | `src/main/resources/application-docker.properties` | Overrides logging levels; also repeats several base settings. | +| Spring test profile configuration | Properties | `src/test/resources/application-test.properties` | H2 in-memory database, test-only credentials, and test upload path. | +| Docker Compose | YAML | `docker-compose.yml` | Sets container environment variables, port mappings, persistent volume, network, health check, and database dependency. | +| Docker image build/runtime | Dockerfile | `Dockerfile` | Maven/Java image versions, exposed port, and JVM options. | +| Local deployment environment | Environment file template | `.env.example` (copied to git-ignored `.env`) | Supplies Oracle administrator, application schema, and application administrator credentials. Actual `.env` values are not inventoried. | +| Oracle initialization | SQL/shell | `oracle-init/01-create-user.sql`, `oracle-init/create-user.sh`, `oracle-init/02-verify-user.sql`, `oracle-init/healthcheck.sql` | Initialization scripts; credential-bearing values are passed through environment variables rather than embedded in the SQL source. | +| Maven project configuration | XML | `pom.xml` | Declares Spring Boot parent, Java version, dependencies, and packaging plugin. | + +No Spring Cloud Config repository, Azure/AWS configuration service, Vault, or dedicated secret store reference was found. GitHub Actions repository secrets are used by repository automation workflows, not by the application's runtime configuration. + +## Build Profiles + +| Profile | Activation | Purpose | Key Dependencies/Plugins | +|---|---|---|---| +| Default Maven build | Automatic; no Maven profiles are declared in `pom.xml` | Compiles and packages the executable application JAR. | Spring Boot Maven plugin; application dependencies are declared in the main dependency set. | +| Docker build | Docker build invocation; `Dockerfile` runs `mvn clean package -DskipTests` | Builds the application in a Maven builder image before copying the JAR into the runtime image. | `maven:3.9.6-eclipse-temurin-8`; no profile-specific dependencies. | + +## Runtime Profiles + +| Profile | Activation Method | Config Files | Key Overrides | +|---|---|---|---| +| Default | Spring default when no profile is active | `src/main/resources/application.properties` | Oracle connection defaults and base logging settings. | +| `docker` | `SPRING_PROFILES_ACTIVE=docker` in Compose | `application.properties`, `application-docker.properties` | Compose-provided Oracle URL/user/password; application log level `INFO`, web log level `WARN`, Hibernate SQL log level `DEBUG`. | +| `test` | `@ActiveProfiles("test")` on `PhotoAlbumApplicationTests` | `application.properties`, `src/test/resources/application-test.properties` | H2 in-memory database; test-only admin credentials and upload path; SQL display disabled. | + +Spring combines the active profile with base configuration. Compose activates only `docker`; the test class activates only `test`. No profile-specific config for production or profile combinations was found. + +## Properties Inventory + +### Application + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `server.port` | `8080` | default, docker | `application.properties`; repeated in `application-docker.properties` | +| `server.servlet.encoding.charset` | `UTF-8` | default, docker | Base and Docker properties | +| `server.servlet.encoding.enabled` | `true` | default, docker | Base and Docker properties | +| `server.servlet.encoding.force` | `true` | default, docker | Base and Docker properties | +| `spring.servlet.multipart.max-file-size` | `10MB` | default, docker | Base and Docker properties | +| `spring.servlet.multipart.max-request-size` | `50MB` | default, docker | Base and Docker properties | +| `app.file-upload.max-file-size-bytes` | `10485760` (10 MiB) | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.allowed-mime-types` | `image/jpeg,image/png,image/gif,image/webp` | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.max-files-per-upload` | `10` | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.upload-path` | Not set in base/Docker configuration | test | `application-test.properties`: `target/test-uploads` | +| `app.admin.username` | `admin` via `@Value` default; Compose can override with `APP_ADMIN_USERNAME` | default, docker | `SecurityConfig`; Compose variable defaults to `admin`; test profile sets `admin` | +| `app.admin.password` | Required in application configuration; no source-code default | default, docker | `SecurityConfig`; Compose requires `APP_ADMIN_PASSWORD`; test profile uses a test-only value | + +### Database + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `spring.datasource.url` | `jdbc:oracle:thin:@oracle-db:1521/FREEPDB1`, overridden by `SPRING_DATASOURCE_URL` | default, docker | Base and Docker properties; Compose supplies the container hostname URL | +| `spring.datasource.username` | `photoalbum`, overridden by `SPRING_DATASOURCE_USERNAME` | default, docker | Base and Docker properties; Compose uses `APP_USER` (default `photoalbum`) | +| `spring.datasource.password` | Required from `SPRING_DATASOURCE_PASSWORD` | default, docker | Base and Docker properties; Compose maps `APP_USER_PASSWORD` to the Spring variable | +| `spring.datasource.driver-class-name` | `oracle.jdbc.OracleDriver` | default, docker | Base and Docker properties | +| `spring.jpa.database-platform` | `org.hibernate.dialect.OracleDialect` | default, docker | Base and Docker properties | +| `spring.jpa.hibernate.ddl-auto` | `create` | default, docker | Base and Docker properties | +| `spring.jpa.show-sql` | `true` | default, docker | Base and Docker properties | +| `spring.jpa.properties.hibernate.format_sql` | `true` | default, docker | Base and Docker properties | +| `spring.datasource.url` | `jdbc:h2:mem:testdb` | test | `application-test.properties` | +| `spring.datasource.driver-class-name` | `org.h2.Driver` | test | `application-test.properties` | +| `spring.datasource.username` | `sa` | test | `application-test.properties` | +| `spring.datasource.password` | Empty | test | `application-test.properties` | +| `spring.jpa.database-platform` | `org.hibernate.dialect.H2Dialect` | test | `application-test.properties` | +| `spring.jpa.hibernate.ddl-auto` | `create-drop` | test | `application-test.properties` | +| `spring.jpa.show-sql` | `false` | test | `application-test.properties` | + +### Logging + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `logging.level.com.photoalbum` | `DEBUG` | default, test; `INFO` in docker | Base, test, and Docker properties | +| `logging.level.org.springframework.web` | `DEBUG` | default; `WARN` in docker | Base and Docker properties | +| `logging.level.org.hibernate.SQL` | Not set in base; `DEBUG` in docker | docker | `application-docker.properties` | + +## Startup Parameters & Resource Requirements + +| Service | JVM/Runtime Options | Memory | Instance Count | +|---|---|---|---| +| `photoalbum-java-app` | `JAVA_OPTS="-Xmx512m -Xms256m"`; Compose sets `SPRING_PROFILES_ACTIVE=docker` and database/admin environment overrides | JVM heap initial 256 MiB, maximum 512 MiB; container memory limit not specified | One Compose service/container declared; scaling not configured | +| `oracle-db` | Oracle container environment variables; no JVM options specified | Not specified | One Compose service/container declared; scaling not configured | + +No CPU allocation, container memory limit, or cloud resource sizing configuration was found. + +## Startup Dependency Chain + +1. `oracle-db` starts first; Compose health check runs `healthcheck.sh` every 30 seconds, with a 10-second timeout, 15 retries, and a 180-second start period. +2. `photoalbum-java-app` waits for `oracle-db` to be healthy through Compose `depends_on: condition: service_healthy`; it then starts with the `docker` Spring profile. + +No application-container health check or explicit Spring database connection retry/startup timeout was found. + +## Secrets & Sensitive Configuration + +| Secret Reference | Type | Storage (masked) | +|---|---|---| +| `ORACLE_PASSWORD` | Oracle administrator password | Local git-ignored `.env`, passed to Oracle container; `[MASKED]` | +| `APP_USER_PASSWORD` | Application database schema password | Local git-ignored `.env`, passed to Oracle and mapped to `SPRING_DATASOURCE_PASSWORD`; `[MASKED]` | +| `APP_ADMIN_PASSWORD` | Application administrator password | Local git-ignored `.env`, passed as an application environment variable; `[MASKED]` | +| `spring.datasource.password` / `SPRING_DATASOURCE_PASSWORD` | Spring datasource credential | Environment-variable reference; required, `[MASKED]` | +| Test `spring.datasource.password` | H2 test credential | Empty test-only value | +| Test `app.admin.password` | Test administrator credential | `application-test.properties`; test-only value, `[MASKED]` | + +### Secrets Provisioning Workflow + +Copy `.env.example` to `.env`, replace all placeholder values with strong unique credentials, and run Docker Compose. Compose requires `ORACLE_PASSWORD`, `APP_USER_PASSWORD`, and `APP_ADMIN_PASSWORD`; it forwards the Oracle administrator and schema credentials to the database container and maps the schema/admin credentials into the application container. `.env` is documented as git-ignored and must not be committed. No managed identity, service principal, RBAC setup, encrypted property mechanism, or external secret manager is configured. Test credentials are isolated in test resources. + +## Feature Flags + +No feature-flag framework, application feature toggle, conditional Spring configuration, or rollout flag was found. + +| Flag Name | Default | Controlled By | +|---|---|---| +| None identified | N/A | N/A | + +## Framework & Runtime Versions + +| Component | Version | Source | +|---|---|---| +| Spring Boot | `2.7.18` | `pom.xml` parent | +| Java source/target | `8` | `pom.xml` | +| Maven | `3.9.6` in Docker build image | Dockerfile base image | +| Java build runtime | Eclipse Temurin 8 | `maven:3.9.6-eclipse-temurin-8` Docker image | +| Java application runtime | Eclipse Temurin 8 JRE | `eclipse-temurin:8-jre` Docker image | +| Oracle JDBC driver | `ojdbc8`; resolved version managed by Spring Boot dependency management | `pom.xml`; no explicit version declared | +| H2 | Version managed by Spring Boot dependency management | `pom.xml`, test scope | +| Commons IO | `2.11.0` | `pom.xml` | +| Oracle Database Free image | `latest` tag (version not pinned) | `docker-compose.yml` | diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/data-architecture.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/data-architecture.md new file mode 100644 index 000000000..b92000ba3 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/data-architecture.md @@ -0,0 +1,70 @@ +# Data Architecture & Persistence Layer + +The application has one JPA entity, `Photo`, persisted in a relational database through Hibernate. Oracle is configured for the normal and Docker profiles, while tests use an in-memory H2 database. + +## Database Configuration + +| Service/Module | DB Type | Profile | Driver | Connection | Migration Tool | +|---|---|---|---|---|---| +| Photo Album | Oracle | Default (no explicit profile) | Oracle JDBC (`ojdbc8`) | JDBC URL defaults to `jdbc:oracle:thin:@oracle-db:1521/FREEPDB1`; URL and credentials can be supplied through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured. | None. Hibernate creates the schema at startup; no versioned migrations or seed scripts were found. | +| Photo Album | Oracle | `docker` | Oracle JDBC (`ojdbc8`) | Same Oracle service URL by default; Docker supplies the URL and credentials through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured. | None. Hibernate creates the schema at startup. | +| Photo Album | H2 (in-memory) | `test` | H2 (`org.h2.Driver`, test-scoped dependency) | `jdbc:h2:mem:testdb` | None. Hibernate creates the schema for tests and drops it when the test context closes. | + +The database and schema-generation settings are defined in the application property files; see `configuration-inventory.md` for the property-level inventory. No database seed data was found. + +## Data Ownership per Service + +| Service | Tables Owned | ORM Framework | Caching | Notes | +|---|---|---|---|---| +| Photo Album | `PHOTOS` (`Photo`) | Spring Data JPA / Hibernate | None found | Single application-owned table; photo bytes are stored in a BLOB. | + +## Entity Model + +`Photo` maps to the `photos` table and owns its upload metadata and image bytes. The table has an index on `uploaded_at`. Its identifier is a UUID string generated in Java. There are no entity relationships or join tables. `UploadResult` is a service response model, not a persisted entity. + +Source: `src/main/java/com/photoalbum/model/Photo.java`. + +The service implementation applies transaction boundaries at class level, with read-only transactions for retrieval methods: `src/main/java/com/photoalbum/service/impl/PhotoServiceImpl.java`. The model's `photo_data` mapping is nullable even though the normal upload path supplies the photo bytes. + +```mermaid +erDiagram + Photo { + string Id PK + string OriginalFileName + bytes PhotoData "nullable BLOB" + string StoredFileName + string FilePath + long FileSize + string MimeType + datetime UploadedAt + int Width + int Height + } +``` + +## Key Repository Methods + +| Service | Repository | Notable Methods | Purpose | +|---|---|---|---| +| Photo Album | `PhotoRepository` — `src/main/java/com/photoalbum/repository/PhotoRepository.java`, entity `Photo` | `findAllOrderByUploadedAtDesc(): List` | Native query returns photos newest first. | +| Photo Album | `PhotoRepository` | `findPhotosUploadedBefore(LocalDateTime): List` | Native Oracle query selects up to ten older photos for navigation. | +| Photo Album | `PhotoRepository` | `findPhotosUploadedAfter(LocalDateTime): List` | Native Oracle query selects newer photos for navigation. | +| Photo Album | `PhotoRepository` | `findPhotosByUploadMonth(String, String): List` | Native Oracle query filters by year and month. | +| Photo Album | `PhotoRepository` | `findPhotosWithPagination(int, int): List` | Native Oracle `ROWNUM` query returns a row range. | +| Photo Album | `PhotoRepository` | `findPhotosWithStatistics(): List` | Native Oracle analytical query returns photos with ranking and running-total columns. | + +The repository extends `JpaRepository`, inheriting standard CRUD operations. The custom queries are native SQL, several of which use Oracle-specific features; no cross-service aggregation methods were found. + +## Caching Strategy + +No application cache provider, cache annotations, cache manager, JCache binding, or Hibernate second-level cache configuration was found. Data access uses repository/database queries without an identified application-level caching pattern or TTL/eviction policy. + +## Data Ownership Boundaries + +The project has one application and one logical data owner. The default and Docker profiles connect to Oracle; the test profile uses an isolated in-memory H2 database. The application reads and writes its own `PHOTOS` table through Spring Data JPA, with transaction management in the service layer. There are no other services, cross-service database access patterns, or CQRS/read-model boundaries identified. + +### Data Classification & Sensitivity + +| Entity | Sensitive Fields | Classification (PII/PHI/PCI/None) | Controls in Place | +|---|---|---|---| +| `Photo` | `original_file_name`, `photo_data` (image content may identify people) | Potential PII | No field-level encryption, data masking, or field-level access controls were found in the application configuration. Encryption at rest is not established by the project configuration. No PHI or PCI fields were identified. | diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/dependency-map.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/dependency-map.md new file mode 100644 index 000000000..f26460dfc --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/facts/dependency-map.md @@ -0,0 +1,72 @@ +# Dependency Map + +Photo Album declares 11 direct dependencies: 9 non-test dependencies and 2 test-scoped dependencies. Versions not explicitly set in `pom.xml` are managed by Spring Boot Starter Parent 2.7.18. + +## Dependencies + +```mermaid +flowchart LR + App["Photo Album"] + BootParent["Spring Boot Starter Parent 2.7.18"] + + subgraph Web["Web Frameworks"] + WebStarter["Spring Boot Web Starter v2.7.18"] + Thymeleaf["Spring Boot Thymeleaf Starter v2.7.18"] + end + subgraph Database["Database / ORM"] + Jpa["Spring Boot Data JPA Starter v2.7.18"] + Oracle["Oracle JDBC ojdbc8 v21.5.0.0"] + end + subgraph Security["Security"] + SecurityStarter["Spring Boot Security Starter v2.7.18"] + end + subgraph Utilities["Utilities"] + Validation["Spring Boot Validation Starter v2.7.18"] + Json["Spring Boot JSON Starter v2.7.18"] + CommonsIO["Apache Commons IO v2.11.0"] + DevTools["Spring Boot DevTools v2.7.18"] + end + + App -->|"web"| Web + App -->|"persistence"| Database + App -->|"security"| Security + App -->|"utilities"| Utilities + BootParent -.->|"manages versions"| WebStarter + BootParent -.->|"manages versions"| Thymeleaf + BootParent -.->|"manages versions"| Jpa + BootParent -.->|"manages version"| Oracle + BootParent -.->|"manages versions"| SecurityStarter + BootParent -.->|"manages versions"| Validation + BootParent -.->|"manages versions"| Json + BootParent -.->|"manages versions"| DevTools +``` + +### Dependency Summary + +| Category | Count | Key Libraries | Notes | +|---|---:|---|---| +| Web Frameworks | 2 | Spring Boot Web, Thymeleaf | Spring MVC application with server-rendered templates | +| Database / ORM | 2 | Spring Data JPA, Oracle JDBC | ORM and Oracle runtime driver | +| Security | 1 | Spring Boot Security | Spring-managed security stack | +| Utilities | 4 | Validation, JSON, Commons IO, DevTools | DevTools is optional; Commons IO is explicitly pinned | + +### Version & Compatibility Risks + +Spring Boot 2.7.18 is the final 2.x release and is outside the community support window; migration to a supported Spring Boot line will also require reviewing Java 8 compatibility, since newer Spring Boot generations require a newer Java baseline. The Oracle JDBC driver is selected through the Boot parent dependency management, so its effective version should be re-evaluated as part of that upgrade. + +### Notable Observations + +- The parent POM manages versions for all direct dependencies except Commons IO, which is explicitly set to 2.11.0. +- The Spring Boot Web Starter already provides JSON support; the separately declared JSON starter may duplicate that functionality. +- The Oracle JDBC driver is runtime-scoped, while H2 is confined to tests. + +## Test Dependencies + +| Framework | Version | Notes | +|---|---|---| +| Spring Boot Test Starter | 2.7.18 | Test-scope starter; its underlying test libraries are transitive and not individually declared in `pom.xml` | +| H2 Database | 2.1.214 | Test-scope in-memory database; version managed by Spring Boot 2.7.18 | + +Total test-scope dependencies: 2 + +The project has a Spring Boot test starter and an in-memory database for tests. The individual versions of transitive test libraries are not pinned in the available build file. diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.html b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.html new file mode 100644 index 000000000..3fd2eb5f7 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.html @@ -0,0 +1,1451 @@ + + + +Assessment - photo-album + + + + + +
+← Back to aggregate dashboard +

photo-album

+
+

Component Information

+
+
+
+
Component Namephoto-album
+
RepositoryPhotoAlbum-Java
+
ApplicationPhotoAlbum-Java
+
+
+
Java Version1.8
+
EffortL (total story points: 56)
+
Build ToolsMaven
+
FrameworksSpring Boot, Spring
+
+
+
+
+ +
+
+ + +
+
+

Issue Summary

+
+
+
+ + + + +

Cloud Readiness

+
6 issues
+
+
+ + + + + +

Java Upgrade

+
4 issues
+
+
+
+
Mandatory
+
Potential
+
Optional
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + + +
+
+ Clear all +
+
+

Cloud Readiness

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Issue CategoryCriticalityStory Point
Oracle database found
Potential8
Password found in configuration file
Potential3
Server port configuration found
Potential1
Restricted configurations found
Potential2
Embedded framework - Spring Security
Potential5
Detects usage of Jakarta Persistence (JPA) APIs
Potential5
+
+
+

Java Upgrade

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Issue CategoryCriticalityStory Point
Spring Boot Version Has Reached the End of OSS Support
Mandatory8
Spring Framework Version Has Reached the End of OSS Support
Mandatory8
Java Version Has Reached the End of Support
Mandatory8
Java Version is not the latest LTS
Optional8
+
+
+
+

Architecture Diagram

+

Photo Album is a server-rendered Spring Boot application for browsing, uploading, serving, and deleting photos. It persists photo data and metadata in Oracle Database and can be run with the database using Docker Compose.

+

Application Architecture

+
+flowchart TD + subgraph ClientA["Client Layer"] + BrowserA["Web Browser"] + end + subgraph AppA["Application Layer - Spring Boot 2.7.18"] + WebA["Spring MVC Controllers"] + ViewA["Thymeleaf Views"] + SecurityA["Spring Security HTTP Basic"] + ServiceA["Photo Service"] + end + subgraph DataA["Data Layer"] + JpaA["Spring Data JPA and Hibernate"] + OracleA[("Oracle Database Free 23ai")] + end + subgraph RuntimeA["Container Runtime"] + ComposeA["Docker Compose"] + end + + BrowserA -->|"HTTP gallery and photo requests"| WebA + WebA -->|"renders pages"| ViewA + ViewA -->|"HTML responses"| BrowserA + BrowserA -->|"upload and delete requests"| SecurityA + SecurityA -->|"authorizes state changes"| WebA + WebA -->|"photo operations"| ServiceA + ServiceA -->|"CRUD and BLOB access"| JpaA + JpaA -->|"Oracle SQL"| OracleA + ComposeA -->|"runs application and database containers"| AppA + ComposeA -->|"runs database container"| OracleA +
+

Technology Stack Summary

+
+ + + + + + + + + +
LayerTechnologyVersionPurpose
Application runtimeJava8Runs the server-side application
Application frameworkSpring Boot2.7.18Application configuration and runtime
PresentationSpring MVC and ThymeleafSpring Boot managedHandles HTTP requests and renders server-side HTML
SecuritySpring SecuritySpring Boot managedHTTP Basic authentication for photo upload and deletion
Data accessSpring Data JPA and HibernateSpring Boot managedPersists photo entities and performs database queries
Database driverOracle JDBC ojdbc8Maven-managedConnects the application to Oracle Database
DatabaseOracle Database Free23ai container imageStores photo metadata and image BLOBs
Container orchestrationDocker ComposeNot specifiedRuns the application and Oracle database containers
+

Data Storage & External Services

+

Oracle Database is the application's only identified data store. Spring Data JPA and Hibernate access Oracle through the Oracle JDBC driver; photo metadata and image bytes are stored together, with image bytes held as BLOB data. Docker Compose provisions the database alongside the application. No cache, message broker, or external API integration was identified.

+

Key Architectural Decisions

+
    +
  • Uses Spring MVC with Thymeleaf for server-rendered gallery and detail pages, with a JSON response for uploads.
  • +
  • Separates photo operations behind a service interface and Spring Data JPA repository.
  • +
  • Stores uploaded images in Oracle rather than on the application filesystem; Spring Security requires HTTP Basic authentication for uploads and deletion.
  • +
+

Component Relationships

+
+flowchart LR + subgraph PresentationC["Presentation"] + HomeC["HomeController"] + DetailC["DetailController"] + FileC["PhotoFileController"] + ViewsC["Thymeleaf Views"] + end + subgraph BusinessC["Business Logic"] + ServiceApiC["PhotoService"] + ServiceImplC["PhotoServiceImpl"] + end + subgraph DataC["Data Access"] + RepoC["PhotoRepository"] + EntityC["Photo Entity"] + DatabaseC[("Oracle Database")] + end + subgraph InfraC["Infrastructure"] + SecurityConfigC["SecurityConfig"] + SecurityFilterC["Spring Security Filter Chain"] + end + subgraph ModelsC["Models"] + UploadC["UploadResult"] + end + + HomeC -->|"gallery and upload operations"| ServiceApiC + DetailC -->|"detail, navigation, and delete operations"| ServiceApiC + FileC -->|"retrieves image data"| ServiceApiC + ServiceApiC -->|"implemented by"| ServiceImplC + ServiceImplC -->|"queries and persists"| RepoC + RepoC -->|"maps"| EntityC + RepoC -->|"executes database operations"| DatabaseC + HomeC -->|"returns upload status"| UploadC + HomeC -->|"selects gallery template"| ViewsC + DetailC -->|"selects detail template"| ViewsC + SecurityConfigC -->|"configures"| SecurityFilterC + SecurityFilterC -.->|"protects upload and delete requests"| HomeC + SecurityFilterC -.->|"protects delete requests"| DetailC +
+

Component Inventory

+
+ + + + + + + + + + + + +
ComponentLayerTypeResponsibility
HomeControllerPresentationSpring MVC ControllerDisplays the gallery and handles multi-file upload requests
DetailControllerPresentationSpring MVC ControllerDisplays a photo, provides previous and next navigation, and handles deletion
PhotoFileControllerPresentationSpring MVC ControllerServes image bytes from the database with the stored media type
Thymeleaf ViewsPresentationServer-rendered templatesRender the gallery and photo detail pages
PhotoServiceBusiness LogicService interfaceDefines photo retrieval, upload, navigation, and deletion operations
PhotoServiceImplBusiness LogicSpring serviceValidates uploaded images and coordinates photo persistence and retrieval
PhotoRepositoryData AccessSpring Data JPA repositoryPerforms entity CRUD and photo-specific Oracle queries
PhotoData AccessJPA entityMaps photo metadata and image BLOB data to the database
SecurityConfigInfrastructureSpring configurationDefines password encoding, in-memory admin credentials, and HTTP authorization
Spring Security Filter ChainInfrastructureSecurity filtersEnforces HTTP Basic authentication for upload and delete endpoints
UploadResultModelsResponse modelCarries upload success, photo identifier, filename, and error information
+
+
+
+

API & Service Communication Contracts

+

The application exposes five HTTP routes: server-rendered gallery/detail pages, a multipart upload endpoint with a JSON response, and an endpoint that serves photo bytes. Requests are handled synchronously within one application and synchronously persisted to the database; no inter-service API or asynchronous messaging was found.

+

Service Catalog

+
+ + + +
ServicePortCategoryPurpose
Photo Album Java (photo-album)8080API LayerSource-built Spring MVC and Thymeleaf web application providing the photo gallery, upload, detail, delete, and image-serving routes.
Oracle Database (oracle-db)1521InfrastructureThird-party Oracle Free container storing photo metadata and image BLOBs; reached by the application over the Compose network.
+

The Maven build contains one application module. Docker Compose defines the application and database containers; the database is an image-based dependency, not a source-built service.

+

API Endpoints Inventory

+
+ + + + + + +
ServiceMethodPathRequest TypeResponse Type
Photo Album Java — HomeControllerGET/No body; optional MVC Model populated by the controllerHTTP 200, rendered index HTML view with photo data; on retrieval error, renders the view with an empty list
Photo Album Java — HomeControllerPOST/uploadMultipart form field files containing one or more uploaded filesJSON map containing success, uploadedPhotos, and failedUploads; HTTP 400 for no files, HTTP 200 for processed uploads, including partial failures; HTTP 401 when unauthenticated
Photo Album Java — DetailControllerGET/detail/{id}Path parameter idHTTP 200, rendered detail HTML view; redirects to / when the photo is absent or an error occurs
Photo Album Java — DetailControllerPOST/detail/{id}/deletePath parameter id; no bodyRedirect to / with a flash success/error message; HTTP 401 when unauthenticated
Photo Album Java — PhotoFileControllerGET/photo/{id}Path parameter idHTTP 200 with image bytes and the stored media type; HTTP 404 if unavailable; HTTP 500 on an unexpected error
+

No explicit API versioning scheme was found. The routes are implemented with Spring MVC controllers; /upload is the only route returning a JSON response rather than an HTML view or binary content.

+

Management & Observability Endpoints

+
+ + + +
ServiceEndpointCustom Metrics (if any)
Photo Album JavaNo Actuator, health, metrics, or management endpoint was found.None found
Oracle DatabaseContainer health check is configured in Docker Compose; no HTTP management endpoint is exposed by this application.None found
+

DTOs & Contracts

+
    +
  • Photo is the mutable JPA entity and is also added to MVC models for the gallery and detail views. Its persistence structure is documented separately in data-architecture.md.
  • +
  • UploadResult is a mutable service-layer result object used to communicate per-file upload success or failure to the controller; it is not directly serialized as the HTTP response.
  • +
  • The upload response is assembled from Java Map instances rather than a dedicated response DTO. Each successful item contains photo metadata, and each failed item contains a filename and error message.
  • +
  • No dedicated request DTO is defined; upload input is Spring's MultipartFile binding. The id values are path parameters.
  • +
  • No immutable DTOs were found. JSON responses use Spring Boot/Jackson serialization, while gallery and detail responses use Thymeleaf templates.
  • +
  • No OpenAPI/Swagger specification, protobuf schema, or GraphQL schema was found.
  • +
+

Communication Patterns

+
    +
  • Synchronous: The browser calls the Spring MVC application over HTTP. Controllers call PhotoService directly; PhotoServiceImpl calls PhotoRepository via Spring Data JPA/JDBC to Oracle. Photo metadata and binary content are stored in the database.
  • +
  • Asynchronous: No message broker, event-driven communication, or asynchronous service calls were found.
  • +
  • Resilience: No retry, circuit-breaker, timeout, or bulkhead policy was found. Errors are handled locally in some controller/service paths (including upload result errors, redirects, and HTTP 404/500 responses); no downstream-service fallback policy exists.
  • +
  • Discovery and gateway: There is no service registry, client-side load balancing, or API gateway. Compose's internal oracle-db hostname is used for database connectivity.
  • +
  • Startup dependency: Compose starts the application after the Oracle container reports healthy. This affects whether the application can start and access the database; operational probe and wait details are maintained in configuration-inventory.md.
  • +
  • Security: Spring Security configures stateless HTTP Basic authentication for POST /upload and POST /detail/*/delete; other routes are permitted anonymously. There is no application-level role check beyond requiring authentication on those state-changing routes. CSRF is disabled. No TLS/HTTPS configuration was found, so transport encryption is not configured in the application.
  • +
+

Service Technology Matrix

+
+ + + +
ServiceWebData AccessDiscoveryGatewayActuatorCacheMetrics
Photo Album JavaSpring MVC, ThymeleafSpring Data JPANoneNoneNone foundNone foundNone found
Oracle DatabaseNoneOracle DatabaseNoneNoneCompose health check onlyN/ANone found
+

Service Communication Sequence

+

The upload path illustrates the main state-changing request flow. Authentication is enforced before the controller receives the request; successful uploads are saved through the local service/repository chain.

+
+sequenceDiagram + participant Client as "Browser" + participant Security as "Spring Security" + participant Controller as "HomeController" + participant Service as "PhotoService" + participant Repository as "PhotoRepository" + participant DB as "Oracle Database" + + Client->>Security: POST /upload with multipart files and HTTP Basic + alt Authenticated + Security->>Controller: Forward upload request + loop Each uploaded file + Controller->>Service: uploadPhoto(file) + Service->>Service: Check file type and size; read image metadata + Service->>Repository: Save Photo and image bytes + Repository->>DB: Synchronous database write + DB-->>Repository: Persisted Photo + Repository-->>Service: Photo with ID + Service-->>Controller: UploadResult + Controller->>Service: getPhotoById(photoId) + Service->>Repository: findById(photoId) + Repository->>DB: Synchronous database read + DB-->>Repository: Photo + Repository-->>Service: Optional Photo + Service-->>Controller: Optional Photo + end + Controller-->>Client: 200 JSON with uploadedPhotos and failedUploads + else Unauthenticated + Security-->>Client: 401 Unauthorized + end +
+
+
+
+

Configuration & Externalized Settings Inventory

+

The project uses Spring properties, a Docker Compose environment, and a git-ignored .env file populated from an example for database and administrator credentials. It has separate Docker and test runtime profiles; no remote configuration service or secret manager is configured.

+

Configuration Sources

+
+ + + + + + + + + +
SourceTypePath/LocationNotes
Spring application configurationPropertiessrc/main/resources/application.propertiesBase settings, including Oracle connection placeholders and application defaults.
Spring Docker profile configurationPropertiessrc/main/resources/application-docker.propertiesOverrides logging levels; also repeats several base settings.
Spring test profile configurationPropertiessrc/test/resources/application-test.propertiesH2 in-memory database, test-only credentials, and test upload path.
Docker ComposeYAMLdocker-compose.ymlSets container environment variables, port mappings, persistent volume, network, health check, and database dependency.
Docker image build/runtimeDockerfileDockerfileMaven/Java image versions, exposed port, and JVM options.
Local deployment environmentEnvironment file template.env.example (copied to git-ignored .env)Supplies Oracle administrator, application schema, and application administrator credentials. Actual .env values are not inventoried.
Oracle initializationSQL/shelloracle-init/01-create-user.sql, oracle-init/create-user.sh, oracle-init/02-verify-user.sql, oracle-init/healthcheck.sqlInitialization scripts; credential-bearing values are passed through environment variables rather than embedded in the SQL source.
Maven project configurationXMLpom.xmlDeclares Spring Boot parent, Java version, dependencies, and packaging plugin.
+

No Spring Cloud Config repository, Azure/AWS configuration service, Vault, or dedicated secret store reference was found. GitHub Actions repository secrets are used by repository automation workflows, not by the application's runtime configuration.

+

Build Profiles

+
+ + + +
ProfileActivationPurposeKey Dependencies/Plugins
Default Maven buildAutomatic; no Maven profiles are declared in pom.xmlCompiles and packages the executable application JAR.Spring Boot Maven plugin; application dependencies are declared in the main dependency set.
Docker buildDocker build invocation; Dockerfile runs mvn clean package -DskipTestsBuilds the application in a Maven builder image before copying the JAR into the runtime image.maven:3.9.6-eclipse-temurin-8; no profile-specific dependencies.
+

Runtime Profiles

+
+ + + + +
ProfileActivation MethodConfig FilesKey Overrides
DefaultSpring default when no profile is activesrc/main/resources/application.propertiesOracle connection defaults and base logging settings.
dockerSPRING_PROFILES_ACTIVE=docker in Composeapplication.properties, application-docker.propertiesCompose-provided Oracle URL/user/password; application log level INFO, web log level WARN, Hibernate SQL log level DEBUG.
test@ActiveProfiles("test") on PhotoAlbumApplicationTestsapplication.properties, src/test/resources/application-test.propertiesH2 in-memory database; test-only admin credentials and upload path; SQL display disabled.
+

Spring combines the active profile with base configuration. Compose activates only docker; the test class activates only test. No profile-specific config for production or profile combinations was found.

+

Properties Inventory

+

Application

+
+ + + + + + + + + + + + + +
Property KeyDefaultProfilesSource
server.port8080default, dockerapplication.properties; repeated in application-docker.properties
server.servlet.encoding.charsetUTF-8default, dockerBase and Docker properties
server.servlet.encoding.enabledtruedefault, dockerBase and Docker properties
server.servlet.encoding.forcetruedefault, dockerBase and Docker properties
spring.servlet.multipart.max-file-size10MBdefault, dockerBase and Docker properties
spring.servlet.multipart.max-request-size50MBdefault, dockerBase and Docker properties
app.file-upload.max-file-size-bytes10485760 (10 MiB)default, docker, testBase, Docker, and test properties; repeated in Docker properties
app.file-upload.allowed-mime-typesimage/jpeg,image/png,image/gif,image/webpdefault, docker, testBase, Docker, and test properties; repeated in Docker properties
app.file-upload.max-files-per-upload10default, docker, testBase, Docker, and test properties; repeated in Docker properties
app.file-upload.upload-pathNot set in base/Docker configurationtestapplication-test.properties: target/test-uploads
app.admin.usernameadmin via @Value default; Compose can override with APP_ADMIN_USERNAMEdefault, dockerSecurityConfig; Compose variable defaults to admin; test profile sets admin
app.admin.passwordRequired in application configuration; no source-code defaultdefault, dockerSecurityConfig; Compose requires APP_ADMIN_PASSWORD; test profile uses a test-only value
+

Database

+
+ + + + + + + + + + + + + + + + +
Property KeyDefaultProfilesSource
spring.datasource.urljdbc:oracle:thin:@oracle-db:1521/FREEPDB1, overridden by SPRING_DATASOURCE_URLdefault, dockerBase and Docker properties; Compose supplies the container hostname URL
spring.datasource.usernamephotoalbum, overridden by SPRING_DATASOURCE_USERNAMEdefault, dockerBase and Docker properties; Compose uses APP_USER (default photoalbum)
spring.datasource.passwordRequired from SPRING_DATASOURCE_PASSWORDdefault, dockerBase and Docker properties; Compose maps APP_USER_PASSWORD to the Spring variable
spring.datasource.driver-class-nameoracle.jdbc.OracleDriverdefault, dockerBase and Docker properties
spring.jpa.database-platformorg.hibernate.dialect.OracleDialectdefault, dockerBase and Docker properties
spring.jpa.hibernate.ddl-autocreatedefault, dockerBase and Docker properties
spring.jpa.show-sqltruedefault, dockerBase and Docker properties
spring.jpa.properties.hibernate.format_sqltruedefault, dockerBase and Docker properties
spring.datasource.urljdbc:h2:mem:testdbtestapplication-test.properties
spring.datasource.driver-class-nameorg.h2.Drivertestapplication-test.properties
spring.datasource.usernamesatestapplication-test.properties
spring.datasource.passwordEmptytestapplication-test.properties
spring.jpa.database-platformorg.hibernate.dialect.H2Dialecttestapplication-test.properties
spring.jpa.hibernate.ddl-autocreate-droptestapplication-test.properties
spring.jpa.show-sqlfalsetestapplication-test.properties
+

Logging

+
+ + + + +
Property KeyDefaultProfilesSource
logging.level.com.photoalbumDEBUGdefault, test; INFO in dockerBase, test, and Docker properties
logging.level.org.springframework.webDEBUGdefault; WARN in dockerBase and Docker properties
logging.level.org.hibernate.SQLNot set in base; DEBUG in dockerdockerapplication-docker.properties
+

Startup Parameters & Resource Requirements

+
+ + + +
ServiceJVM/Runtime OptionsMemoryInstance Count
photoalbum-java-appJAVA_OPTS="-Xmx512m -Xms256m"; Compose sets SPRING_PROFILES_ACTIVE=docker and database/admin environment overridesJVM heap initial 256 MiB, maximum 512 MiB; container memory limit not specifiedOne Compose service/container declared; scaling not configured
oracle-dbOracle container environment variables; no JVM options specifiedNot specifiedOne Compose service/container declared; scaling not configured
+

No CPU allocation, container memory limit, or cloud resource sizing configuration was found.

+

Startup Dependency Chain

+
    +
  1. oracle-db starts first; Compose health check runs healthcheck.sh every 30 seconds, with a 10-second timeout, 15 retries, and a 180-second start period.
  2. +
  3. photoalbum-java-app waits for oracle-db to be healthy through Compose depends_on: condition: service_healthy; it then starts with the docker Spring profile.
  4. +
+

No application-container health check or explicit Spring database connection retry/startup timeout was found.

+

Secrets & Sensitive Configuration

+
+ + + + + + + +
Secret ReferenceTypeStorage (masked)
ORACLE_PASSWORDOracle administrator passwordLocal git-ignored .env, passed to Oracle container; [MASKED]
APP_USER_PASSWORDApplication database schema passwordLocal git-ignored .env, passed to Oracle and mapped to SPRING_DATASOURCE_PASSWORD; [MASKED]
APP_ADMIN_PASSWORDApplication administrator passwordLocal git-ignored .env, passed as an application environment variable; [MASKED]
spring.datasource.password / SPRING_DATASOURCE_PASSWORDSpring datasource credentialEnvironment-variable reference; required, [MASKED]
Test spring.datasource.passwordH2 test credentialEmpty test-only value
Test app.admin.passwordTest administrator credentialapplication-test.properties; test-only value, [MASKED]
+

Secrets Provisioning Workflow

+

Copy .env.example to .env, replace all placeholder values with strong unique credentials, and run Docker Compose. Compose requires ORACLE_PASSWORD, APP_USER_PASSWORD, and APP_ADMIN_PASSWORD; it forwards the Oracle administrator and schema credentials to the database container and maps the schema/admin credentials into the application container. .env is documented as git-ignored and must not be committed. No managed identity, service principal, RBAC setup, encrypted property mechanism, or external secret manager is configured. Test credentials are isolated in test resources.

+

Feature Flags

+

No feature-flag framework, application feature toggle, conditional Spring configuration, or rollout flag was found.

+
+ + +
Flag NameDefaultControlled By
None identifiedN/AN/A
+

Framework & Runtime Versions

+
+ + + + + + + + + + +
ComponentVersionSource
Spring Boot2.7.18pom.xml parent
Java source/target8pom.xml
Maven3.9.6 in Docker build imageDockerfile base image
Java build runtimeEclipse Temurin 8maven:3.9.6-eclipse-temurin-8 Docker image
Java application runtimeEclipse Temurin 8 JREeclipse-temurin:8-jre Docker image
Oracle JDBC driverojdbc8; resolved version managed by Spring Boot dependency managementpom.xml; no explicit version declared
H2Version managed by Spring Boot dependency managementpom.xml, test scope
Commons IO2.11.0pom.xml
Oracle Database Free imagelatest tag (version not pinned)docker-compose.yml
+
+
+
+

Core Business Workflows

+

Photo Album lets users upload, browse, inspect, navigate, and remove photos from a shared gallery. Each accepted photo and its image data are kept together as a persistent gallery item.

+

Domain Entities

+
+ + + +
EntityService / Bounded ContextDescriptionKey Relationships
PhotoPhoto Album / Gallery ManagementA user-contributed image managed as a gallery item, with information used to display and navigate the collection.Each photo is independently addressable in the gallery and is the unit of upload, viewing, navigation, and deletion.
UploadResultPhoto Album / Gallery ManagementThe outcome of processing an individual uploaded file, including its success or failure and any associated photo identifier.Produced for each file submitted to the upload workflow; a successful result identifies the persisted Photo.
+

Service-to-Domain Mapping

+

The application is a single deployable Spring MVC application, not a set of independently deployable business services. Its presentation, business operations, and persistence components share the Photo Album bounded context.

+
+ + + + + +
ServiceDomain ContextOwned EntitiesExternal Dependencies
HomeController and DetailControllerGallery presentation and workflowsNone; coordinates gallery operationsPhotoService
PhotoFileControllerPhoto deliveryNone; retrieves image data for displayPhotoService
PhotoServiceImplGallery managementPhoto lifecycle and UploadResult outcomesPhotoRepository; Oracle database through JPA
PhotoRepositoryGallery persistencePersists and queries Photo recordsOracle database
+

All photo records, including their image data, are owned by this application and stored in Oracle. There are no cross-service APIs, events, or shared cross-context identifiers.

+

Primary Workflows

+

Workflow 1: Upload Photos

+

An authenticated user submits one or more files through the upload interface to POST /upload. The controller processes each file independently through PhotoService. For each file, the service checks the supported MIME type, configured per-file size limit, non-empty content, and image pixel limit when dimensions can be read. Invalid files receive an individual failure result; accepted data is stored as a Photo in Oracle and returned with its identifier. The controller gathers successful photo summaries and per-file failures, allowing partial success when a batch contains both accepted and rejected files.

+

Workflow 2: Browse and View a Photo

+

The gallery page at GET / requests photos ordered newest-first and renders each photo with its metadata. Selecting an item opens GET /detail/{id}; the controller retrieves that photo and asks the service for the adjacent older and newer photos by upload time. The image is served separately from GET /photo/{id} using the Photo's stored image data. If a photo cannot be found, the detail and image handlers return the user to the gallery or return not found, respectively.

+

Workflow 3: Delete a Photo

+

An authenticated user submits the delete form to POST /detail/{id}/delete. PhotoService looks up the photo; if present, it deletes that record (including the database-stored image) and the controller reports success. If no photo exists, the controller reports that it was not found. The user is redirected to the gallery.

+

Cross-Service Data Flows

+

There are no cross-service composition or aggregation flows: all business operations run within one application. The PhotoService coordinates with the PhotoRepository, which reads or writes the Photo and its image data in Oracle. Gallery, detail, and image responses are assembled from this same source of truth; no remote-service fallback path is implemented.

+

Business Workflow Sequence

+

The upload workflow is the primary business process. Each submitted file is handled independently, so a failed validation or save for one file does not prevent the controller from processing the remaining files.

+
+sequenceDiagram + participant User + participant Gallery as "Gallery Controller" + participant PhotoService as "Photo Service" + participant Photo as "Photo" + participant Repository as "Photo Repository" + participant Oracle as "Oracle Database" + + User->>Gallery: Submit photo files to POST /upload + Gallery->>PhotoService: Process each file + PhotoService->>PhotoService: Check file type, size, content, and image dimensions + alt File passes upload rules + PhotoService->>Photo: Create gallery item with image and metadata + PhotoService->>Repository: Save photo + Repository->>Oracle: Persist photo record and image data + Oracle-->>Repository: Saved photo + Repository-->>PhotoService: Photo with identifier + PhotoService-->>Gallery: Successful upload result + else File fails a rule or cannot be saved + PhotoService-->>Gallery: Failed upload result with reason + end + Gallery-->>User: Report uploaded and failed files +
+

Business Rules & Decision Logic

+
    +
  • Supported file types: Upload accepts MIME types configured as JPEG, PNG, GIF, or WebP.
  • +
  • Per-file size and content: Each file must be non-empty and no larger than the configured 10 MiB limit. The HTTP multipart request also has a configured maximum request size.
  • +
  • Image dimensions: When image dimensions can be read, images exceeding 40 million pixels are rejected. If dimensions cannot be extracted, upload continues without dimension metadata.
  • +
  • Batch outcomes: Each file produces an independent outcome. The response includes successful photo summaries and individual failures; its overall success flag is true when at least one uploaded photo is included.
  • +
  • Gallery ordering and navigation: Gallery entries are ordered newest-first. Detail navigation finds older and newer items using upload timestamps.
  • +
  • Photo lifecycle: A successful upload creates a persistent Photo; deletion removes it from the gallery and database. No additional business lifecycle states are modeled.
  • +
  • Authorization: Gallery and photo reads are public. Upload and delete POST operations require HTTP Basic authentication.
  • +
  • Persistence boundary: PhotoService operations use transaction management for database changes. There are no distributed transactions, event-driven side effects, or compensating workflows.
  • +
  • Failure handling: Missing photos yield not-found behavior for image retrieval and a not-found message for deletion. Upload validation and persistence failures are represented as per-file errors; no external-service fallback applies.
  • +
+
+
+
+

Dependency Map

+

Photo Album declares 11 direct dependencies: 9 non-test dependencies and 2 test-scoped dependencies. Versions not explicitly set in pom.xml are managed by Spring Boot Starter Parent 2.7.18.

+

Dependencies

+
+flowchart LR + App["Photo Album"] + BootParent["Spring Boot Starter Parent 2.7.18"] + + subgraph Web["Web Frameworks"] + WebStarter["Spring Boot Web Starter v2.7.18"] + Thymeleaf["Spring Boot Thymeleaf Starter v2.7.18"] + end + subgraph Database["Database / ORM"] + Jpa["Spring Boot Data JPA Starter v2.7.18"] + Oracle["Oracle JDBC ojdbc8 v21.5.0.0"] + end + subgraph Security["Security"] + SecurityStarter["Spring Boot Security Starter v2.7.18"] + end + subgraph Utilities["Utilities"] + Validation["Spring Boot Validation Starter v2.7.18"] + Json["Spring Boot JSON Starter v2.7.18"] + CommonsIO["Apache Commons IO v2.11.0"] + DevTools["Spring Boot DevTools v2.7.18"] + end + + App -->|"web"| Web + App -->|"persistence"| Database + App -->|"security"| Security + App -->|"utilities"| Utilities + BootParent -.->|"manages versions"| WebStarter + BootParent -.->|"manages versions"| Thymeleaf + BootParent -.->|"manages versions"| Jpa + BootParent -.->|"manages version"| Oracle + BootParent -.->|"manages versions"| SecurityStarter + BootParent -.->|"manages versions"| Validation + BootParent -.->|"manages versions"| Json + BootParent -.->|"manages versions"| DevTools +
+

Dependency Summary

+
+ + + + + +
CategoryCountKey LibrariesNotes
Web Frameworks2Spring Boot Web, ThymeleafSpring MVC application with server-rendered templates
Database / ORM2Spring Data JPA, Oracle JDBCORM and Oracle runtime driver
Security1Spring Boot SecuritySpring-managed security stack
Utilities4Validation, JSON, Commons IO, DevToolsDevTools is optional; Commons IO is explicitly pinned
+

Version & Compatibility Risks

+

Spring Boot 2.7.18 is the final 2.x release and is outside the community support window; migration to a supported Spring Boot line will also require reviewing Java 8 compatibility, since newer Spring Boot generations require a newer Java baseline. The Oracle JDBC driver is selected through the Boot parent dependency management, so its effective version should be re-evaluated as part of that upgrade.

+

Notable Observations

+
    +
  • The parent POM manages versions for all direct dependencies except Commons IO, which is explicitly set to 2.11.0.
  • +
  • The Spring Boot Web Starter already provides JSON support; the separately declared JSON starter may duplicate that functionality.
  • +
  • The Oracle JDBC driver is runtime-scoped, while H2 is confined to tests.
  • +
+

Test Dependencies

+
+ + + +
FrameworkVersionNotes
Spring Boot Test Starter2.7.18Test-scope starter; its underlying test libraries are transitive and not individually declared in pom.xml
H2 Database2.1.214Test-scope in-memory database; version managed by Spring Boot 2.7.18
+

Total test-scope dependencies: 2

+

The project has a Spring Boot test starter and an in-memory database for tests. The individual versions of transitive test libraries are not pinned in the available build file.

+
+
+
+

Data Architecture & Persistence Layer

+

The application has one JPA entity, Photo, persisted in a relational database through Hibernate. Oracle is configured for the normal and Docker profiles, while tests use an in-memory H2 database.

+

Database Configuration

+
+ + + + +
Service/ModuleDB TypeProfileDriverConnectionMigration Tool
Photo AlbumOracleDefault (no explicit profile)Oracle JDBC (ojdbc8)JDBC URL defaults to jdbc:oracle:thin:@oracle-db:1521/FREEPDB1; URL and credentials can be supplied through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured.None. Hibernate creates the schema at startup; no versioned migrations or seed scripts were found.
Photo AlbumOracledockerOracle JDBC (ojdbc8)Same Oracle service URL by default; Docker supplies the URL and credentials through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured.None. Hibernate creates the schema at startup.
Photo AlbumH2 (in-memory)testH2 (org.h2.Driver, test-scoped dependency)jdbc:h2:mem:testdbNone. Hibernate creates the schema for tests and drops it when the test context closes.
+

The database and schema-generation settings are defined in the application property files; see configuration-inventory.md for the property-level inventory. No database seed data was found.

+

Data Ownership per Service

+
+ + +
ServiceTables OwnedORM FrameworkCachingNotes
Photo AlbumPHOTOS (Photo)Spring Data JPA / HibernateNone foundSingle application-owned table; photo bytes are stored in a BLOB.
+

Entity Model

+

Photo maps to the photos table and owns its upload metadata and image bytes. The table has an index on uploaded_at. Its identifier is a UUID string generated in Java. There are no entity relationships or join tables. UploadResult is a service response model, not a persisted entity.

+

Source: src/main/java/com/photoalbum/model/Photo.java.

+

The service implementation applies transaction boundaries at class level, with read-only transactions for retrieval methods: src/main/java/com/photoalbum/service/impl/PhotoServiceImpl.java. The model's photo_data mapping is nullable even though the normal upload path supplies the photo bytes.

+
+erDiagram + Photo { + string Id PK + string OriginalFileName + bytes PhotoData "nullable BLOB" + string StoredFileName + string FilePath + long FileSize + string MimeType + datetime UploadedAt + int Width + int Height + } +
+

Key Repository Methods

+
+ + + + + + + +
ServiceRepositoryNotable MethodsPurpose
Photo AlbumPhotoRepository — src/main/java/com/photoalbum/repository/PhotoRepository.java, entity PhotofindAllOrderByUploadedAtDesc(): List<Photo>Native query returns photos newest first.
Photo AlbumPhotoRepositoryfindPhotosUploadedBefore(LocalDateTime): List<Photo>Native Oracle query selects up to ten older photos for navigation.
Photo AlbumPhotoRepositoryfindPhotosUploadedAfter(LocalDateTime): List<Photo>Native Oracle query selects newer photos for navigation.
Photo AlbumPhotoRepositoryfindPhotosByUploadMonth(String, String): List<Photo>Native Oracle query filters by year and month.
Photo AlbumPhotoRepositoryfindPhotosWithPagination(int, int): List<Photo>Native Oracle ROWNUM query returns a row range.
Photo AlbumPhotoRepositoryfindPhotosWithStatistics(): List<Object[]>Native Oracle analytical query returns photos with ranking and running-total columns.
+

The repository extends JpaRepository<Photo, String>, inheriting standard CRUD operations. The custom queries are native SQL, several of which use Oracle-specific features; no cross-service aggregation methods were found.

+

Caching Strategy

+

No application cache provider, cache annotations, cache manager, JCache binding, or Hibernate second-level cache configuration was found. Data access uses repository/database queries without an identified application-level caching pattern or TTL/eviction policy.

+

Data Ownership Boundaries

+

The project has one application and one logical data owner. The default and Docker profiles connect to Oracle; the test profile uses an isolated in-memory H2 database. The application reads and writes its own PHOTOS table through Spring Data JPA, with transaction management in the service layer. There are no other services, cross-service database access patterns, or CQRS/read-model boundaries identified.

+

Data Classification & Sensitivity

+
+ + +
EntitySensitive FieldsClassification (PII/PHI/PCI/None)Controls in Place
Photooriginal_file_name, photo_data (image content may identify people)Potential PIINo field-level encryption, data masking, or field-level access controls were found in the application configuration. Encryption at rest is not established by the project configuration. No PHI or PCI fields were identified.
+
+
+ + + +
diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.json b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.json new file mode 100644 index 000000000..0b5e96d95 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.json @@ -0,0 +1,1173 @@ +{ + "version": "1.0.0", + "producer": "Java AppCAT CLI", + "metadata": { + "analysisStartTime": "2026-11-04T09:30:52.775351Z", + "analysisEndTime": "2026-11-04T09:34:32.6470284Z", + "status": "Complete", + "privacyMode": "Protected", + "privacyModeHelpUrl": "https://aka.ms/appcat-privacy-mode", + "targetIds": [ + "azure-appservice", + "azure-aks", + "azure-container-apps" + ], + "targetDisplayNames": [ + "Azure App Service", + "Azure Kubernetes Service", + "Azure Container Apps" + ], + "capabilities": [ + "openjdk25" + ], + "os": [ + "linux", + "windows" + ], + "domains": [ + "java-upgrade", + "cloud-readiness" + ], + "mode": "full", + "minimumCveSeverity": null + }, + "summary": { + "totalProjects": 1, + "totalIssues": 10, + "totalIncidents": 31, + "totalEffort": 196, + "charts": { + "severity": { + "mandatory": 13, + "optional": 2, + "potential": 16, + "information": 0 + }, + "category": { + "auth-migration": 1, + "database-migration": 6, + "framework-upgrade": 12, + "jakarta-migration": 1, + "java-version-upgrade": 3, + "local-credential": 4, + "spring-migration": 4 + } + } + }, + "projects": [ + { + "path": ".", + "issues": 10, + "storyPoints": 196, + "properties": { + "appName": "photo-album", + "jdkVersion": "1.8", + "frameworks": [ + "Spring Boot", + "Spring" + ], + "languages": [ + "Java", + "JavaScript" + ], + "tools": [ + "Maven" + ] + }, + "incidents": [ + { + "ruleId": "auth-02000", + "incidentId": "b845d4f9-3276-4be7-a051-635b67d9259a", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1bdf6e21-f623-4f14-9339-ae1a0ba204d2", + "location": "pom.xml", + "locationKind": "File", + "line": 52, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "44b171b1-2ca1-4627-a673-8060fdce5f29", + "location": "docker-compose.yml", + "locationKind": "File", + "line": 32, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "40510a44-6c1f-49c5-9103-c5d2a17a7682", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 3, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "5f157c91-507e-4794-86f9-6799a711160c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 12, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "bc62976f-e22d-4434-9046-b0836c0e6b7c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 15, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1004341d-8548-4a06-afba-e56fdf194ba1", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 6, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "995a2b3e-4870-4e04-8d6d-a5701427008a", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "97097182-5728-42f1-b69e-5b16a66937ff", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "154a6aea-7e34-49c7-8d83-8897ac979f65", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "433cc960-b5ae-4a76-a45b-2c34d446e0a3", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "a95a682d-dad5-4275-8d6d-6d43ed4188cd", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b3814a33-4403-4442-a2f0-a119b0f6248a", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 23, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "1ac607ee-8338-440c-b110-79d433327062", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b9c9e02a-c269-4137-9d82-f8671fcb2130", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 14, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-01000", + "incidentId": "36436d2b-df81-40eb-a0fe-d3f8ede23005", + "location": "pom.xml", + "locationKind": "File", + "line": 24, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "927cfd73-3a17-4888-99f0-b00ccd7d84ce", + "location": "pom.xml", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "9b3a9911-d9d3-4190-a82b-6f28e3a0ad14", + "location": "pom.xml", + "locationKind": "File", + "line": 26, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "6c645761-fb02-4d39-a079-1389acb57081", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "8474445f-4d9f-4410-ba08-4eea0a5f407b", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "615ff34c-58b8-44ac-850a-0760d5125fe9", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "2c17f79e-3853-464c-8de3-42ffb30fb98e", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c61300ea-5005-4813-a64d-41cc57d2d16f", + "location": "pom.xml", + "locationKind": "File", + "line": 40, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "4264b153-88a3-4faf-a76f-a4c4b13bf6d1", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "3507d6d7-3397-428f-883f-aa62f6d638de", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "7cd37e0b-2862-40da-b295-7cfecdf85b33", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "69e09b4a-1b8a-4a8c-b2f6-61f53bf7a6cf", + "location": "pom.xml", + "locationKind": "File", + "line": 78, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "b1800a7a-6a4b-4b66-8e98-bbbff27bafa4", + "location": "pom.xml", + "locationKind": "File", + "line": 59, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "bcf5c6b6-36a4-45ae-ac0c-c941c264a286", + "location": "pom.xml", + "locationKind": "File", + "line": 98, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c5ff81c6-2bdf-417a-b7a5-6943bdb72930", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "jakarta-database-00002", + "incidentId": "73c943cd-017a-4dbe-965e-8128b10fdc4e", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=cloud-readiness" + ] + } + ] + } + ], + "rules": { + "auth-02000": { + "id": "auth-02000", + "description": "The application embeds a Spring Security library. Ensure that the application is configured to access the identity provider.\n\nConsider using Microsoft Entra ID as an identity provider.", + "title": "Embedded framework - Spring Security", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-app-service#identity-providers", + "title": "Spring Security-related dependencies found in the project" + }, + { + "url": "https://docs.microsoft.com/azure/active-directory/external-identities/azure-ad-account", + "title": "Azure Active Directory (Azure AD) identity provider for External Identities" + }, + { + "url": "https://docs.spring.io/spring-security/reference/index.html", + "title": "Spring Security" + }, + { + "url": "https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization", + "title": "Spring Boot API: Authorization" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=auth-migration", + "os=windows", + "os=linux" + ] + }, + "azure-database-microsoft-oracle-07000": { + "id": "azure-database-microsoft-oracle-07000", + "description": "Oracle database found. To migrate a Java application that uses an Oracle database to Azure, you can follow these recommendations:\n\n * **Migrate to Azure Database for PostgreSQL**: Azure recommends migrating Oracle databases to Azure Database for PostgreSQL Flexible Server as it provides better cost-effectiveness and performance. Create a managed PostgreSQL Flexible Server database in Azure and choose the appropriate pricing tier based on your application\u0027s requirements.\n\n * **Use migration tools**: Utilize the Azure Database Migration Service (DMS) or third-party tools to migrate your Oracle database schema and data to PostgreSQL. Consider using ora2pg or similar tools to convert Oracle-specific SQL to PostgreSQL-compatible SQL.\n\n * **Update database drivers and connection strings**: Replace Oracle JDBC drivers with PostgreSQL drivers in your Java application. Update connection strings from Oracle format (jdbc:oracle:thin:) to PostgreSQL format (jdbc:postgresql:).\n\n * **Review and convert Oracle-specific code**: Identify and convert Oracle-specific SQL functions, stored procedures, and PL/SQL code to PostgreSQL equivalents. Pay attention to data types, syntax differences, and built-in functions.\n\n * Enable **monitoring and diagnostics**: Utilize Azure Monitor to gain insights into the performance and health of your Java application and the underlying PostgreSQL database. Set up metrics, alerts, and log analytics to proactively identify and resolve issues.\n\n * Implement **security** measures: Apply security best practices to protect your Java application and the PostgreSQL database. This includes implementing authentication and authorization mechanisms with passwordless connections and leveraging Microsoft Defender for Cloud for threat detection and vulnerability assessments.\n\n * **Backup** your data: Azure Database for PostgreSQL provides automated backups by default. You can configure the retention period for backups based on your requirements. You can also enable geo-redundant backups, if needed, to enhance data durability and availability.", + "title": "Oracle database found", + "severity": "potential", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/postgresql", + "title": "Azure Database for PostgreSQL documentation" + }, + { + "url": "https://learn.microsoft.com/azure/postgresql/migrate/how-to-migrate-oracle-ora2pg", + "title": "Oracle to PostgreSQL migration guide" + }, + { + "url": "https://learn.microsoft.com/azure/dms", + "title": "Azure Database Migration Service documentation" + }, + { + "url": "https://learn.microsoft.com/azure/azure-monitor", + "title": "Azure Monitor documentation" + }, + { + "url": "https://learn.microsoft.com/azure/defender-for-cloud", + "title": "Microsoft Defender for Cloud" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=database-migration", + "database", + "oracle", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-01000": { + "id": "azure-java-version-01000", + "description": "The application is using a Java version that has reached the end of support. It is strongly recommended to plan and execute a migration strategy to upgrade your application to a supported Java version.\nSupported Java versions receive long-term support (LTS) from the Java community, including bug fixes and updates. Migrating to a supported version provides you with a stable and well-maintained platform for your application.", + "title": "Java Version Has Reached the End of Support", + "severity": "mandatory", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-02000": { + "id": "azure-java-version-02000", + "description": "The application is not using the latest LTS Java version. It is recommended to consider upgrading to the latest LTS version to take advantage of the newest language features, performance improvements, and extended support timelines.\nUpgrading to the latest LTS version ensures your application benefits from the most recent security enhancements and a longer support lifecycle.", + "title": "Java Version is not the latest LTS", + "severity": "optional", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-password-01000": { + "id": "azure-password-01000", + "description": "Using clear passwords in property files is a security risk, as they can be easily compromised if the files are accessed by unauthorized individuals. To enhance the security of your application, it is recommended to employ secure credential management practices.\n\n * **Azure Key Vault**: Utilize Azure Key Vault to securely store and manage your application\u0027s passwords and other sensitive credentials. Azure Key Vault provides a centralized and highly secure location for storing secrets, keys, and certificates.\n\n * **Passwordless connections**: You can provide an additional layer of security and convenience for accessing resources in Azure by eliminating the need for passwords. This way you can reduce the risk of password-related vulnerabilities, such as weak passwords or password theft.", + "title": "Password found in configuration file", + "severity": "potential", + "effort": 3, + "links": [ + { + "url": "https://learn.microsoft.com/azure/key-vault", + "title": "Azure Key Vault documentation" + }, + { + "url": "https://learn.microsoft.com/azure/developer/intro/passwordless-overview", + "title": "Passwordless connections for Azure services" + }, + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#inventory-configuration-sources-and-secrets", + "title": "Password found in configuration file" + }, + { + "url": "https://docs.microsoft.com/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-key-vault", + "title": "Read a secret from Azure Key Vault in a Spring Boot application" + }, + { + "url": "https://search.maven.org/artifact/com.azure.spring/azure-spring-boot-starter-keyvault-secrets", + "title": "Azure Spring Boot Starter for Azure Key Vault Secrets" + } + ], + "labels": [ + "source", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=local-credential", + "password", + "security", + "os=windows", + "os=linux" + ] + }, + "jakarta-database-00002": { + "id": "jakarta-database-00002", + "description": "The application depends on **Jakarta Persistence (JPA)** APIs (\u0060jakarta.persistence.*\u0060 or legacy \u0060javax.persistence.*\u0060), which are used for object-relational mapping (ORM) and database interaction in Jakarta EE or Java EE applications.\n\nWhen migrating to Azure:\n- Ensure that the database connection, JPA provider (e.g., Hibernate, EclipseLink), and dialect are compatible with your target Azure database service.\n- Recommended database services include **Azure Database for PostgreSQL**, **Azure Database for MySQL**, or **Azure SQL Database**.\n- For containerized deployments, these JPA-based applications can run on **Azure Kubernetes Service (AKS)** or **Azure App Service for Linux/Windows**.\n- If using **Spring Data JPA**, verify that connection pool settings and environment variables are properly configured for the cloud environment.\n- Consider leveraging **Azure Key Vault** for secure storage of database credentials and connection strings.", + "title": "Detects usage of Jakarta Persistence (JPA) APIs", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://jakarta.ee/specifications/persistence/", + "title": "Jakarta Persistence Specification" + }, + { + "url": "https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/data-stores-getting-started#common-database-scenarios", + "title": "Prepare to choose a data store in Azure" + } + ], + "labels": [ + "source=java", + "source=java-ee", + "target=azure-aks", + "target=azure-container-apps", + "target=azure-appservice", + "domain=cloud-readiness", + "category=jakarta-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-port-01000": { + "id": "spring-boot-to-azure-port-01000", + "description": "The application is setting the server port. To migrate a Java application that sets the server port to Azure Container Apps:\n\n * **Azure Container Apps allows you to expose port according to your Azure Container Apps resource configuration. For instance, a Spring Boot application listens to port of 8080 by default, but it can be set with server.port or environment variable SERVER_PORT as you need.", + "title": "Server port configuration found", + "severity": "potential", + "effort": 1, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#identify-any-clients-relying-on-a-non-standard-port", + "title": "Identify any clients relying on a non-standard port" + } + ], + "labels": [ + "source=springboot", + "target=azure-aks", + "target=azure-appservice", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=spring-migration", + "port", + "server port", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-restricted-config-01000": { + "id": "spring-boot-to-azure-restricted-config-01000", + "description": "The application uses restricted configurations for Azure Container Apps.\n These properties can be automatically injected into your application environment by Azure Container Apps to access managed Config Server and managed Eureka Server.\n Please remove them from your application, including configuration files, config server files, command line parameters, Java system attributes, and environment variables.\n\n If configured in **configuration files**: they will be ignored and overrided by Azure Container Apps.\n \n If configured in **Config Server files**, **command line parameters**, **Java system attribute**, **environment variable**: they need to be removed or you might experience conflicts and unexpected behavior.", + "title": "Restricted configurations found", + "severity": "potential", + "effort": 2, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-cloud-to-azure-container-apps#remove-restricted-configurations", + "title": "Migrate Spring Boot applications to Azure Container Apps - Remove restricted configurations" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-config-server?tabs=azure-cli", + "title": "Connect to a managed Config Server for Spring in Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-eureka-server?tabs=azure-cli", + "title": "Connect to a managed Eureka Server for Spring in Azure Container Apps" + } + ], + "labels": [ + "target=azure-container-apps", + "source=springboot", + "domain=cloud-readiness", + "category=spring-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-spring-boot-version-01000": { + "id": "spring-boot-to-azure-spring-boot-version-01000", + "description": "The application is using a Spring Boot version that has reached its End of OSS Support.\nWith the officially supported new versions from Spring, you can get the best experience. Here are some steps you can take to update your application to the latest version of Spring Boot:\n\n* Choose a **supported Spring Boot version**: Check out Spring Boot Support Versions and determine the most suitable supported Spring Boot version.\n\n* **Update Spring Boot version**: Update the Spring Boot version of your application. There are automated tools like Rewrite to help you with the migration.\n\n* **Address code compatibility**: Review your application\u0027s codebase for any potential compatibility issues with the target Spring Boot version. Update deprecated APIs or features, address any language or library changes, and ensure that your code follows best practices and standards.\n\n* **Test thoroughly**: Execute a comprehensive testing process to verify the compatibility and functionality of your application with the new Spring Boot version. Perform unit tests, integration tests, and system tests to validate that all components and dependencies work as expected.", + "title": "Spring Boot Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps", + "title": "Migrate Spring Boot applications to Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-microservice-get-started?tabs=azure-cli", + "title": "Launch your first Java microservice application with managed Java components in Azure Container Apps" + }, + { + "url": "https://spring.io/projects/spring-boot/#support", + "title": "Spring Boot Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-boot/wiki/Supported-Versions", + "title": "Spring Boot Support Policy" + } + ], + "labels": [ + "source=springboot", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "spring-framework-version-01000": { + "id": "spring-framework-version-01000", + "description": "Your application is using a Spring Framework version that has reached its End of OSS Support.\nUpgrading to a supported version ensures better performance, security, and compatibility with modern tools.\n 1. Pick a Supported Version: Review the Spring Framework support policy and choose an actively supported version.\n 2. Update Your Project: Change the Spring Framework version in your pom.xml or build.gradle.\n 3. Fix Compatibility Issues: Update deprecated code, replace removed features, and ensure dependencies are compatible with the new Spring Framework version.\n 4. Thoroughly Test: Run unit, integration, and end-to-end tests to make sure everything still works after the upgrade.", + "title": "Spring Framework Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://spring.io/projects/spring-framework#support", + "title": "Spring Framework Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions", + "title": "Spring Framework Support Policy" + } + ], + "labels": [ + "source=spring", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + } + }, + "rearchitect": [] +} \ No newline at end of file diff --git a/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.md b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.md new file mode 100644 index 000000000..7d289b270 --- /dev/null +++ b/.github/modernize/assessment/reports-20261104093550/repos/PhotoAlbum-Java/report.md @@ -0,0 +1,148 @@ +# photo-album + +## Summary + +| Metric | Value | +|--------|-------| +| Total Issues | 10 | +| Mandatory Blockers | 3 | +| Potential Issues | 6 | + +## Component Information + +| Property | Value | +|----------|-------| +| Language | Java, JavaScript | +| Frameworks | Spring Boot, Spring | +| Build tools | Maven | +| JDK version | 1.8 | + +## Cloud Readiness Issues + +| Issue Name | Criticality | Story Points | Occurrences | +|------------|-------------|--------------|-------------| +| Oracle database found | Potential | 8 | [6](#Oracle_database_found) | +| Password found in configuration file | Potential | 3 | [4](#Password_found_in_configuration_file) | +| Server port configuration found | Potential | 1 | [2](#Server_port_configuration_found) | +| Restricted configurations found | Potential | 2 | [2](#Restricted_configurations_found) | +| Embedded framework - Spring Security | Potential | 5 | [1](#Embedded_framework_-_Spring_Security) | +| Detects usage of Jakarta Persistence (JPA) APIs | Potential | 5 | [1](#Detects_usage_of_Jakarta_Persistence_JPA_APIs) | + +### Issue Details + +
+Oracle database found — affected files + +- `pom.xml (line 52, col 0)` +- `docker-compose.yml (line 32, col 0)` +- `src/main/resources/application-docker.properties (line 3, col 0)` +- `src/main/resources/application.properties (line 12, col 0)` +- `src/main/resources/application.properties (line 15, col 0)` +- `src/main/resources/application-docker.properties (line 6, col 0)` + +
+ +
+Password found in configuration file — affected files + +- `src/test/resources/application-test.properties (line 5, col 0)` +- `src/test/resources/application-test.properties (line 23, col 0)` +- `src/main/resources/application-docker.properties (line 5, col 0)` +- `src/main/resources/application.properties (line 14, col 0)` + +
+ +
+Server port configuration found — affected files + +- `src/main/resources/application-docker.properties (line 25, col 0)` +- `src/main/resources/application.properties (line 2, col 0)` + +
+ +
+Restricted configurations found — affected files + +- `src/main/resources/application-docker.properties (line 25, col 0)` +- `src/main/resources/application.properties (line 2, col 0)` + +
+ +
+Embedded framework - Spring Security — affected files + +- `pom.xml (line 65, col 0)` + +
+ +
+Detects usage of Jakarta Persistence (JPA) APIs — affected files + +- `pom.xml (line 46, col 0)` + +
+ +## Upgrade Issues + +| Issue Name | Criticality | Story Points | Occurrences | +|------------|-------------|--------------|-------------| +| Spring Boot Version Has Reached the End of OSS Support | Mandatory | 8 | [8](#Spring_Boot_Version_Has_Reached_the_End_of_OSS_Support) | +| Spring Framework Version Has Reached the End of OSS Support | Mandatory | 8 | [4](#Spring_Framework_Version_Has_Reached_the_End_of_OSS_Support) | +| Java Version Has Reached the End of Support | Mandatory | 8 | [1](#Java_Version_Has_Reached_the_End_of_Support) | +| Java Version is not the latest LTS | Optional | 8 | [2](#Java_Version_is_not_the_latest_LTS) | + +### Issue Details + +
+Spring Boot Version Has Reached the End of OSS Support — affected files + +- `pom.xml (line 40, col 0)` +- `pom.xml (line 34, col 0)` +- `pom.xml (line 84, col 0)` +- `pom.xml (line 46, col 0)` +- `pom.xml (line 78, col 0)` +- `pom.xml (line 59, col 0)` +- `pom.xml (line 98, col 0)` +- `pom.xml (line 65, col 0)` + +
+ +
+Spring Framework Version Has Reached the End of OSS Support — affected files + +- `pom.xml (line 84, col 0)` +- `pom.xml (line 34, col 0)` +- `pom.xml (line 65, col 0)` +- `pom.xml (line 46, col 0)` + +
+ +
+Java Version Has Reached the End of Support — affected files + +- `pom.xml (line 24, col 0)` + +
+ +
+Java Version is not the latest LTS — affected files + +- `pom.xml (line 25, col 0)` +- `pom.xml (line 26, col 0)` + +
+ +--- + +## Codebase Insights + +> **Note:** These documents are generated by AI and may contain inaccuracies or incomplete information. Please review carefully. + +1. **[Architecture Diagram](facts/architecture-diagram.md)** — Understand the big picture: system layers and component relationships +2. **[Dependency Map](facts/dependency-map.md)** — Know what the project depends on and where the risks are +3. **[API & Service Contracts](facts/api-service-contracts.md)** — See how services communicate and what contracts they expose +4. **[Data Architecture](facts/data-architecture.md)** — Explore data models, storage, and data flow patterns +5. **[Configuration Inventory](facts/configuration-inventory.md)** — Review how the application is configured across environments +6. **[Business Workflows](facts/business-workflows.md)** — Trace end-to-end business processes and domain logic + +[Share feedback](https://aka.ms/ghcp-appmod/feedback) diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/api-service-contracts.md b/.github/modernize/assessment/reports/report-20261104093052/facts/api-service-contracts.md new file mode 100644 index 000000000..786b3443c --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/api-service-contracts.md @@ -0,0 +1,93 @@ +# API & Service Communication Contracts + +The application exposes five HTTP routes: server-rendered gallery/detail pages, a multipart upload endpoint with a JSON response, and an endpoint that serves photo bytes. Requests are handled synchronously within one application and synchronously persisted to the database; no inter-service API or asynchronous messaging was found. + +## Service Catalog + +| Service | Port | Category | Purpose | +|---|---:|---|---| +| Photo Album Java (`photo-album`) | 8080 | API Layer | Source-built Spring MVC and Thymeleaf web application providing the photo gallery, upload, detail, delete, and image-serving routes. | +| Oracle Database (`oracle-db`) | 1521 | Infrastructure | Third-party Oracle Free container storing photo metadata and image BLOBs; reached by the application over the Compose network. | + +The Maven build contains one application module. Docker Compose defines the application and database containers; the database is an image-based dependency, not a source-built service. + +## API Endpoints Inventory + +| Service | Method | Path | Request Type | Response Type | +|---|---|---|---|---| +| Photo Album Java — `HomeController` | GET | `/` | No body; optional MVC `Model` populated by the controller | HTTP 200, rendered `index` HTML view with photo data; on retrieval error, renders the view with an empty list | +| Photo Album Java — `HomeController` | POST | `/upload` | Multipart form field `files` containing one or more uploaded files | JSON map containing `success`, `uploadedPhotos`, and `failedUploads`; HTTP 400 for no files, HTTP 200 for processed uploads, including partial failures; HTTP 401 when unauthenticated | +| Photo Album Java — `DetailController` | GET | `/detail/{id}` | Path parameter `id` | HTTP 200, rendered `detail` HTML view; redirects to `/` when the photo is absent or an error occurs | +| Photo Album Java — `DetailController` | POST | `/detail/{id}/delete` | Path parameter `id`; no body | Redirect to `/` with a flash success/error message; HTTP 401 when unauthenticated | +| Photo Album Java — `PhotoFileController` | GET | `/photo/{id}` | Path parameter `id` | HTTP 200 with image bytes and the stored media type; HTTP 404 if unavailable; HTTP 500 on an unexpected error | + +No explicit API versioning scheme was found. The routes are implemented with Spring MVC controllers; `/upload` is the only route returning a JSON response rather than an HTML view or binary content. + +## Management & Observability Endpoints + +| Service | Endpoint | Custom Metrics (if any) | +|---|---|---| +| Photo Album Java | No Actuator, health, metrics, or management endpoint was found. | None found | +| Oracle Database | Container health check is configured in Docker Compose; no HTTP management endpoint is exposed by this application. | None found | + +## DTOs & Contracts + +- `Photo` is the mutable JPA entity and is also added to MVC models for the gallery and detail views. Its persistence structure is documented separately in `data-architecture.md`. +- `UploadResult` is a mutable service-layer result object used to communicate per-file upload success or failure to the controller; it is not directly serialized as the HTTP response. +- The upload response is assembled from Java `Map` instances rather than a dedicated response DTO. Each successful item contains photo metadata, and each failed item contains a filename and error message. +- No dedicated request DTO is defined; upload input is Spring's `MultipartFile` binding. The `id` values are path parameters. +- No immutable DTOs were found. JSON responses use Spring Boot/Jackson serialization, while gallery and detail responses use Thymeleaf templates. +- No OpenAPI/Swagger specification, protobuf schema, or GraphQL schema was found. + +## Communication Patterns + +- **Synchronous:** The browser calls the Spring MVC application over HTTP. Controllers call `PhotoService` directly; `PhotoServiceImpl` calls `PhotoRepository` via Spring Data JPA/JDBC to Oracle. Photo metadata and binary content are stored in the database. +- **Asynchronous:** No message broker, event-driven communication, or asynchronous service calls were found. +- **Resilience:** No retry, circuit-breaker, timeout, or bulkhead policy was found. Errors are handled locally in some controller/service paths (including upload result errors, redirects, and HTTP 404/500 responses); no downstream-service fallback policy exists. +- **Discovery and gateway:** There is no service registry, client-side load balancing, or API gateway. Compose's internal `oracle-db` hostname is used for database connectivity. +- **Startup dependency:** Compose starts the application after the Oracle container reports healthy. This affects whether the application can start and access the database; operational probe and wait details are maintained in `configuration-inventory.md`. +- **Security:** Spring Security configures stateless HTTP Basic authentication for `POST /upload` and `POST /detail/*/delete`; other routes are permitted anonymously. There is no application-level role check beyond requiring authentication on those state-changing routes. CSRF is disabled. No TLS/HTTPS configuration was found, so transport encryption is not configured in the application. + +## Service Technology Matrix + +| Service | Web | Data Access | Discovery | Gateway | Actuator | Cache | Metrics | +|---|---|---|---|---|---|---|---| +| Photo Album Java | Spring MVC, Thymeleaf | Spring Data JPA | None | None | None found | None found | None found | +| Oracle Database | None | Oracle Database | None | None | Compose health check only | N/A | None found | + +## Service Communication Sequence + +The upload path illustrates the main state-changing request flow. Authentication is enforced before the controller receives the request; successful uploads are saved through the local service/repository chain. + +```mermaid +sequenceDiagram + participant Client as "Browser" + participant Security as "Spring Security" + participant Controller as "HomeController" + participant Service as "PhotoService" + participant Repository as "PhotoRepository" + participant DB as "Oracle Database" + + Client->>Security: POST /upload with multipart files and HTTP Basic + alt Authenticated + Security->>Controller: Forward upload request + loop Each uploaded file + Controller->>Service: uploadPhoto(file) + Service->>Service: Check file type and size; read image metadata + Service->>Repository: Save Photo and image bytes + Repository->>DB: Synchronous database write + DB-->>Repository: Persisted Photo + Repository-->>Service: Photo with ID + Service-->>Controller: UploadResult + Controller->>Service: getPhotoById(photoId) + Service->>Repository: findById(photoId) + Repository->>DB: Synchronous database read + DB-->>Repository: Photo + Repository-->>Service: Optional Photo + Service-->>Controller: Optional Photo + end + Controller-->>Client: 200 JSON with uploadedPhotos and failedUploads + else Unauthenticated + Security-->>Client: 401 Unauthorized + end +``` diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/architecture-diagram.md b/.github/modernize/assessment/reports/report-20261104093052/facts/architecture-diagram.md new file mode 100644 index 000000000..4d9a02f71 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/architecture-diagram.md @@ -0,0 +1,117 @@ +# Architecture Diagram + +Photo Album is a server-rendered Spring Boot application for browsing, uploading, serving, and deleting photos. It persists photo data and metadata in Oracle Database and can be run with the database using Docker Compose. + +## Application Architecture + +```mermaid +flowchart TD + subgraph ClientA["Client Layer"] + BrowserA["Web Browser"] + end + subgraph AppA["Application Layer - Spring Boot 2.7.18"] + WebA["Spring MVC Controllers"] + ViewA["Thymeleaf Views"] + SecurityA["Spring Security HTTP Basic"] + ServiceA["Photo Service"] + end + subgraph DataA["Data Layer"] + JpaA["Spring Data JPA and Hibernate"] + OracleA[("Oracle Database Free 23ai")] + end + subgraph RuntimeA["Container Runtime"] + ComposeA["Docker Compose"] + end + + BrowserA -->|"HTTP gallery and photo requests"| WebA + WebA -->|"renders pages"| ViewA + ViewA -->|"HTML responses"| BrowserA + BrowserA -->|"upload and delete requests"| SecurityA + SecurityA -->|"authorizes state changes"| WebA + WebA -->|"photo operations"| ServiceA + ServiceA -->|"CRUD and BLOB access"| JpaA + JpaA -->|"Oracle SQL"| OracleA + ComposeA -->|"runs application and database containers"| AppA + ComposeA -->|"runs database container"| OracleA +``` + +### Technology Stack Summary + +| Layer | Technology | Version | Purpose | +|---|---|---|---| +| Application runtime | Java | 8 | Runs the server-side application | +| Application framework | Spring Boot | 2.7.18 | Application configuration and runtime | +| Presentation | Spring MVC and Thymeleaf | Spring Boot managed | Handles HTTP requests and renders server-side HTML | +| Security | Spring Security | Spring Boot managed | HTTP Basic authentication for photo upload and deletion | +| Data access | Spring Data JPA and Hibernate | Spring Boot managed | Persists photo entities and performs database queries | +| Database driver | Oracle JDBC ojdbc8 | Maven-managed | Connects the application to Oracle Database | +| Database | Oracle Database Free | 23ai container image | Stores photo metadata and image BLOBs | +| Container orchestration | Docker Compose | Not specified | Runs the application and Oracle database containers | + +### Data Storage & External Services + +Oracle Database is the application's only identified data store. Spring Data JPA and Hibernate access Oracle through the Oracle JDBC driver; photo metadata and image bytes are stored together, with image bytes held as BLOB data. Docker Compose provisions the database alongside the application. No cache, message broker, or external API integration was identified. + +### Key Architectural Decisions + +- Uses Spring MVC with Thymeleaf for server-rendered gallery and detail pages, with a JSON response for uploads. +- Separates photo operations behind a service interface and Spring Data JPA repository. +- Stores uploaded images in Oracle rather than on the application filesystem; Spring Security requires HTTP Basic authentication for uploads and deletion. + +## Component Relationships + +```mermaid +flowchart LR + subgraph PresentationC["Presentation"] + HomeC["HomeController"] + DetailC["DetailController"] + FileC["PhotoFileController"] + ViewsC["Thymeleaf Views"] + end + subgraph BusinessC["Business Logic"] + ServiceApiC["PhotoService"] + ServiceImplC["PhotoServiceImpl"] + end + subgraph DataC["Data Access"] + RepoC["PhotoRepository"] + EntityC["Photo Entity"] + DatabaseC[("Oracle Database")] + end + subgraph InfraC["Infrastructure"] + SecurityConfigC["SecurityConfig"] + SecurityFilterC["Spring Security Filter Chain"] + end + subgraph ModelsC["Models"] + UploadC["UploadResult"] + end + + HomeC -->|"gallery and upload operations"| ServiceApiC + DetailC -->|"detail, navigation, and delete operations"| ServiceApiC + FileC -->|"retrieves image data"| ServiceApiC + ServiceApiC -->|"implemented by"| ServiceImplC + ServiceImplC -->|"queries and persists"| RepoC + RepoC -->|"maps"| EntityC + RepoC -->|"executes database operations"| DatabaseC + HomeC -->|"returns upload status"| UploadC + HomeC -->|"selects gallery template"| ViewsC + DetailC -->|"selects detail template"| ViewsC + SecurityConfigC -->|"configures"| SecurityFilterC + SecurityFilterC -.->|"protects upload and delete requests"| HomeC + SecurityFilterC -.->|"protects delete requests"| DetailC +``` + +### Component Inventory + +| Component | Layer | Type | Responsibility | +|---|---|---|---| +| HomeController | Presentation | Spring MVC Controller | Displays the gallery and handles multi-file upload requests | +| DetailController | Presentation | Spring MVC Controller | Displays a photo, provides previous and next navigation, and handles deletion | +| PhotoFileController | Presentation | Spring MVC Controller | Serves image bytes from the database with the stored media type | +| Thymeleaf Views | Presentation | Server-rendered templates | Render the gallery and photo detail pages | +| PhotoService | Business Logic | Service interface | Defines photo retrieval, upload, navigation, and deletion operations | +| PhotoServiceImpl | Business Logic | Spring service | Validates uploaded images and coordinates photo persistence and retrieval | +| PhotoRepository | Data Access | Spring Data JPA repository | Performs entity CRUD and photo-specific Oracle queries | +| Photo | Data Access | JPA entity | Maps photo metadata and image BLOB data to the database | +| SecurityConfig | Infrastructure | Spring configuration | Defines password encoding, in-memory admin credentials, and HTTP authorization | +| Spring Security Filter Chain | Infrastructure | Security filters | Enforces HTTP Basic authentication for upload and delete endpoints | +| UploadResult | Models | Response model | Carries upload success, photo identifier, filename, and error information | diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/assessment-overview.md b/.github/modernize/assessment/reports/report-20261104093052/facts/assessment-overview.md new file mode 100644 index 000000000..496a9ddfa --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/assessment-overview.md @@ -0,0 +1,42 @@ +# 📊 Project Assessment Overview + +> This documentation provides a comprehensive assessment of the project, generated by the AI-powered modernization assessment tool. Use this page as your starting point to navigate through all assessment artifacts. + +--- + +## Quick Start Guide + +Follow this recommended reading order for the most effective understanding: + +1. **[Architecture Diagram](architecture-diagram.md)** — Understand the big picture: system layers and component relationships +2. **[Dependency Map](dependency-map.md)** — Know what the project depends on and where the risks are +3. **[API & Service Contracts](api-service-contracts.md)** — See how services communicate and what contracts they expose +4. **[Data Architecture](data-architecture.md)** — Explore data models, storage, and data flow patterns +5. **[Configuration Inventory](configuration-inventory.md)** — Review how the application is configured across environments +6. **[Business Workflows](business-workflows.md)** — Trace end-to-end business processes and domain logic + +--- + +## Documentation Structure + +### 🏛️ Architecture & Dependencies + +| Document | Description | +|----------|-------------| +| [Architecture Diagram](architecture-diagram.md) | High-level application architecture and detailed component relationship diagrams with Mermaid visualizations | +| [Dependency Map](dependency-map.md) | External dependencies grouped by functional category — frameworks, databases, messaging, security, and more | + +### 🔌 APIs & Data + +| Document | Description | +|----------|-------------| +| [API & Service Contracts](api-service-contracts.md) | Service inventory, API endpoints, communication patterns (sync/async), DTOs, and resilience policies | +| [Data Architecture](data-architecture.md) | Database configuration, entity models, data ownership boundaries, repository interfaces, and caching strategies | + +### ⚙️ Configuration & Business Logic + +| Document | Description | +|----------|-------------| +| [Configuration Inventory](configuration-inventory.md) | Configuration sources, build/runtime profiles, externalized properties, secrets management, and feature flags | +| [Business Workflows](business-workflows.md) | Business processes end-to-end, domain entities, business rules, cross-service data flows, and decision logic | + diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/business-workflows.md b/.github/modernize/assessment/reports/report-20261104093052/facts/business-workflows.md new file mode 100644 index 000000000..53a3d2528 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/business-workflows.md @@ -0,0 +1,82 @@ +# Core Business Workflows + +Photo Album lets users upload, browse, inspect, navigate, and remove photos from a shared gallery. Each accepted photo and its image data are kept together as a persistent gallery item. + +## Domain Entities + +| Entity | Service / Bounded Context | Description | Key Relationships | +|---|---|---|---| +| Photo | Photo Album / Gallery Management | A user-contributed image managed as a gallery item, with information used to display and navigate the collection. | Each photo is independently addressable in the gallery and is the unit of upload, viewing, navigation, and deletion. | +| UploadResult | Photo Album / Gallery Management | The outcome of processing an individual uploaded file, including its success or failure and any associated photo identifier. | Produced for each file submitted to the upload workflow; a successful result identifies the persisted Photo. | + +## Service-to-Domain Mapping + +The application is a single deployable Spring MVC application, not a set of independently deployable business services. Its presentation, business operations, and persistence components share the Photo Album bounded context. + +| Service | Domain Context | Owned Entities | External Dependencies | +|---|---|---|---| +| HomeController and DetailController | Gallery presentation and workflows | None; coordinates gallery operations | PhotoService | +| PhotoFileController | Photo delivery | None; retrieves image data for display | PhotoService | +| PhotoServiceImpl | Gallery management | Photo lifecycle and UploadResult outcomes | PhotoRepository; Oracle database through JPA | +| PhotoRepository | Gallery persistence | Persists and queries Photo records | Oracle database | + +All photo records, including their image data, are owned by this application and stored in Oracle. There are no cross-service APIs, events, or shared cross-context identifiers. + +## Primary Workflows + +### Workflow 1: Upload Photos + +An authenticated user submits one or more files through the upload interface to `POST /upload`. The controller processes each file independently through PhotoService. For each file, the service checks the supported MIME type, configured per-file size limit, non-empty content, and image pixel limit when dimensions can be read. Invalid files receive an individual failure result; accepted data is stored as a Photo in Oracle and returned with its identifier. The controller gathers successful photo summaries and per-file failures, allowing partial success when a batch contains both accepted and rejected files. + +### Workflow 2: Browse and View a Photo + +The gallery page at `GET /` requests photos ordered newest-first and renders each photo with its metadata. Selecting an item opens `GET /detail/{id}`; the controller retrieves that photo and asks the service for the adjacent older and newer photos by upload time. The image is served separately from `GET /photo/{id}` using the Photo's stored image data. If a photo cannot be found, the detail and image handlers return the user to the gallery or return not found, respectively. + +### Workflow 3: Delete a Photo + +An authenticated user submits the delete form to `POST /detail/{id}/delete`. PhotoService looks up the photo; if present, it deletes that record (including the database-stored image) and the controller reports success. If no photo exists, the controller reports that it was not found. The user is redirected to the gallery. + +## Cross-Service Data Flows + +There are no cross-service composition or aggregation flows: all business operations run within one application. The PhotoService coordinates with the PhotoRepository, which reads or writes the Photo and its image data in Oracle. Gallery, detail, and image responses are assembled from this same source of truth; no remote-service fallback path is implemented. + +## Business Workflow Sequence + +The upload workflow is the primary business process. Each submitted file is handled independently, so a failed validation or save for one file does not prevent the controller from processing the remaining files. + +```mermaid +sequenceDiagram + participant User + participant Gallery as "Gallery Controller" + participant PhotoService as "Photo Service" + participant Photo as "Photo" + participant Repository as "Photo Repository" + participant Oracle as "Oracle Database" + + User->>Gallery: Submit photo files to POST /upload + Gallery->>PhotoService: Process each file + PhotoService->>PhotoService: Check file type, size, content, and image dimensions + alt File passes upload rules + PhotoService->>Photo: Create gallery item with image and metadata + PhotoService->>Repository: Save photo + Repository->>Oracle: Persist photo record and image data + Oracle-->>Repository: Saved photo + Repository-->>PhotoService: Photo with identifier + PhotoService-->>Gallery: Successful upload result + else File fails a rule or cannot be saved + PhotoService-->>Gallery: Failed upload result with reason + end + Gallery-->>User: Report uploaded and failed files +``` + +## Business Rules & Decision Logic + +- **Supported file types:** Upload accepts MIME types configured as JPEG, PNG, GIF, or WebP. +- **Per-file size and content:** Each file must be non-empty and no larger than the configured 10 MiB limit. The HTTP multipart request also has a configured maximum request size. +- **Image dimensions:** When image dimensions can be read, images exceeding 40 million pixels are rejected. If dimensions cannot be extracted, upload continues without dimension metadata. +- **Batch outcomes:** Each file produces an independent outcome. The response includes successful photo summaries and individual failures; its overall success flag is true when at least one uploaded photo is included. +- **Gallery ordering and navigation:** Gallery entries are ordered newest-first. Detail navigation finds older and newer items using upload timestamps. +- **Photo lifecycle:** A successful upload creates a persistent Photo; deletion removes it from the gallery and database. No additional business lifecycle states are modeled. +- **Authorization:** Gallery and photo reads are public. Upload and delete POST operations require HTTP Basic authentication. +- **Persistence boundary:** PhotoService operations use transaction management for database changes. There are no distributed transactions, event-driven side effects, or compensating workflows. +- **Failure handling:** Missing photos yield not-found behavior for image retrieval and a not-found message for deletion. Upload validation and persistence failures are represented as per-file errors; no external-service fallback applies. diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/configuration-inventory.md b/.github/modernize/assessment/reports/report-20261104093052/facts/configuration-inventory.md new file mode 100644 index 000000000..cb59ba4bc --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/configuration-inventory.md @@ -0,0 +1,135 @@ +# Configuration & Externalized Settings Inventory + +The project uses Spring properties, a Docker Compose environment, and a git-ignored `.env` file populated from an example for database and administrator credentials. It has separate Docker and test runtime profiles; no remote configuration service or secret manager is configured. + +## Configuration Sources + +| Source | Type | Path/Location | Notes | +|---|---|---|---| +| Spring application configuration | Properties | `src/main/resources/application.properties` | Base settings, including Oracle connection placeholders and application defaults. | +| Spring Docker profile configuration | Properties | `src/main/resources/application-docker.properties` | Overrides logging levels; also repeats several base settings. | +| Spring test profile configuration | Properties | `src/test/resources/application-test.properties` | H2 in-memory database, test-only credentials, and test upload path. | +| Docker Compose | YAML | `docker-compose.yml` | Sets container environment variables, port mappings, persistent volume, network, health check, and database dependency. | +| Docker image build/runtime | Dockerfile | `Dockerfile` | Maven/Java image versions, exposed port, and JVM options. | +| Local deployment environment | Environment file template | `.env.example` (copied to git-ignored `.env`) | Supplies Oracle administrator, application schema, and application administrator credentials. Actual `.env` values are not inventoried. | +| Oracle initialization | SQL/shell | `oracle-init/01-create-user.sql`, `oracle-init/create-user.sh`, `oracle-init/02-verify-user.sql`, `oracle-init/healthcheck.sql` | Initialization scripts; credential-bearing values are passed through environment variables rather than embedded in the SQL source. | +| Maven project configuration | XML | `pom.xml` | Declares Spring Boot parent, Java version, dependencies, and packaging plugin. | + +No Spring Cloud Config repository, Azure/AWS configuration service, Vault, or dedicated secret store reference was found. GitHub Actions repository secrets are used by repository automation workflows, not by the application's runtime configuration. + +## Build Profiles + +| Profile | Activation | Purpose | Key Dependencies/Plugins | +|---|---|---|---| +| Default Maven build | Automatic; no Maven profiles are declared in `pom.xml` | Compiles and packages the executable application JAR. | Spring Boot Maven plugin; application dependencies are declared in the main dependency set. | +| Docker build | Docker build invocation; `Dockerfile` runs `mvn clean package -DskipTests` | Builds the application in a Maven builder image before copying the JAR into the runtime image. | `maven:3.9.6-eclipse-temurin-8`; no profile-specific dependencies. | + +## Runtime Profiles + +| Profile | Activation Method | Config Files | Key Overrides | +|---|---|---|---| +| Default | Spring default when no profile is active | `src/main/resources/application.properties` | Oracle connection defaults and base logging settings. | +| `docker` | `SPRING_PROFILES_ACTIVE=docker` in Compose | `application.properties`, `application-docker.properties` | Compose-provided Oracle URL/user/password; application log level `INFO`, web log level `WARN`, Hibernate SQL log level `DEBUG`. | +| `test` | `@ActiveProfiles("test")` on `PhotoAlbumApplicationTests` | `application.properties`, `src/test/resources/application-test.properties` | H2 in-memory database; test-only admin credentials and upload path; SQL display disabled. | + +Spring combines the active profile with base configuration. Compose activates only `docker`; the test class activates only `test`. No profile-specific config for production or profile combinations was found. + +## Properties Inventory + +### Application + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `server.port` | `8080` | default, docker | `application.properties`; repeated in `application-docker.properties` | +| `server.servlet.encoding.charset` | `UTF-8` | default, docker | Base and Docker properties | +| `server.servlet.encoding.enabled` | `true` | default, docker | Base and Docker properties | +| `server.servlet.encoding.force` | `true` | default, docker | Base and Docker properties | +| `spring.servlet.multipart.max-file-size` | `10MB` | default, docker | Base and Docker properties | +| `spring.servlet.multipart.max-request-size` | `50MB` | default, docker | Base and Docker properties | +| `app.file-upload.max-file-size-bytes` | `10485760` (10 MiB) | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.allowed-mime-types` | `image/jpeg,image/png,image/gif,image/webp` | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.max-files-per-upload` | `10` | default, docker, test | Base, Docker, and test properties; repeated in Docker properties | +| `app.file-upload.upload-path` | Not set in base/Docker configuration | test | `application-test.properties`: `target/test-uploads` | +| `app.admin.username` | `admin` via `@Value` default; Compose can override with `APP_ADMIN_USERNAME` | default, docker | `SecurityConfig`; Compose variable defaults to `admin`; test profile sets `admin` | +| `app.admin.password` | Required in application configuration; no source-code default | default, docker | `SecurityConfig`; Compose requires `APP_ADMIN_PASSWORD`; test profile uses a test-only value | + +### Database + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `spring.datasource.url` | `jdbc:oracle:thin:@oracle-db:1521/FREEPDB1`, overridden by `SPRING_DATASOURCE_URL` | default, docker | Base and Docker properties; Compose supplies the container hostname URL | +| `spring.datasource.username` | `photoalbum`, overridden by `SPRING_DATASOURCE_USERNAME` | default, docker | Base and Docker properties; Compose uses `APP_USER` (default `photoalbum`) | +| `spring.datasource.password` | Required from `SPRING_DATASOURCE_PASSWORD` | default, docker | Base and Docker properties; Compose maps `APP_USER_PASSWORD` to the Spring variable | +| `spring.datasource.driver-class-name` | `oracle.jdbc.OracleDriver` | default, docker | Base and Docker properties | +| `spring.jpa.database-platform` | `org.hibernate.dialect.OracleDialect` | default, docker | Base and Docker properties | +| `spring.jpa.hibernate.ddl-auto` | `create` | default, docker | Base and Docker properties | +| `spring.jpa.show-sql` | `true` | default, docker | Base and Docker properties | +| `spring.jpa.properties.hibernate.format_sql` | `true` | default, docker | Base and Docker properties | +| `spring.datasource.url` | `jdbc:h2:mem:testdb` | test | `application-test.properties` | +| `spring.datasource.driver-class-name` | `org.h2.Driver` | test | `application-test.properties` | +| `spring.datasource.username` | `sa` | test | `application-test.properties` | +| `spring.datasource.password` | Empty | test | `application-test.properties` | +| `spring.jpa.database-platform` | `org.hibernate.dialect.H2Dialect` | test | `application-test.properties` | +| `spring.jpa.hibernate.ddl-auto` | `create-drop` | test | `application-test.properties` | +| `spring.jpa.show-sql` | `false` | test | `application-test.properties` | + +### Logging + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `logging.level.com.photoalbum` | `DEBUG` | default, test; `INFO` in docker | Base, test, and Docker properties | +| `logging.level.org.springframework.web` | `DEBUG` | default; `WARN` in docker | Base and Docker properties | +| `logging.level.org.hibernate.SQL` | Not set in base; `DEBUG` in docker | docker | `application-docker.properties` | + +## Startup Parameters & Resource Requirements + +| Service | JVM/Runtime Options | Memory | Instance Count | +|---|---|---|---| +| `photoalbum-java-app` | `JAVA_OPTS="-Xmx512m -Xms256m"`; Compose sets `SPRING_PROFILES_ACTIVE=docker` and database/admin environment overrides | JVM heap initial 256 MiB, maximum 512 MiB; container memory limit not specified | One Compose service/container declared; scaling not configured | +| `oracle-db` | Oracle container environment variables; no JVM options specified | Not specified | One Compose service/container declared; scaling not configured | + +No CPU allocation, container memory limit, or cloud resource sizing configuration was found. + +## Startup Dependency Chain + +1. `oracle-db` starts first; Compose health check runs `healthcheck.sh` every 30 seconds, with a 10-second timeout, 15 retries, and a 180-second start period. +2. `photoalbum-java-app` waits for `oracle-db` to be healthy through Compose `depends_on: condition: service_healthy`; it then starts with the `docker` Spring profile. + +No application-container health check or explicit Spring database connection retry/startup timeout was found. + +## Secrets & Sensitive Configuration + +| Secret Reference | Type | Storage (masked) | +|---|---|---| +| `ORACLE_PASSWORD` | Oracle administrator password | Local git-ignored `.env`, passed to Oracle container; `[MASKED]` | +| `APP_USER_PASSWORD` | Application database schema password | Local git-ignored `.env`, passed to Oracle and mapped to `SPRING_DATASOURCE_PASSWORD`; `[MASKED]` | +| `APP_ADMIN_PASSWORD` | Application administrator password | Local git-ignored `.env`, passed as an application environment variable; `[MASKED]` | +| `spring.datasource.password` / `SPRING_DATASOURCE_PASSWORD` | Spring datasource credential | Environment-variable reference; required, `[MASKED]` | +| Test `spring.datasource.password` | H2 test credential | Empty test-only value | +| Test `app.admin.password` | Test administrator credential | `application-test.properties`; test-only value, `[MASKED]` | + +### Secrets Provisioning Workflow + +Copy `.env.example` to `.env`, replace all placeholder values with strong unique credentials, and run Docker Compose. Compose requires `ORACLE_PASSWORD`, `APP_USER_PASSWORD`, and `APP_ADMIN_PASSWORD`; it forwards the Oracle administrator and schema credentials to the database container and maps the schema/admin credentials into the application container. `.env` is documented as git-ignored and must not be committed. No managed identity, service principal, RBAC setup, encrypted property mechanism, or external secret manager is configured. Test credentials are isolated in test resources. + +## Feature Flags + +No feature-flag framework, application feature toggle, conditional Spring configuration, or rollout flag was found. + +| Flag Name | Default | Controlled By | +|---|---|---| +| None identified | N/A | N/A | + +## Framework & Runtime Versions + +| Component | Version | Source | +|---|---|---| +| Spring Boot | `2.7.18` | `pom.xml` parent | +| Java source/target | `8` | `pom.xml` | +| Maven | `3.9.6` in Docker build image | Dockerfile base image | +| Java build runtime | Eclipse Temurin 8 | `maven:3.9.6-eclipse-temurin-8` Docker image | +| Java application runtime | Eclipse Temurin 8 JRE | `eclipse-temurin:8-jre` Docker image | +| Oracle JDBC driver | `ojdbc8`; resolved version managed by Spring Boot dependency management | `pom.xml`; no explicit version declared | +| H2 | Version managed by Spring Boot dependency management | `pom.xml`, test scope | +| Commons IO | `2.11.0` | `pom.xml` | +| Oracle Database Free image | `latest` tag (version not pinned) | `docker-compose.yml` | diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/data-architecture.md b/.github/modernize/assessment/reports/report-20261104093052/facts/data-architecture.md new file mode 100644 index 000000000..b92000ba3 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/data-architecture.md @@ -0,0 +1,70 @@ +# Data Architecture & Persistence Layer + +The application has one JPA entity, `Photo`, persisted in a relational database through Hibernate. Oracle is configured for the normal and Docker profiles, while tests use an in-memory H2 database. + +## Database Configuration + +| Service/Module | DB Type | Profile | Driver | Connection | Migration Tool | +|---|---|---|---|---|---| +| Photo Album | Oracle | Default (no explicit profile) | Oracle JDBC (`ojdbc8`) | JDBC URL defaults to `jdbc:oracle:thin:@oracle-db:1521/FREEPDB1`; URL and credentials can be supplied through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured. | None. Hibernate creates the schema at startup; no versioned migrations or seed scripts were found. | +| Photo Album | Oracle | `docker` | Oracle JDBC (`ojdbc8`) | Same Oracle service URL by default; Docker supplies the URL and credentials through environment variables. Spring Boot's default HikariCP pool is used; no pool sizing is explicitly configured. | None. Hibernate creates the schema at startup. | +| Photo Album | H2 (in-memory) | `test` | H2 (`org.h2.Driver`, test-scoped dependency) | `jdbc:h2:mem:testdb` | None. Hibernate creates the schema for tests and drops it when the test context closes. | + +The database and schema-generation settings are defined in the application property files; see `configuration-inventory.md` for the property-level inventory. No database seed data was found. + +## Data Ownership per Service + +| Service | Tables Owned | ORM Framework | Caching | Notes | +|---|---|---|---|---| +| Photo Album | `PHOTOS` (`Photo`) | Spring Data JPA / Hibernate | None found | Single application-owned table; photo bytes are stored in a BLOB. | + +## Entity Model + +`Photo` maps to the `photos` table and owns its upload metadata and image bytes. The table has an index on `uploaded_at`. Its identifier is a UUID string generated in Java. There are no entity relationships or join tables. `UploadResult` is a service response model, not a persisted entity. + +Source: `src/main/java/com/photoalbum/model/Photo.java`. + +The service implementation applies transaction boundaries at class level, with read-only transactions for retrieval methods: `src/main/java/com/photoalbum/service/impl/PhotoServiceImpl.java`. The model's `photo_data` mapping is nullable even though the normal upload path supplies the photo bytes. + +```mermaid +erDiagram + Photo { + string Id PK + string OriginalFileName + bytes PhotoData "nullable BLOB" + string StoredFileName + string FilePath + long FileSize + string MimeType + datetime UploadedAt + int Width + int Height + } +``` + +## Key Repository Methods + +| Service | Repository | Notable Methods | Purpose | +|---|---|---|---| +| Photo Album | `PhotoRepository` — `src/main/java/com/photoalbum/repository/PhotoRepository.java`, entity `Photo` | `findAllOrderByUploadedAtDesc(): List` | Native query returns photos newest first. | +| Photo Album | `PhotoRepository` | `findPhotosUploadedBefore(LocalDateTime): List` | Native Oracle query selects up to ten older photos for navigation. | +| Photo Album | `PhotoRepository` | `findPhotosUploadedAfter(LocalDateTime): List` | Native Oracle query selects newer photos for navigation. | +| Photo Album | `PhotoRepository` | `findPhotosByUploadMonth(String, String): List` | Native Oracle query filters by year and month. | +| Photo Album | `PhotoRepository` | `findPhotosWithPagination(int, int): List` | Native Oracle `ROWNUM` query returns a row range. | +| Photo Album | `PhotoRepository` | `findPhotosWithStatistics(): List` | Native Oracle analytical query returns photos with ranking and running-total columns. | + +The repository extends `JpaRepository`, inheriting standard CRUD operations. The custom queries are native SQL, several of which use Oracle-specific features; no cross-service aggregation methods were found. + +## Caching Strategy + +No application cache provider, cache annotations, cache manager, JCache binding, or Hibernate second-level cache configuration was found. Data access uses repository/database queries without an identified application-level caching pattern or TTL/eviction policy. + +## Data Ownership Boundaries + +The project has one application and one logical data owner. The default and Docker profiles connect to Oracle; the test profile uses an isolated in-memory H2 database. The application reads and writes its own `PHOTOS` table through Spring Data JPA, with transaction management in the service layer. There are no other services, cross-service database access patterns, or CQRS/read-model boundaries identified. + +### Data Classification & Sensitivity + +| Entity | Sensitive Fields | Classification (PII/PHI/PCI/None) | Controls in Place | +|---|---|---|---| +| `Photo` | `original_file_name`, `photo_data` (image content may identify people) | Potential PII | No field-level encryption, data masking, or field-level access controls were found in the application configuration. Encryption at rest is not established by the project configuration. No PHI or PCI fields were identified. | diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/dependency-map.md b/.github/modernize/assessment/reports/report-20261104093052/facts/dependency-map.md new file mode 100644 index 000000000..f26460dfc --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/dependency-map.md @@ -0,0 +1,72 @@ +# Dependency Map + +Photo Album declares 11 direct dependencies: 9 non-test dependencies and 2 test-scoped dependencies. Versions not explicitly set in `pom.xml` are managed by Spring Boot Starter Parent 2.7.18. + +## Dependencies + +```mermaid +flowchart LR + App["Photo Album"] + BootParent["Spring Boot Starter Parent 2.7.18"] + + subgraph Web["Web Frameworks"] + WebStarter["Spring Boot Web Starter v2.7.18"] + Thymeleaf["Spring Boot Thymeleaf Starter v2.7.18"] + end + subgraph Database["Database / ORM"] + Jpa["Spring Boot Data JPA Starter v2.7.18"] + Oracle["Oracle JDBC ojdbc8 v21.5.0.0"] + end + subgraph Security["Security"] + SecurityStarter["Spring Boot Security Starter v2.7.18"] + end + subgraph Utilities["Utilities"] + Validation["Spring Boot Validation Starter v2.7.18"] + Json["Spring Boot JSON Starter v2.7.18"] + CommonsIO["Apache Commons IO v2.11.0"] + DevTools["Spring Boot DevTools v2.7.18"] + end + + App -->|"web"| Web + App -->|"persistence"| Database + App -->|"security"| Security + App -->|"utilities"| Utilities + BootParent -.->|"manages versions"| WebStarter + BootParent -.->|"manages versions"| Thymeleaf + BootParent -.->|"manages versions"| Jpa + BootParent -.->|"manages version"| Oracle + BootParent -.->|"manages versions"| SecurityStarter + BootParent -.->|"manages versions"| Validation + BootParent -.->|"manages versions"| Json + BootParent -.->|"manages versions"| DevTools +``` + +### Dependency Summary + +| Category | Count | Key Libraries | Notes | +|---|---:|---|---| +| Web Frameworks | 2 | Spring Boot Web, Thymeleaf | Spring MVC application with server-rendered templates | +| Database / ORM | 2 | Spring Data JPA, Oracle JDBC | ORM and Oracle runtime driver | +| Security | 1 | Spring Boot Security | Spring-managed security stack | +| Utilities | 4 | Validation, JSON, Commons IO, DevTools | DevTools is optional; Commons IO is explicitly pinned | + +### Version & Compatibility Risks + +Spring Boot 2.7.18 is the final 2.x release and is outside the community support window; migration to a supported Spring Boot line will also require reviewing Java 8 compatibility, since newer Spring Boot generations require a newer Java baseline. The Oracle JDBC driver is selected through the Boot parent dependency management, so its effective version should be re-evaluated as part of that upgrade. + +### Notable Observations + +- The parent POM manages versions for all direct dependencies except Commons IO, which is explicitly set to 2.11.0. +- The Spring Boot Web Starter already provides JSON support; the separately declared JSON starter may duplicate that functionality. +- The Oracle JDBC driver is runtime-scoped, while H2 is confined to tests. + +## Test Dependencies + +| Framework | Version | Notes | +|---|---|---| +| Spring Boot Test Starter | 2.7.18 | Test-scope starter; its underlying test libraries are transitive and not individually declared in `pom.xml` | +| H2 Database | 2.1.214 | Test-scope in-memory database; version managed by Spring Boot 2.7.18 | + +Total test-scope dependencies: 2 + +The project has a Spring Boot test starter and an in-memory database for tests. The individual versions of transitive test libraries are not pinned in the available build file. diff --git a/.github/modernize/assessment/reports/report-20261104093052/facts/rearchitect-assessment.json b/.github/modernize/assessment/reports/report-20261104093052/facts/rearchitect-assessment.json new file mode 100644 index 000000000..30c5011f8 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/facts/rearchitect-assessment.json @@ -0,0 +1,3 @@ +{ + "outdatedFrameworks": [] +} \ No newline at end of file diff --git a/.github/modernize/assessment/reports/report-20261104093052/report.json b/.github/modernize/assessment/reports/report-20261104093052/report.json new file mode 100644 index 000000000..0b5e96d95 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/report.json @@ -0,0 +1,1173 @@ +{ + "version": "1.0.0", + "producer": "Java AppCAT CLI", + "metadata": { + "analysisStartTime": "2026-11-04T09:30:52.775351Z", + "analysisEndTime": "2026-11-04T09:34:32.6470284Z", + "status": "Complete", + "privacyMode": "Protected", + "privacyModeHelpUrl": "https://aka.ms/appcat-privacy-mode", + "targetIds": [ + "azure-appservice", + "azure-aks", + "azure-container-apps" + ], + "targetDisplayNames": [ + "Azure App Service", + "Azure Kubernetes Service", + "Azure Container Apps" + ], + "capabilities": [ + "openjdk25" + ], + "os": [ + "linux", + "windows" + ], + "domains": [ + "java-upgrade", + "cloud-readiness" + ], + "mode": "full", + "minimumCveSeverity": null + }, + "summary": { + "totalProjects": 1, + "totalIssues": 10, + "totalIncidents": 31, + "totalEffort": 196, + "charts": { + "severity": { + "mandatory": 13, + "optional": 2, + "potential": 16, + "information": 0 + }, + "category": { + "auth-migration": 1, + "database-migration": 6, + "framework-upgrade": 12, + "jakarta-migration": 1, + "java-version-upgrade": 3, + "local-credential": 4, + "spring-migration": 4 + } + } + }, + "projects": [ + { + "path": ".", + "issues": 10, + "storyPoints": 196, + "properties": { + "appName": "photo-album", + "jdkVersion": "1.8", + "frameworks": [ + "Spring Boot", + "Spring" + ], + "languages": [ + "Java", + "JavaScript" + ], + "tools": [ + "Maven" + ] + }, + "incidents": [ + { + "ruleId": "auth-02000", + "incidentId": "b845d4f9-3276-4be7-a051-635b67d9259a", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1bdf6e21-f623-4f14-9339-ae1a0ba204d2", + "location": "pom.xml", + "locationKind": "File", + "line": 52, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "44b171b1-2ca1-4627-a673-8060fdce5f29", + "location": "docker-compose.yml", + "locationKind": "File", + "line": 32, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "40510a44-6c1f-49c5-9103-c5d2a17a7682", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 3, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "5f157c91-507e-4794-86f9-6799a711160c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 12, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "bc62976f-e22d-4434-9046-b0836c0e6b7c", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 15, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-database-microsoft-oracle-07000", + "incidentId": "1004341d-8548-4a06-afba-e56fdf194ba1", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 6, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "potential" + }, + "azure-appservice": { + "effort": 8, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 8, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "995a2b3e-4870-4e04-8d6d-a5701427008a", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "97097182-5728-42f1-b69e-5b16a66937ff", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "154a6aea-7e34-49c7-8d83-8897ac979f65", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-framework-version-01000", + "incidentId": "433cc960-b5ae-4a76-a45b-2c34d446e0a3", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "a95a682d-dad5-4275-8d6d-6d43ed4188cd", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b3814a33-4403-4442-a2f0-a119b0f6248a", + "location": "src/test/resources/application-test.properties", + "locationKind": "File", + "line": 23, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "1ac607ee-8338-440c-b110-79d433327062", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 5, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-password-01000", + "incidentId": "b9c9e02a-c269-4137-9d82-f8671fcb2130", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 14, + "column": 0, + "targets": { + "azure-aks": { + "effort": 3, + "severity": "potential" + }, + "azure-appservice": { + "effort": 3, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 3, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-01000", + "incidentId": "36436d2b-df81-40eb-a0fe-d3f8ede23005", + "location": "pom.xml", + "locationKind": "File", + "line": 24, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "927cfd73-3a17-4888-99f0-b00ccd7d84ce", + "location": "pom.xml", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "azure-java-version-02000", + "incidentId": "9b3a9911-d9d3-4190-a82b-6f28e3a0ad14", + "location": "pom.xml", + "locationKind": "File", + "line": 26, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "optional" + }, + "azure-appservice": { + "effort": 8, + "severity": "optional" + }, + "azure-container-apps": { + "effort": 8, + "severity": "optional" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "6c645761-fb02-4d39-a079-1389acb57081", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-port-01000", + "incidentId": "8474445f-4d9f-4410-ba08-4eea0a5f407b", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-aks": { + "effort": 1, + "severity": "potential" + }, + "azure-appservice": { + "effort": 1, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 1, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "615ff34c-58b8-44ac-850a-0760d5125fe9", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 25, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-restricted-config-01000", + "incidentId": "2c17f79e-3853-464c-8de3-42ffb30fb98e", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 2, + "column": 0, + "targets": { + "azure-container-apps": { + "effort": 2, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c61300ea-5005-4813-a64d-41cc57d2d16f", + "location": "pom.xml", + "locationKind": "File", + "line": 40, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "4264b153-88a3-4faf-a76f-a4c4b13bf6d1", + "location": "pom.xml", + "locationKind": "File", + "line": 34, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "3507d6d7-3397-428f-883f-aa62f6d638de", + "location": "pom.xml", + "locationKind": "File", + "line": 84, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "7cd37e0b-2862-40da-b295-7cfecdf85b33", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "69e09b4a-1b8a-4a8c-b2f6-61f53bf7a6cf", + "location": "pom.xml", + "locationKind": "File", + "line": 78, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "b1800a7a-6a4b-4b66-8e98-bbbff27bafa4", + "location": "pom.xml", + "locationKind": "File", + "line": 59, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "bcf5c6b6-36a4-45ae-ac0c-c941c264a286", + "location": "pom.xml", + "locationKind": "File", + "line": 98, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "spring-boot-to-azure-spring-boot-version-01000", + "incidentId": "c5ff81c6-2bdf-417a-b7a5-6943bdb72930", + "location": "pom.xml", + "locationKind": "File", + "line": 65, + "column": 0, + "targets": { + "azure-aks": { + "effort": 8, + "severity": "mandatory" + }, + "azure-appservice": { + "effort": 8, + "severity": "mandatory" + }, + "azure-container-apps": { + "effort": 8, + "severity": "mandatory" + } + }, + "labels": [ + "type=violation", + "ruleset=azure/springboot" + ] + }, + { + "ruleId": "jakarta-database-00002", + "incidentId": "73c943cd-017a-4dbe-965e-8128b10fdc4e", + "location": "pom.xml", + "locationKind": "File", + "line": 46, + "column": 0, + "targets": { + "azure-aks": { + "effort": 5, + "severity": "potential" + }, + "azure-appservice": { + "effort": 5, + "severity": "potential" + }, + "azure-container-apps": { + "effort": 5, + "severity": "potential" + } + }, + "labels": [ + "type=violation", + "ruleset=cloud-readiness" + ] + } + ] + } + ], + "rules": { + "auth-02000": { + "id": "auth-02000", + "description": "The application embeds a Spring Security library. Ensure that the application is configured to access the identity provider.\n\nConsider using Microsoft Entra ID as an identity provider.", + "title": "Embedded framework - Spring Security", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-app-service#identity-providers", + "title": "Spring Security-related dependencies found in the project" + }, + { + "url": "https://docs.microsoft.com/azure/active-directory/external-identities/azure-ad-account", + "title": "Azure Active Directory (Azure AD) identity provider for External Identities" + }, + { + "url": "https://docs.spring.io/spring-security/reference/index.html", + "title": "Spring Security" + }, + { + "url": "https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization", + "title": "Spring Boot API: Authorization" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=auth-migration", + "os=windows", + "os=linux" + ] + }, + "azure-database-microsoft-oracle-07000": { + "id": "azure-database-microsoft-oracle-07000", + "description": "Oracle database found. To migrate a Java application that uses an Oracle database to Azure, you can follow these recommendations:\n\n * **Migrate to Azure Database for PostgreSQL**: Azure recommends migrating Oracle databases to Azure Database for PostgreSQL Flexible Server as it provides better cost-effectiveness and performance. Create a managed PostgreSQL Flexible Server database in Azure and choose the appropriate pricing tier based on your application\u0027s requirements.\n\n * **Use migration tools**: Utilize the Azure Database Migration Service (DMS) or third-party tools to migrate your Oracle database schema and data to PostgreSQL. Consider using ora2pg or similar tools to convert Oracle-specific SQL to PostgreSQL-compatible SQL.\n\n * **Update database drivers and connection strings**: Replace Oracle JDBC drivers with PostgreSQL drivers in your Java application. Update connection strings from Oracle format (jdbc:oracle:thin:) to PostgreSQL format (jdbc:postgresql:).\n\n * **Review and convert Oracle-specific code**: Identify and convert Oracle-specific SQL functions, stored procedures, and PL/SQL code to PostgreSQL equivalents. Pay attention to data types, syntax differences, and built-in functions.\n\n * Enable **monitoring and diagnostics**: Utilize Azure Monitor to gain insights into the performance and health of your Java application and the underlying PostgreSQL database. Set up metrics, alerts, and log analytics to proactively identify and resolve issues.\n\n * Implement **security** measures: Apply security best practices to protect your Java application and the PostgreSQL database. This includes implementing authentication and authorization mechanisms with passwordless connections and leveraging Microsoft Defender for Cloud for threat detection and vulnerability assessments.\n\n * **Backup** your data: Azure Database for PostgreSQL provides automated backups by default. You can configure the retention period for backups based on your requirements. You can also enable geo-redundant backups, if needed, to enhance data durability and availability.", + "title": "Oracle database found", + "severity": "potential", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/postgresql", + "title": "Azure Database for PostgreSQL documentation" + }, + { + "url": "https://learn.microsoft.com/azure/postgresql/migrate/how-to-migrate-oracle-ora2pg", + "title": "Oracle to PostgreSQL migration guide" + }, + { + "url": "https://learn.microsoft.com/azure/dms", + "title": "Azure Database Migration Service documentation" + }, + { + "url": "https://learn.microsoft.com/azure/azure-monitor", + "title": "Azure Monitor documentation" + }, + { + "url": "https://learn.microsoft.com/azure/defender-for-cloud", + "title": "Microsoft Defender for Cloud" + } + ], + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=cloud-readiness", + "category=database-migration", + "database", + "oracle", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-01000": { + "id": "azure-java-version-01000", + "description": "The application is using a Java version that has reached the end of support. It is strongly recommended to plan and execute a migration strategy to upgrade your application to a supported Java version.\nSupported Java versions receive long-term support (LTS) from the Java community, including bug fixes and updates. Migrating to a supported version provides you with a stable and well-maintained platform for your application.", + "title": "Java Version Has Reached the End of Support", + "severity": "mandatory", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-java-version-02000": { + "id": "azure-java-version-02000", + "description": "The application is not using the latest LTS Java version. It is recommended to consider upgrading to the latest LTS version to take advantage of the newest language features, performance improvements, and extended support timelines.\nUpgrading to the latest LTS version ensures your application benefits from the most recent security enhancements and a longer support lifecycle.", + "title": "Java Version is not the latest LTS", + "severity": "optional", + "effort": 8, + "labels": [ + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "source", + "domain=java-upgrade", + "category=java-version-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "azure-password-01000": { + "id": "azure-password-01000", + "description": "Using clear passwords in property files is a security risk, as they can be easily compromised if the files are accessed by unauthorized individuals. To enhance the security of your application, it is recommended to employ secure credential management practices.\n\n * **Azure Key Vault**: Utilize Azure Key Vault to securely store and manage your application\u0027s passwords and other sensitive credentials. Azure Key Vault provides a centralized and highly secure location for storing secrets, keys, and certificates.\n\n * **Passwordless connections**: You can provide an additional layer of security and convenience for accessing resources in Azure by eliminating the need for passwords. This way you can reduce the risk of password-related vulnerabilities, such as weak passwords or password theft.", + "title": "Password found in configuration file", + "severity": "potential", + "effort": 3, + "links": [ + { + "url": "https://learn.microsoft.com/azure/key-vault", + "title": "Azure Key Vault documentation" + }, + { + "url": "https://learn.microsoft.com/azure/developer/intro/passwordless-overview", + "title": "Passwordless connections for Azure services" + }, + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#inventory-configuration-sources-and-secrets", + "title": "Password found in configuration file" + }, + { + "url": "https://docs.microsoft.com/azure/developer/java/spring-framework/configure-spring-boot-starter-java-app-with-azure-key-vault", + "title": "Read a secret from Azure Key Vault in a Spring Boot application" + }, + { + "url": "https://search.maven.org/artifact/com.azure.spring/azure-spring-boot-starter-keyvault-secrets", + "title": "Azure Spring Boot Starter for Azure Key Vault Secrets" + } + ], + "labels": [ + "source", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=local-credential", + "password", + "security", + "os=windows", + "os=linux" + ] + }, + "jakarta-database-00002": { + "id": "jakarta-database-00002", + "description": "The application depends on **Jakarta Persistence (JPA)** APIs (\u0060jakarta.persistence.*\u0060 or legacy \u0060javax.persistence.*\u0060), which are used for object-relational mapping (ORM) and database interaction in Jakarta EE or Java EE applications.\n\nWhen migrating to Azure:\n- Ensure that the database connection, JPA provider (e.g., Hibernate, EclipseLink), and dialect are compatible with your target Azure database service.\n- Recommended database services include **Azure Database for PostgreSQL**, **Azure Database for MySQL**, or **Azure SQL Database**.\n- For containerized deployments, these JPA-based applications can run on **Azure Kubernetes Service (AKS)** or **Azure App Service for Linux/Windows**.\n- If using **Spring Data JPA**, verify that connection pool settings and environment variables are properly configured for the cloud environment.\n- Consider leveraging **Azure Key Vault** for secure storage of database credentials and connection strings.", + "title": "Detects usage of Jakarta Persistence (JPA) APIs", + "severity": "potential", + "effort": 5, + "links": [ + { + "url": "https://jakarta.ee/specifications/persistence/", + "title": "Jakarta Persistence Specification" + }, + { + "url": "https://learn.microsoft.com/en-us/azure/architecture/guide/technology-choices/data-stores-getting-started#common-database-scenarios", + "title": "Prepare to choose a data store in Azure" + } + ], + "labels": [ + "source=java", + "source=java-ee", + "target=azure-aks", + "target=azure-container-apps", + "target=azure-appservice", + "domain=cloud-readiness", + "category=jakarta-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-port-01000": { + "id": "spring-boot-to-azure-port-01000", + "description": "The application is setting the server port. To migrate a Java application that sets the server port to Azure Container Apps:\n\n * **Azure Container Apps allows you to expose port according to your Azure Container Apps resource configuration. For instance, a Spring Boot application listens to port of 8080 by default, but it can be set with server.port or environment variable SERVER_PORT as you need.", + "title": "Server port configuration found", + "severity": "potential", + "effort": 1, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps#identify-any-clients-relying-on-a-non-standard-port", + "title": "Identify any clients relying on a non-standard port" + } + ], + "labels": [ + "source=springboot", + "target=azure-aks", + "target=azure-appservice", + "target=azure-container-apps", + "domain=cloud-readiness", + "category=spring-migration", + "port", + "server port", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-restricted-config-01000": { + "id": "spring-boot-to-azure-restricted-config-01000", + "description": "The application uses restricted configurations for Azure Container Apps.\n These properties can be automatically injected into your application environment by Azure Container Apps to access managed Config Server and managed Eureka Server.\n Please remove them from your application, including configuration files, config server files, command line parameters, Java system attributes, and environment variables.\n\n If configured in **configuration files**: they will be ignored and overrided by Azure Container Apps.\n \n If configured in **Config Server files**, **command line parameters**, **Java system attribute**, **environment variable**: they need to be removed or you might experience conflicts and unexpected behavior.", + "title": "Restricted configurations found", + "severity": "potential", + "effort": 2, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-cloud-to-azure-container-apps#remove-restricted-configurations", + "title": "Migrate Spring Boot applications to Azure Container Apps - Remove restricted configurations" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-config-server?tabs=azure-cli", + "title": "Connect to a managed Config Server for Spring in Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-eureka-server?tabs=azure-cli", + "title": "Connect to a managed Eureka Server for Spring in Azure Container Apps" + } + ], + "labels": [ + "target=azure-container-apps", + "source=springboot", + "domain=cloud-readiness", + "category=spring-migration", + "os=windows", + "os=linux" + ] + }, + "spring-boot-to-azure-spring-boot-version-01000": { + "id": "spring-boot-to-azure-spring-boot-version-01000", + "description": "The application is using a Spring Boot version that has reached its End of OSS Support.\nWith the officially supported new versions from Spring, you can get the best experience. Here are some steps you can take to update your application to the latest version of Spring Boot:\n\n* Choose a **supported Spring Boot version**: Check out Spring Boot Support Versions and determine the most suitable supported Spring Boot version.\n\n* **Update Spring Boot version**: Update the Spring Boot version of your application. There are automated tools like Rewrite to help you with the migration.\n\n* **Address code compatibility**: Review your application\u0027s codebase for any potential compatibility issues with the target Spring Boot version. Update deprecated APIs or features, address any language or library changes, and ensure that your code follows best practices and standards.\n\n* **Test thoroughly**: Execute a comprehensive testing process to verify the compatibility and functionality of your application with the new Spring Boot version. Perform unit tests, integration tests, and system tests to validate that all components and dependencies work as expected.", + "title": "Spring Boot Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://learn.microsoft.com/azure/developer/java/migration/migrate-spring-boot-to-azure-container-apps", + "title": "Migrate Spring Boot applications to Azure Container Apps" + }, + { + "url": "https://learn.microsoft.com/azure/container-apps/java-microservice-get-started?tabs=azure-cli", + "title": "Launch your first Java microservice application with managed Java components in Azure Container Apps" + }, + { + "url": "https://spring.io/projects/spring-boot/#support", + "title": "Spring Boot Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-boot/wiki/Supported-Versions", + "title": "Spring Boot Support Policy" + } + ], + "labels": [ + "source=springboot", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + }, + "spring-framework-version-01000": { + "id": "spring-framework-version-01000", + "description": "Your application is using a Spring Framework version that has reached its End of OSS Support.\nUpgrading to a supported version ensures better performance, security, and compatibility with modern tools.\n 1. Pick a Supported Version: Review the Spring Framework support policy and choose an actively supported version.\n 2. Update Your Project: Change the Spring Framework version in your pom.xml or build.gradle.\n 3. Fix Compatibility Issues: Update deprecated code, replace removed features, and ensure dependencies are compatible with the new Spring Framework version.\n 4. Thoroughly Test: Run unit, integration, and end-to-end tests to make sure everything still works after the upgrade.", + "title": "Spring Framework Version Has Reached the End of OSS Support", + "severity": "mandatory", + "effort": 8, + "links": [ + { + "url": "https://spring.io/projects/spring-framework#support", + "title": "Spring Framework Supported Versions" + }, + { + "url": "https://github.com/spring-projects/spring-framework/wiki/Spring-Framework-Versions", + "title": "Spring Framework Support Policy" + } + ], + "labels": [ + "source=spring", + "target=azure-appservice", + "target=azure-aks", + "target=azure-container-apps", + "domain=java-upgrade", + "category=framework-upgrade", + "version", + "os=windows", + "os=linux" + ] + } + }, + "rearchitect": [] +} \ No newline at end of file diff --git a/.github/modernize/assessment/reports/report-20261104093052/report.md b/.github/modernize/assessment/reports/report-20261104093052/report.md new file mode 100644 index 000000000..7d289b270 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20261104093052/report.md @@ -0,0 +1,148 @@ +# photo-album + +## Summary + +| Metric | Value | +|--------|-------| +| Total Issues | 10 | +| Mandatory Blockers | 3 | +| Potential Issues | 6 | + +## Component Information + +| Property | Value | +|----------|-------| +| Language | Java, JavaScript | +| Frameworks | Spring Boot, Spring | +| Build tools | Maven | +| JDK version | 1.8 | + +## Cloud Readiness Issues + +| Issue Name | Criticality | Story Points | Occurrences | +|------------|-------------|--------------|-------------| +| Oracle database found | Potential | 8 | [6](#Oracle_database_found) | +| Password found in configuration file | Potential | 3 | [4](#Password_found_in_configuration_file) | +| Server port configuration found | Potential | 1 | [2](#Server_port_configuration_found) | +| Restricted configurations found | Potential | 2 | [2](#Restricted_configurations_found) | +| Embedded framework - Spring Security | Potential | 5 | [1](#Embedded_framework_-_Spring_Security) | +| Detects usage of Jakarta Persistence (JPA) APIs | Potential | 5 | [1](#Detects_usage_of_Jakarta_Persistence_JPA_APIs) | + +### Issue Details + +
+Oracle database found — affected files + +- `pom.xml (line 52, col 0)` +- `docker-compose.yml (line 32, col 0)` +- `src/main/resources/application-docker.properties (line 3, col 0)` +- `src/main/resources/application.properties (line 12, col 0)` +- `src/main/resources/application.properties (line 15, col 0)` +- `src/main/resources/application-docker.properties (line 6, col 0)` + +
+ +
+Password found in configuration file — affected files + +- `src/test/resources/application-test.properties (line 5, col 0)` +- `src/test/resources/application-test.properties (line 23, col 0)` +- `src/main/resources/application-docker.properties (line 5, col 0)` +- `src/main/resources/application.properties (line 14, col 0)` + +
+ +
+Server port configuration found — affected files + +- `src/main/resources/application-docker.properties (line 25, col 0)` +- `src/main/resources/application.properties (line 2, col 0)` + +
+ +
+Restricted configurations found — affected files + +- `src/main/resources/application-docker.properties (line 25, col 0)` +- `src/main/resources/application.properties (line 2, col 0)` + +
+ +
+Embedded framework - Spring Security — affected files + +- `pom.xml (line 65, col 0)` + +
+ +
+Detects usage of Jakarta Persistence (JPA) APIs — affected files + +- `pom.xml (line 46, col 0)` + +
+ +## Upgrade Issues + +| Issue Name | Criticality | Story Points | Occurrences | +|------------|-------------|--------------|-------------| +| Spring Boot Version Has Reached the End of OSS Support | Mandatory | 8 | [8](#Spring_Boot_Version_Has_Reached_the_End_of_OSS_Support) | +| Spring Framework Version Has Reached the End of OSS Support | Mandatory | 8 | [4](#Spring_Framework_Version_Has_Reached_the_End_of_OSS_Support) | +| Java Version Has Reached the End of Support | Mandatory | 8 | [1](#Java_Version_Has_Reached_the_End_of_Support) | +| Java Version is not the latest LTS | Optional | 8 | [2](#Java_Version_is_not_the_latest_LTS) | + +### Issue Details + +
+Spring Boot Version Has Reached the End of OSS Support — affected files + +- `pom.xml (line 40, col 0)` +- `pom.xml (line 34, col 0)` +- `pom.xml (line 84, col 0)` +- `pom.xml (line 46, col 0)` +- `pom.xml (line 78, col 0)` +- `pom.xml (line 59, col 0)` +- `pom.xml (line 98, col 0)` +- `pom.xml (line 65, col 0)` + +
+ +
+Spring Framework Version Has Reached the End of OSS Support — affected files + +- `pom.xml (line 84, col 0)` +- `pom.xml (line 34, col 0)` +- `pom.xml (line 65, col 0)` +- `pom.xml (line 46, col 0)` + +
+ +
+Java Version Has Reached the End of Support — affected files + +- `pom.xml (line 24, col 0)` + +
+ +
+Java Version is not the latest LTS — affected files + +- `pom.xml (line 25, col 0)` +- `pom.xml (line 26, col 0)` + +
+ +--- + +## Codebase Insights + +> **Note:** These documents are generated by AI and may contain inaccuracies or incomplete information. Please review carefully. + +1. **[Architecture Diagram](facts/architecture-diagram.md)** — Understand the big picture: system layers and component relationships +2. **[Dependency Map](facts/dependency-map.md)** — Know what the project depends on and where the risks are +3. **[API & Service Contracts](facts/api-service-contracts.md)** — See how services communicate and what contracts they expose +4. **[Data Architecture](facts/data-architecture.md)** — Explore data models, storage, and data flow patterns +5. **[Configuration Inventory](facts/configuration-inventory.md)** — Review how the application is configured across environments +6. **[Business Workflows](facts/business-workflows.md)** — Trace end-to-end business processes and domain logic + +[Share feedback](https://aka.ms/ghcp-appmod/feedback)