diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/api-service-contracts.md b/.github/modernize/assessment/reports/report-20260521061104/facts/api-service-contracts.md new file mode 100644 index 000000000..a11b3f0c1 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/api-service-contracts.md @@ -0,0 +1,77 @@ +# API & Service Communication Contracts + +The application exposes a small HTTP surface made up of one gallery page, one upload endpoint, one image streaming endpoint, and detail/delete routes for individual photos. Communication is entirely synchronous: browser requests are handled by Spring MVC controllers, which call a local service and persist to Oracle through JPA. + +## Service Catalog + +| Service | Port | Category | Purpose | +|---|---:|---|---| +| `photoalbum-java-app` | 8080 | API Layer | Serves gallery/detail pages, accepts uploads, streams photo content, and deletes photos | +| `oracle-db` | 1521 | Infrastructure | Stores photo metadata and image BLOB data for the web application | + +## API Endpoints Inventory + +| Service | Method | Path | Request Type | Response Type | +|---|---|---|---|---| +| `photoalbum-java-app` | GET | `/` | None | Thymeleaf `index.html` view with `photos` model data | +| `photoalbum-java-app` | POST | `/upload` | Multipart form field `files` containing one or more `MultipartFile` objects | JSON `Map` with `success`, `uploadedPhotos`, and `failedUploads` | +| `photoalbum-java-app` | GET | `/detail/{id}` | Path parameter `id:String` | Thymeleaf `detail.html` view or redirect to `/` when not found | +| `photoalbum-java-app` | POST | `/detail/{id}/delete` | Path parameter `id:String` | Redirect to `/` with flash success/error message | +| `photoalbum-java-app` | GET | `/photo/{id}` | Path parameter `id:String` | `ResponseEntity` image stream with 200, 404, or 500 status | + +## Management & Observability Endpoints + +| Service | Endpoint | Custom Metrics | +|---|---|---| +| `photoalbum-java-app` | None detected | No Actuator, health, metrics, or Swagger endpoints detected | + +## DTOs & Contracts + +The application does not define dedicated request DTO classes for its HTTP surface. Upload requests are accepted as Spring `MultipartFile` collections, while server-rendered views bind directly to the `Photo` domain entity for display purposes. The only explicit contract helper class is `UploadResult`, which is used internally by the service layer to communicate per-file outcomes before the controller converts those results into an ad hoc JSON map. + +No gateway-level aggregation DTOs, OpenAPI specifications, protobuf schemas, or GraphQL schemas were found. JSON serialization relies on Spring Boot's default Jackson configuration through `spring-boot-starter-json`. + +## Communication Patterns + +All communication is synchronous. Browser clients call Spring MVC controllers over HTTP, controllers invoke `PhotoServiceImpl` through direct in-process method calls, and the service uses `PhotoRepository` for synchronous Oracle database access. No asynchronous messaging, background workers, service discovery, API gateway, client-side load balancing, retry policy, circuit breaker, or timeout library was detected. + +API availability depends on the Oracle database container being healthy before the web container starts; Docker Compose enforces this startup dependency with `depends_on` and a database health check. Security posture is minimal: no TLS termination, authentication, authorization rules, or Spring Security configuration were found, so all endpoints are publicly accessible within the deployment environment. + +## Service Technology Matrix + +| Service | Web | Data Access | Discovery | Gateway | Actuator | Cache | Metrics | +|---|---|---|---|---|---|---|---| +| `photoalbum-java-app` | Spring MVC + Thymeleaf | Spring Data JPA / Hibernate | None | None | None | None | None | +| `oracle-db` | N/A | Oracle database engine | None | None | Health check only | N/A | None | + +## Service Communication Sequence + +```mermaid +sequenceDiagram + participant Browser + participant Home as "HomeController" + participant Service as "PhotoServiceImpl" + participant Repo as "PhotoRepository" + participant DB as "Oracle Database" + + Browser->>Home: POST /upload (multipart files) + Home->>Service: uploadPhoto(file) + alt File passes validation + Service->>Service: Validate MIME type and size + Service->>Service: Read bytes and image dimensions + Service->>Repo: save(Photo) + Repo->>DB: INSERT photo metadata and BLOB + DB-->>Repo: Stored row + Repo-->>Service: Persisted Photo + Home->>Service: getPhotoById(photoId) + Service->>Repo: findById(photoId) + Repo->>DB: SELECT photo by id + DB-->>Repo: Photo row + Repo-->>Service: Photo + Service-->>Home: Upload success result + Home-->>Browser: 200 JSON with uploadedPhotos + else Validation or persistence failure + Service-->>Home: UploadResult failure + Home-->>Browser: 200 JSON with failedUploads or 400 for missing files + end +``` diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/architecture-diagram.md b/.github/modernize/assessment/reports/report-20260521061104/facts/architecture-diagram.md new file mode 100644 index 000000000..cc8654870 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/architecture-diagram.md @@ -0,0 +1,126 @@ +# Architecture Diagram + +This document summarizes the Photo Album application's runtime structure and the main component relationships that support photo upload, gallery browsing, image retrieval, and deletion. + +## Application Architecture + +```mermaid +flowchart TD + subgraph Client["Client Layer"] + Browser["Web Browser"] + end + + subgraph App["Application Layer - Spring Boot 2.7"] + Views["Thymeleaf Views"] + UploadJs["Upload JavaScript"] + HomeCtrl["HomeController"] + DetailCtrl["DetailController"] + FileCtrl["PhotoFileController"] + PhotoSvc["PhotoServiceImpl"] + end + + subgraph Data["Data Layer"] + PhotoRepo["Spring Data JPA Repository"] + OracleDB[("Oracle Database PHOTOS table")] + end + + subgraph External["External Services"] + BootstrapCdn["Bootstrap CDN"] + OracleContainer["Oracle DB Container"] + end + + Browser -->|"Loads gallery and detail pages"| Views + Browser -->|"Drag and drop uploads"| UploadJs + Views -->|"GET / and GET /detail/{id}"| HomeCtrl + Views -->|"Photo detail navigation and delete"| DetailCtrl + UploadJs -->|"POST /upload"| HomeCtrl + Browser -->|"GET /photo/{id}"| FileCtrl + HomeCtrl -->|"List and create photo records"| PhotoSvc + DetailCtrl -->|"Lookup, navigate, delete"| PhotoSvc + FileCtrl -->|"Load BLOB content"| PhotoSvc + PhotoSvc -->|"CRUD and custom queries"| PhotoRepo + PhotoRepo -->|"SQL and BLOB persistence"| OracleDB + OracleContainer -->|"Hosts"| OracleDB + Browser -->|"Fetches UI assets"| BootstrapCdn +``` + +### Technology Stack Summary + +| Layer | Technology | Version | Purpose | +|---|---|---|---| +| Presentation | Thymeleaf templates, Bootstrap, vanilla JavaScript | Thymeleaf via Spring Boot 2.7.18, Bootstrap 5.3.0 | Renders gallery/detail pages and handles drag-and-drop uploads | +| Web | Spring Boot Web, Spring MVC | 2.7.18 | Handles HTTP requests and server-side page composition | +| Business Logic | PhotoServiceImpl | Application code | Validates uploads, extracts metadata, coordinates persistence | +| Data Access | Spring Data JPA, Hibernate | Spring Boot managed | Maps the Photo entity to Oracle and executes repository queries | +| Storage | Oracle Database Free/XE | Container image latest / XE-compatible JDBC URL | Stores photo metadata and image BLOB data | +| Delivery | Docker, Docker Compose | Dockerfile + compose | Runs the web app and Oracle database as containers | + +### Data Storage & External Services + +The application stores all photo metadata and binary image content in a single Oracle database table named `PHOTOS`. Beyond the database, the only notable external dependency at runtime is the Bootstrap CDN used by the server-rendered UI for styling and client-side bundle delivery. + +### Key Architectural Decisions + +- Uses a classic layered Spring MVC design where controllers delegate all business logic and persistence orchestration to a single `PhotoServiceImpl` service. +- Persists uploaded images as Oracle BLOB data instead of filesystem storage, which keeps the application stateless from a file-hosting perspective. +- Uses server-rendered Thymeleaf pages for navigation while enhancing uploads with client-side JavaScript for drag-and-drop and immediate gallery updates. + +## Component Relationships + +```mermaid +flowchart LR + subgraph Presentation["Presentation"] + IndexView["index.html"] + DetailView["detail.html"] + UploadClient["upload.js"] + HomeControllerNode["HomeController"] + DetailControllerNode["DetailController"] + PhotoFileControllerNode["PhotoFileController"] + end + + subgraph Business["Business Logic"] + PhotoServiceNode["PhotoService"] + PhotoServiceImplNode["PhotoServiceImpl"] + UploadResultNode["UploadResult"] + end + + subgraph DataAccess["Data Access"] + PhotoRepoNode["PhotoRepository"] + PhotoEntityNode["Photo Entity"] + end + + subgraph Infra["Infrastructure"] + SpringBootNode["Spring Boot Runtime"] + TxNode["Transactional Boundary"] + OracleNode["Oracle Database"] + end + + IndexView -->|"submits uploads to"| UploadClient + IndexView -->|"binds model data from"| HomeControllerNode + DetailView -->|"binds model data from"| DetailControllerNode + UploadClient -->|"calls"| HomeControllerNode + HomeControllerNode -->|"delegates"| PhotoServiceNode + DetailControllerNode -->|"delegates"| PhotoServiceNode + PhotoFileControllerNode -->|"delegates"| PhotoServiceNode + PhotoServiceNode -->|"implemented by"| PhotoServiceImplNode + PhotoServiceImplNode -->|"returns"| UploadResultNode + PhotoServiceImplNode -->|"queries and saves"| PhotoRepoNode + PhotoRepoNode -->|"maps"| PhotoEntityNode + PhotoRepoNode -->|"executes against"| OracleNode + SpringBootNode -.->|"creates and wires"| Presentation + TxNode -.->|"wraps service methods"| PhotoServiceImplNode +``` + +### Component Inventory + +| Component | Layer | Type | Responsibility | +|---|---|---|---| +| `index.html` | Presentation | Thymeleaf template | Displays the gallery, upload area, and flash/error messages | +| `detail.html` | Presentation | Thymeleaf template | Shows a single photo, metadata, navigation, and delete action | +| `upload.js` | Presentation | Browser script | Validates dropped files, posts multipart uploads, and prepends new cards to the gallery | +| `HomeController` | Presentation | MVC controller | Handles gallery rendering and upload responses | +| `DetailController` | Presentation | MVC controller | Handles detail page rendering and delete operations | +| `PhotoFileController` | Presentation | MVC controller | Streams image bytes with cache-control headers | +| `PhotoServiceImpl` | Business Logic | Service | Applies upload rules, extracts image dimensions, and coordinates CRUD operations | +| `PhotoRepository` | Data Access | Spring Data repository | Executes Oracle-specific native SQL for ordered listing and navigation | +| `Photo` | Data Access | JPA entity | Represents persisted photo metadata and BLOB content | diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/assessment-overview.md b/.github/modernize/assessment/reports/report-20260521061104/facts/assessment-overview.md new file mode 100644 index 000000000..58bd3ba47 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/assessment-overview.md @@ -0,0 +1,10 @@ +# Assessment Overview + +This directory contains supplementary assessment documents generated from the repository analysis. Use the links below to navigate to the detailed fact files that were copied into this versioned report. + +- [architecture-diagram.md](architecture-diagram.md) — High-level application architecture and detailed component relationship diagrams. +- [dependency-map.md](dependency-map.md) — Declared build dependencies grouped by functional category, including test dependencies. +- [api-service-contracts.md](api-service-contracts.md) — Service catalog, endpoint inventory, communication patterns, and API sequence flow. +- [data-architecture.md](data-architecture.md) — Database configuration, entity model, repository methods, and data ownership boundaries. +- [configuration-inventory.md](configuration-inventory.md) — Configuration sources, runtime profiles, properties, secrets handling, and runtime settings. +- [business-workflows.md](business-workflows.md) — Core domain entities, user workflows, business rules, and workflow sequence diagram. diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/business-workflows.md b/.github/modernize/assessment/reports/report-20260521061104/facts/business-workflows.md new file mode 100644 index 000000000..cc3b663ef --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/business-workflows.md @@ -0,0 +1,91 @@ +# Core Business Workflows + +The Photo Album application supports a simple user journey: upload image files, browse the gallery, inspect photo details, and remove photos when no longer needed. Its business logic is concentrated around validating upload eligibility, preserving metadata, and presenting stored photos in a user-friendly order. + +## Domain Entities + +| Entity | Service / Bounded Context | Description | Key Relationships | +|---|---|---|---| +| `Photo` | Photo Management | Core business record representing a user-uploaded image plus its display metadata | Serves as the source of truth for gallery cards, detail views, navigation, and deletion | +| `UploadResult` | Photo Management | Transient workflow object describing whether an individual file upload succeeded or failed | Produced during upload processing and converted into the JSON response payload | + +## Service-to-Domain Mapping + +| Service | Domain Context | Owned Entities | External Dependencies | +|---|---|---|---| +| `photoalbum-java-app` | Photo Management | `Photo`, `UploadResult` | Oracle database for persistence; browser clients for UI-driven workflows | +| `oracle-db` | Persistence Infrastructure | Physical storage for `Photo` records | None beyond its role as the backing data store | + +## Primary Workflows + +### Workflow 1: Upload Photos + +A user drags photos into the gallery page or selects them through the file picker. The browser script performs a first pass of MIME type and file size validation, then posts the accepted files to `/upload`. The service layer validates each file again, rejects unsupported or empty uploads, extracts image dimensions when possible, assigns a UUID-based stored filename for compatibility, saves the photo bytes and metadata, and returns a per-file success or failure outcome. + +Business rules involved: +- Only JPEG, PNG, GIF, and WebP files are accepted. +- Each file must be non-empty and no larger than 10 MB. +- Upload success is reported per file so mixed-result batches can still partially succeed. +- Image dimension extraction is best-effort; failure to read dimensions does not block persistence. + +### Workflow 2: Browse Gallery and View Photo Details + +When a user opens `/`, the application retrieves all photos ordered by most recent upload time and renders them as gallery cards. Selecting a card opens `/detail/{id}`, where the application loads the chosen photo, computes previous/next navigation targets based on upload timestamps, and displays metadata such as file size, MIME type, and image dimensions. + +Business rules involved: +- Gallery ordering is newest-first. +- Missing photo identifiers gracefully redirect the user back to the gallery. +- Previous and next navigation is derived from upload time, not filename or insertion order. + +### Workflow 3: Delete a Photo + +From the detail page, a user can submit the delete action for a specific photo. The service checks whether the record exists, removes it from the database when present, and redirects the user back to the gallery with a success or error flash message. + +Business rules involved: +- Deletion only succeeds when the target photo exists. +- The UI always returns the user to the gallery after the operation. +- User confirmation is required in the browser before the delete form is submitted. + +## Cross-Service Data Flows + +There is no multi-service business composition layer in this application. Business data flows from the browser to the Spring Boot application and then to Oracle, with the same service acting as the source of truth for validation, persistence, and presentation. When failures occur, the application degrades in business-visible ways: upload batches return per-file error messages, missing detail records redirect to the gallery, and delete attempts surface a user-facing error message instead of throwing a visible exception. + +## Business Workflow Sequence + +```mermaid +sequenceDiagram + participant User + participant Browser as "Gallery UI" + participant Home as "HomeController" + participant Service as "PhotoServiceImpl" + participant Repo as "PhotoRepository" + participant DB as "Oracle Database" + + User->>Browser: Drop image files into upload zone + Browser->>Browser: Validate file type and size client-side + Browser->>Home: Submit accepted files to /upload + Home->>Service: Process each file + Service->>Service: Enforce upload rules and extract metadata + alt File is valid + Service->>Repo: Save Photo record + Repo->>DB: Persist metadata and image BLOB + DB-->>Repo: Stored photo + Repo-->>Service: Persisted entity + Service-->>Home: Success with photo id + Home-->>Browser: JSON response with uploaded photo summary + Browser-->>User: Show success and prepend new gallery card + else File is invalid or save fails + Service-->>Home: Failure result with error message + Home-->>Browser: JSON response with failedUploads entry + Browser-->>User: Show upload error message + end +``` + +## Business Rules & Decision Logic + +- **Validation rules:** The application accepts only JPEG, PNG, GIF, and WebP uploads, enforces a 10 MB per-file limit, and rejects empty files. +- **Decision logic:** Upload processing follows a per-file success/failure model so one bad file does not block the entire batch. +- **Computed values:** The service derives image width and height from image bytes when available, and the UI computes human-readable file size displays and formatted upload timestamps. +- **State transitions:** A `Photo` record moves from not persisted to persisted on upload and from persisted to removed on delete; there are no intermediate approval states. +- **Transactions:** `PhotoServiceImpl` is annotated with `@Transactional`, so persistence operations run inside Spring-managed transaction boundaries. +- **Error handling and authorization:** The application logs operational failures and converts them into redirects or structured upload errors. No business-level authorization rules or role checks were detected. diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/configuration-inventory.md b/.github/modernize/assessment/reports/report-20260521061104/facts/configuration-inventory.md new file mode 100644 index 000000000..ca5ec1136 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/configuration-inventory.md @@ -0,0 +1,114 @@ +# Configuration & Externalized Settings Inventory + +The application uses a small set of local configuration sources: Spring property files, Docker Compose service definitions, Docker image settings, and Oracle initialization scripts. Configuration is straightforward, but secrets are stored inline in repository-managed files rather than external secret stores. + +## Configuration Sources + +| Source | Type | Path/Location | Notes | +|---|---|---|---| +| Spring application properties | Runtime config | `src/main/resources/application.properties` | Default server, datasource, JPA, upload, and logging settings | +| Spring profile properties | Runtime config | `src/main/resources/application-docker.properties` | Docker-specific Oracle connection and logging overrides | +| Docker Compose | Container orchestration config | `docker-compose.yml` | Defines web and Oracle services, environment variables, networking, and startup order | +| Dockerfile | Image/runtime config | `Dockerfile` | Defines build image, runtime image, JVM options, and exposed port | +| Maven build file | Build config | `pom.xml` | Declares framework versions and dependencies | +| Oracle init SQL | Database bootstrap config | `oracle-init/*.sql` | Creates database user, verifies privileges, and supports health checks | +| Test annotation config | Test runtime signal | `src/test/java/com/photoalbum/PhotoAlbumApplicationTests.java` | Activates the `test` Spring profile during context-load testing | + +## Build Profiles + +| Profile | Activation | Purpose | Key Dependencies/Plugins | +|---|---|---|---| +| Default Maven build | Automatic | Standard packaging of the Spring Boot jar | `spring-boot-maven-plugin`; no explicit Maven `` detected | + +## Runtime Profiles + +| Profile | Activation Method | Config Files | Key Overrides | +|---|---|---|---| +| `default` | Automatic when no profile is set | `application.properties` | Oracle `FREEPDB1` JDBC URL, debug logging for app and Spring Web, upload limits | +| `docker` | `SPRING_PROFILES_ACTIVE=docker` in Docker Compose | `application.properties`, `application-docker.properties` | Oracle XE JDBC URL, lower web logging verbosity, container-oriented datasource host | +| `test` | `@ActiveProfiles("test")` on test class | Base properties plus Spring Boot test defaults | Uses test-scoped H2 dependency; no dedicated `application-test.properties` file detected | + +## Properties Inventory + +### photoalbum-java-app + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `server.port` | `8080` | default, docker | Spring properties files | +| `server.servlet.encoding.charset` | `UTF-8` | default, docker | Spring properties files | +| `server.servlet.encoding.enabled` | `true` | default, docker | Spring properties files | +| `server.servlet.encoding.force` | `true` | default, docker | Spring properties files | +| `spring.datasource.url` | Oracle JDBC URL | default, docker | Spring properties files | +| `spring.datasource.username` | `photoalbum` | default, docker | Spring properties files | +| `spring.datasource.password` | `[MASKED]` | default, docker | Spring properties files | +| `spring.datasource.driver-class-name` | `oracle.jdbc.OracleDriver` | default, docker | Spring properties files | +| `spring.jpa.database-platform` | `org.hibernate.dialect.OracleDialect` | default, docker | Spring properties files | +| `spring.jpa.hibernate.ddl-auto` | `create` | default, docker | Spring properties files | +| `spring.jpa.show-sql` | `true` | default, docker | Spring properties files | +| `spring.jpa.properties.hibernate.format_sql` | `true` | default, docker | Spring properties files | +| `spring.servlet.multipart.max-file-size` | `10MB` | default, docker | Spring properties files | +| `spring.servlet.multipart.max-request-size` | `50MB` | default, docker | Spring properties files | +| `app.file-upload.max-file-size-bytes` | `10485760` | default, docker | Spring properties files | +| `app.file-upload.allowed-mime-types` | `image/jpeg,image/png,image/gif,image/webp` | default, docker | Spring properties files | +| `app.file-upload.max-files-per-upload` | `10` | default, docker | Spring properties files | +| `logging.level.com.photoalbum` | `DEBUG` or `INFO` | default, docker | Spring properties files | +| `logging.level.org.springframework.web` | `DEBUG` or `WARN` | default, docker | Spring properties files | +| `logging.level.org.hibernate.SQL` | `DEBUG` | docker only | `application-docker.properties` | + +### docker-compose service environment overrides + +| Property Key | Default | Profiles | Source | +|---|---|---|---| +| `SPRING_PROFILES_ACTIVE` | `docker` | docker | `docker-compose.yml` | +| `SPRING_DATASOURCE_URL` | Oracle JDBC URL | docker | `docker-compose.yml` | +| `SPRING_DATASOURCE_USERNAME` | `photoalbum` | docker | `docker-compose.yml` | +| `SPRING_DATASOURCE_PASSWORD` | `[MASKED]` | docker | `docker-compose.yml` | +| `ORACLE_PASSWORD` | `[MASKED]` | docker | `docker-compose.yml` | +| `APP_USER` | `photoalbum` | docker | `docker-compose.yml` | +| `APP_USER_PASSWORD` | `[MASKED]` | docker | `docker-compose.yml` | + +## Startup Parameters & Resource Requirements + +| Service | JVM/Runtime Options | Memory | Instance Count | +|---|---|---|---:| +| `photoalbum-java-app` | `JAVA_OPTS="-Xmx512m -Xms256m"`; exposes port 8080 | 256 MB initial heap, 512 MB max heap | 1 container in Compose | +| `oracle-db` | No JVM options; Oracle container health check script | No explicit memory limit configured | 1 container in Compose | + +## Startup Dependency Chain + +1. `oracle-db` starts first and initializes the database user via scripts mounted from `oracle-init/`. +2. Docker Compose waits for the Oracle container health check (`healthcheck.sh`) to report healthy. +3. `photoalbum-java-app` starts only after `oracle-db` is healthy because of `depends_on.condition: service_healthy`. +4. The Spring Boot application then connects to Oracle using the active datasource settings and creates the schema on startup. + +## Secrets & Sensitive Configuration + +| Secret Reference | Type | Storage (masked) | +|---|---|---| +| `spring.datasource.password` | Database password | Inline Spring properties value masked in report | +| `SPRING_DATASOURCE_PASSWORD` | Database password | Docker Compose environment variable masked in report | +| `ORACLE_PASSWORD` | Oracle system/admin password | Docker Compose environment variable masked in report | +| `APP_USER_PASSWORD` | Oracle application user password | Docker Compose environment variable masked in report | + +### Secrets Provisioning Workflow + +Secrets are provisioned locally and statically. Source values are checked into repository-managed Spring properties and Docker Compose environment variables, then injected directly into the application container and Oracle container at startup. No managed identity, external vault, RBAC-governed secret store, or deployment-time secret binding workflow was detected. + +## Feature Flags + +| Flag Name | Default | Controlled By | +|---|---|---| +| None detected | N/A | No `@ConditionalOnProperty`, feature flag framework, or toggle file detected | + +## Framework & Runtime Versions + +| Component | Version | Source | +|---|---|---| +| Spring Boot | `2.7.18` | `pom.xml` parent POM | +| Java target | `1.8` | `pom.xml` properties | +| Maven build image | `3.9.6-eclipse-temurin-8` | `Dockerfile` | +| Java runtime image | `eclipse-temurin:8-jre` | `Dockerfile` | +| Bootstrap | `5.3.0` | CDN reference in Thymeleaf templates | +| Oracle JDBC | `ojdbc8` (managed) | `pom.xml` dependency | +| Oracle database container | `gvenzl/oracle-free:latest` | `docker-compose.yml` | +| Spring Data JPA / Hibernate | Spring Boot managed | `pom.xml` dependency set | diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/data-architecture.md b/.github/modernize/assessment/reports/report-20260521061104/facts/data-architecture.md new file mode 100644 index 000000000..1466ea917 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/data-architecture.md @@ -0,0 +1,63 @@ +# Data Architecture & Persistence Layer + +The data layer is intentionally small: a single JPA entity is stored in Oracle using Spring Data JPA and Hibernate. All persistence concerns are concentrated in one repository interface, with no caching layer or separate reporting store. + +## Database Configuration + +| Service/Module | DB Type | Profile | Driver | Connection | Migration Tool | +|---|---|---|---|---|---| +| `photoalbum-java-app` | Oracle Database | default | Oracle JDBC (`ojdbc8`) | JDBC URL points to `oracle-db:1521/FREEPDB1` | None; Hibernate creates schema at startup | +| `photoalbum-java-app` | Oracle Database XE | `docker` | Oracle JDBC (`ojdbc8`) | JDBC URL points to `oracle-db:1521:XE` | None; Hibernate creates schema at startup | +| `photoalbum-java-app` tests | H2 in-memory database | `test` | H2 | Spring Boot test auto-configuration with `@ActiveProfiles("test")` | None detected | + +## Data Ownership per Service + +| Service | Tables Owned | ORM Framework | Caching | Notes | +|---|---|---|---|---| +| `photoalbum-java-app` | `PHOTOS` | JPA / Hibernate via Spring Data JPA | None detected | Single-service ownership; stores both metadata and binary photo content | +| `oracle-db` | `PHOTOS` physical storage | Oracle engine | N/A | Backing store only; no independent domain ownership | + +## Entity Model + +```mermaid +erDiagram + Photo { + string id PK + string originalFileName + bytes photoData + string storedFileName + string filePath + long fileSize + string mimeType + datetime uploadedAt + int width + int height + } +``` + +The source entity model is defined in `src/main/java/com/photoalbum/model/Photo.java`. No JPA relationships to other entities were found; the model is a self-contained aggregate for photo content and metadata. + +## Key Repository Methods + +| Service | Repository | Notable Methods | Purpose | +|---|---|---|---| +| `photoalbum-java-app` | `PhotoRepository` (`src/main/java/com/photoalbum/repository/PhotoRepository.java`) | `findAllOrderByUploadedAtDesc()` | Returns gallery items ordered newest-first | +| `photoalbum-java-app` | `PhotoRepository` | `findPhotosUploadedBefore(LocalDateTime)` | Finds older photos for previous-photo navigation | +| `photoalbum-java-app` | `PhotoRepository` | `findPhotosUploadedAfter(LocalDateTime)` | Finds newer photos for next-photo navigation | +| `photoalbum-java-app` | `PhotoRepository` | `findPhotosByUploadMonth(String, String)` | Oracle-specific month-based filtering helper | +| `photoalbum-java-app` | `PhotoRepository` | `findPhotosWithPagination(int, int)` | Uses Oracle `ROWNUM` pagination for ordered slicing | +| `photoalbum-java-app` | `PhotoRepository` | `findPhotosWithStatistics()` | Uses Oracle analytical functions for ranking and running totals | + +## Caching Strategy + +No application-level caching was detected. There are no `@Cacheable` annotations, Spring Cache configuration classes, Redis dependencies, or Hibernate second-level cache settings in the repository. Every gallery, detail, and image request ultimately reads from Oracle, while uploaded image responses add explicit no-cache headers to avoid stale browser content. + +## Data Ownership Boundaries + +The application uses a single shared Oracle database that is owned and accessed by one Spring Boot service. There is no database-per-service split, no CQRS read model, and no cross-service access pattern beyond the web application talking directly to its own database. Transaction management is centralized in `PhotoServiceImpl`, which is annotated with `@Transactional` so upload, lookup, and delete operations run inside Spring-managed transaction boundaries. + +### Data Classification & Sensitivity + +| Entity | Sensitive Fields | Classification (PII/PHI/PCI/None) | Controls in Place | +|---|---|---|---| +| `Photo` | `originalFileName`, `photoData` | PII (potential user-generated personal content) | No explicit masking, field-level access control, or application-managed encryption detected | diff --git a/.github/modernize/assessment/reports/report-20260521061104/facts/dependency-map.md b/.github/modernize/assessment/reports/report-20260521061104/facts/dependency-map.md new file mode 100644 index 000000000..fff7e6e5d --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/facts/dependency-map.md @@ -0,0 +1,69 @@ +# Dependency Map + +This project is a Spring Boot Maven application with 8 declared non-test dependencies plus 2 test-scoped dependencies. The dependency set is compact and centered around web delivery, JPA-based persistence, Oracle connectivity, and a small number of utility libraries. + +## Dependencies + +```mermaid +flowchart LR + App["photo-album application"] + Parent["spring-boot-starter-parent 2.7.18"] + + subgraph Web["Web Frameworks"] + WebStarter["spring-boot-starter-web 2.7.18"] + Thymeleaf["spring-boot-starter-thymeleaf 2.7.18"] + end + + subgraph Db["Database and ORM"] + Jpa["spring-boot-starter-data-jpa 2.7.18"] + OracleJdbc["ojdbc8 managed runtime"] + end + + subgraph Util["Utilities"] + Validation["spring-boot-starter-validation 2.7.18"] + Json["spring-boot-starter-json 2.7.18"] + CommonsIo["commons-io 2.11.0"] + Devtools["spring-boot-devtools 2.7.18 optional"] + end + + App -->|"web"| Web + App -->|"persistence"| Db + App -->|"support"| Util + Parent -.->|"manages versions"| WebStarter + Parent -.->|"manages versions"| Thymeleaf + Parent -.->|"manages versions"| Jpa + Parent -.->|"manages versions"| Validation + Parent -.->|"manages versions"| Json + Parent -.->|"manages versions"| Devtools + Parent -.->|"manages BOM for"| OracleJdbc +``` + +### Dependency Summary + +| Category | Count | Key Libraries | Notes | +|---|---:|---|---| +| Web Frameworks | 2 | Spring Boot Web, Spring Boot Thymeleaf | Server-rendered MVC UI with template rendering | +| Database / ORM | 2 | Spring Data JPA, ojdbc8 | Hibernate-backed persistence against Oracle | +| Utilities | 4 | Validation, JSON, Commons IO, DevTools | Validation, JSON serialization, file helpers, and development reload support | + +### Version & Compatibility Risks + +The dependency set is tied to Spring Boot 2.7.18 and Java 8, both of which are older baselines for Azure modernization scenarios and are already highlighted by the generated AppCAT report. The Oracle JDBC runtime dependency also keeps the application closely coupled to Oracle-specific behavior, while the JPA repository contains native Oracle SQL that may complicate migration to other managed data platforms. + +### Notable Observations + +- The Spring Boot parent POM centrally manages nearly all starter versions, so framework upgrades will have broad impact across web, JPA, validation, and JSON support. +- `commons-io` is the only explicitly version-pinned third-party utility outside the Spring Boot managed dependency set. +- The application declares Oracle JDBC only at runtime scope, which is appropriate for deployment but still creates a hard production dependency on Oracle drivers. +- No dedicated messaging, caching, security, or observability libraries are declared in the build. + +## Test Dependencies + +| Framework | Version | Notes | +|---|---|---| +| `spring-boot-starter-test` | 2.7.18 | Provides JUnit 5 and the default Spring Boot test stack | +| `h2` | Spring Boot managed | In-memory database dependency used for tests | + +Total test-scope dependencies: 2 + +The test toolchain is minimal and focused on application context startup validation. No dedicated integration-test libraries, containerized test dependencies, or contract-testing frameworks are declared. diff --git a/.github/modernize/assessment/reports/report-20260521061104/report.html b/.github/modernize/assessment/reports/report-20260521061104/report.html new file mode 100644 index 000000000..fd8ab3a9e --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/report.html @@ -0,0 +1,1378 @@ + + + +Assessment - photo-album + + + + + +
+

photo-album

+
+

Application Information

+
+
+
+
Application Namephoto-album
+
Java Version1.8
+
EffortL (total story points: 51)
+
+
+
Build ToolsMaven
+
FrameworksSpring Boot, Spring
+
+
+
+
+ +
+
+ + +
+
+

Issue Summary

+
+
+
+ + + + +

Cloud Readiness

+
5 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
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

+

This document summarizes the Photo Album application's runtime structure and the main component relationships that support photo upload, gallery browsing, image retrieval, and deletion.

+

Application Architecture

+
+flowchart TD + subgraph Client["Client Layer"] + Browser["Web Browser"] + end + + subgraph App["Application Layer - Spring Boot 2.7"] + Views["Thymeleaf Views"] + UploadJs["Upload JavaScript"] + HomeCtrl["HomeController"] + DetailCtrl["DetailController"] + FileCtrl["PhotoFileController"] + PhotoSvc["PhotoServiceImpl"] + end + + subgraph Data["Data Layer"] + PhotoRepo["Spring Data JPA Repository"] + OracleDB[("Oracle Database PHOTOS table")] + end + + subgraph External["External Services"] + BootstrapCdn["Bootstrap CDN"] + OracleContainer["Oracle DB Container"] + end + + Browser -->|"Loads gallery and detail pages"| Views + Browser -->|"Drag and drop uploads"| UploadJs + Views -->|"GET / and GET /detail/{id}"| HomeCtrl + Views -->|"Photo detail navigation and delete"| DetailCtrl + UploadJs -->|"POST /upload"| HomeCtrl + Browser -->|"GET /photo/{id}"| FileCtrl + HomeCtrl -->|"List and create photo records"| PhotoSvc + DetailCtrl -->|"Lookup, navigate, delete"| PhotoSvc + FileCtrl -->|"Load BLOB content"| PhotoSvc + PhotoSvc -->|"CRUD and custom queries"| PhotoRepo + PhotoRepo -->|"SQL and BLOB persistence"| OracleDB + OracleContainer -->|"Hosts"| OracleDB + Browser -->|"Fetches UI assets"| BootstrapCdn +
+

Technology Stack Summary

+
+ + + + + + + + +
LayerTechnologyVersionPurpose
PresentationThymeleaf templates, Bootstrap, vanilla JavaScriptThymeleaf via Spring Boot 2.7.18, Bootstrap 5.3.0Renders gallery/detail pages and handles drag-and-drop uploads
WebSpring Boot Web, Spring MVC2.7.18Handles HTTP requests and server-side page composition
Business LogicPhotoServiceImplApplication codeValidates uploads, extracts metadata, coordinates persistence
Data AccessSpring Data JPA, HibernateSpring Boot managedMaps the Photo entity to Oracle and executes repository queries
StorageOracle Database Free/XEContainer image latest / XE-compatible JDBC URLStores photo metadata and image BLOB data
DeliveryDocker, Docker ComposeDockerfile + composeRuns the web app and Oracle database as containers
+

Data Storage & External Services

+

The application stores all photo metadata and binary image content in a single Oracle database table named PHOTOS. Beyond the database, the only notable external dependency at runtime is the Bootstrap CDN used by the server-rendered UI for styling and client-side bundle delivery.

+

Key Architectural Decisions

+
    +
  • Uses a classic layered Spring MVC design where controllers delegate all business logic and persistence orchestration to a single PhotoServiceImpl service.
  • +
  • Persists uploaded images as Oracle BLOB data instead of filesystem storage, which keeps the application stateless from a file-hosting perspective.
  • +
  • Uses server-rendered Thymeleaf pages for navigation while enhancing uploads with client-side JavaScript for drag-and-drop and immediate gallery updates.
  • +
+

Component Relationships

+
+flowchart LR + subgraph Presentation["Presentation"] + IndexView["index.html"] + DetailView["detail.html"] + UploadClient["upload.js"] + HomeControllerNode["HomeController"] + DetailControllerNode["DetailController"] + PhotoFileControllerNode["PhotoFileController"] + end + + subgraph Business["Business Logic"] + PhotoServiceNode["PhotoService"] + PhotoServiceImplNode["PhotoServiceImpl"] + UploadResultNode["UploadResult"] + end + + subgraph DataAccess["Data Access"] + PhotoRepoNode["PhotoRepository"] + PhotoEntityNode["Photo Entity"] + end + + subgraph Infra["Infrastructure"] + SpringBootNode["Spring Boot Runtime"] + TxNode["Transactional Boundary"] + OracleNode["Oracle Database"] + end + + IndexView -->|"submits uploads to"| UploadClient + IndexView -->|"binds model data from"| HomeControllerNode + DetailView -->|"binds model data from"| DetailControllerNode + UploadClient -->|"calls"| HomeControllerNode + HomeControllerNode -->|"delegates"| PhotoServiceNode + DetailControllerNode -->|"delegates"| PhotoServiceNode + PhotoFileControllerNode -->|"delegates"| PhotoServiceNode + PhotoServiceNode -->|"implemented by"| PhotoServiceImplNode + PhotoServiceImplNode -->|"returns"| UploadResultNode + PhotoServiceImplNode -->|"queries and saves"| PhotoRepoNode + PhotoRepoNode -->|"maps"| PhotoEntityNode + PhotoRepoNode -->|"executes against"| OracleNode + SpringBootNode -.->|"creates and wires"| Presentation + TxNode -.->|"wraps service methods"| PhotoServiceImplNode +
+

Component Inventory

+
+ + + + + + + + + + + +
ComponentLayerTypeResponsibility
index.htmlPresentationThymeleaf templateDisplays the gallery, upload area, and flash/error messages
detail.htmlPresentationThymeleaf templateShows a single photo, metadata, navigation, and delete action
upload.jsPresentationBrowser scriptValidates dropped files, posts multipart uploads, and prepends new cards to the gallery
HomeControllerPresentationMVC controllerHandles gallery rendering and upload responses
DetailControllerPresentationMVC controllerHandles detail page rendering and delete operations
PhotoFileControllerPresentationMVC controllerStreams image bytes with cache-control headers
PhotoServiceImplBusiness LogicServiceApplies upload rules, extracts image dimensions, and coordinates CRUD operations
PhotoRepositoryData AccessSpring Data repositoryExecutes Oracle-specific native SQL for ordered listing and navigation
PhotoData AccessJPA entityRepresents persisted photo metadata and BLOB content
+
+
+

API & Service Communication Contracts

+

The application exposes a small HTTP surface made up of one gallery page, one upload endpoint, one image streaming endpoint, and detail/delete routes for individual photos. Communication is entirely synchronous: browser requests are handled by Spring MVC controllers, which call a local service and persist to Oracle through JPA.

+

Service Catalog

+
+ + + + +
ServicePortCategoryPurpose
photoalbum-java-app8080API LayerServes gallery/detail pages, accepts uploads, streams photo content, and deletes photos
oracle-db1521InfrastructureStores photo metadata and image BLOB data for the web application
+

API Endpoints Inventory

+
+ + + + + + + +
ServiceMethodPathRequest TypeResponse Type
photoalbum-java-appGET/NoneThymeleaf index.html view with photos model data
photoalbum-java-appPOST/uploadMultipart form field files containing one or more MultipartFile objectsJSON Map with success, uploadedPhotos, and failedUploads
photoalbum-java-appGET/detail/{id}Path parameter id:StringThymeleaf detail.html view or redirect to / when not found
photoalbum-java-appPOST/detail/{id}/deletePath parameter id:StringRedirect to / with flash success/error message
photoalbum-java-appGET/photo/{id}Path parameter id:StringResponseEntity image stream with 200, 404, or 500 status
+

Management & Observability Endpoints

+
+ + + +
ServiceEndpointCustom Metrics
photoalbum-java-appNone detectedNo Actuator, health, metrics, or Swagger endpoints detected
+

DTOs & Contracts

+

The application does not define dedicated request DTO classes for its HTTP surface. Upload requests are accepted as Spring MultipartFile collections, while server-rendered views bind directly to the Photo domain entity for display purposes. The only explicit contract helper class is UploadResult, which is used internally by the service layer to communicate per-file outcomes before the controller converts those results into an ad hoc JSON map.

+

No gateway-level aggregation DTOs, OpenAPI specifications, protobuf schemas, or GraphQL schemas were found. JSON serialization relies on Spring Boot's default Jackson configuration through spring-boot-starter-json.

+

Communication Patterns

+

All communication is synchronous. Browser clients call Spring MVC controllers over HTTP, controllers invoke PhotoServiceImpl through direct in-process method calls, and the service uses PhotoRepository for synchronous Oracle database access. No asynchronous messaging, background workers, service discovery, API gateway, client-side load balancing, retry policy, circuit breaker, or timeout library was detected.

+

API availability depends on the Oracle database container being healthy before the web container starts; Docker Compose enforces this startup dependency with depends_on and a database health check. Security posture is minimal: no TLS termination, authentication, authorization rules, or Spring Security configuration were found, so all endpoints are publicly accessible within the deployment environment.

+

Service Technology Matrix

+
+ + + + +
ServiceWebData AccessDiscoveryGatewayActuatorCacheMetrics
photoalbum-java-appSpring MVC + ThymeleafSpring Data JPA / HibernateNoneNoneNoneNoneNone
oracle-dbN/AOracle database engineNoneNoneHealth check onlyN/ANone
+

Service Communication Sequence

+
+sequenceDiagram + participant Browser + participant Home as "HomeController" + participant Service as "PhotoServiceImpl" + participant Repo as "PhotoRepository" + participant DB as "Oracle Database" + + Browser->>Home: POST /upload (multipart files) + Home->>Service: uploadPhoto(file) + alt File passes validation + Service->>Service: Validate MIME type and size + Service->>Service: Read bytes and image dimensions + Service->>Repo: save(Photo) + Repo->>DB: INSERT photo metadata and BLOB + DB-->>Repo: Stored row + Repo-->>Service: Persisted Photo + Home->>Service: getPhotoById(photoId) + Service->>Repo: findById(photoId) + Repo->>DB: SELECT photo by id + DB-->>Repo: Photo row + Repo-->>Service: Photo + Service-->>Home: Upload success result + Home-->>Browser: 200 JSON with uploadedPhotos + else Validation or persistence failure + Service-->>Home: UploadResult failure + Home-->>Browser: 200 JSON with failedUploads or 400 for missing files + end +
+
+
+

Configuration & Externalized Settings Inventory

+

The application uses a small set of local configuration sources: Spring property files, Docker Compose service definitions, Docker image settings, and Oracle initialization scripts. Configuration is straightforward, but secrets are stored inline in repository-managed files rather than external secret stores.

+

Configuration Sources

+
+ + + + + + + + + +
SourceTypePath/LocationNotes
Spring application propertiesRuntime configsrc/main/resources/application.propertiesDefault server, datasource, JPA, upload, and logging settings
Spring profile propertiesRuntime configsrc/main/resources/application-docker.propertiesDocker-specific Oracle connection and logging overrides
Docker ComposeContainer orchestration configdocker-compose.ymlDefines web and Oracle services, environment variables, networking, and startup order
DockerfileImage/runtime configDockerfileDefines build image, runtime image, JVM options, and exposed port
Maven build fileBuild configpom.xmlDeclares framework versions and dependencies
Oracle init SQLDatabase bootstrap configoracle-init/*.sqlCreates database user, verifies privileges, and supports health checks
Test annotation configTest runtime signalsrc/test/java/com/photoalbum/PhotoAlbumApplicationTests.javaActivates the test Spring profile during context-load testing
+

Build Profiles

+
+ + + +
ProfileActivationPurposeKey Dependencies/Plugins
Default Maven buildAutomaticStandard packaging of the Spring Boot jarspring-boot-maven-plugin; no explicit Maven detected
+

Runtime Profiles

+
+ + + + + +
ProfileActivation MethodConfig FilesKey Overrides
defaultAutomatic when no profile is setapplication.propertiesOracle FREEPDB1 JDBC URL, debug logging for app and Spring Web, upload limits
dockerSPRING_PROFILES_ACTIVE=docker in Docker Composeapplication.properties, application-docker.propertiesOracle XE JDBC URL, lower web logging verbosity, container-oriented datasource host
test@ActiveProfiles("test") on test classBase properties plus Spring Boot test defaultsUses test-scoped H2 dependency; no dedicated application-test.properties file detected
+

Properties Inventory

+

photoalbum-java-app

+
+ + + + + + + + + + + + + + + + + + + + + + +
Property KeyDefaultProfilesSource
server.port8080default, dockerSpring properties files
server.servlet.encoding.charsetUTF-8default, dockerSpring properties files
server.servlet.encoding.enabledtruedefault, dockerSpring properties files
server.servlet.encoding.forcetruedefault, dockerSpring properties files
spring.datasource.urlOracle JDBC URLdefault, dockerSpring properties files
spring.datasource.usernamephotoalbumdefault, dockerSpring properties files
spring.datasource.password[MASKED]default, dockerSpring properties files
spring.datasource.driver-class-nameoracle.jdbc.OracleDriverdefault, dockerSpring properties files
spring.jpa.database-platformorg.hibernate.dialect.OracleDialectdefault, dockerSpring properties files
spring.jpa.hibernate.ddl-autocreatedefault, dockerSpring properties files
spring.jpa.show-sqltruedefault, dockerSpring properties files
spring.jpa.properties.hibernate.format_sqltruedefault, dockerSpring properties files
spring.servlet.multipart.max-file-size10MBdefault, dockerSpring properties files
spring.servlet.multipart.max-request-size50MBdefault, dockerSpring properties files
app.file-upload.max-file-size-bytes10485760default, dockerSpring properties files
app.file-upload.allowed-mime-typesimage/jpeg,image/png,image/gif,image/webpdefault, dockerSpring properties files
app.file-upload.max-files-per-upload10default, dockerSpring properties files
logging.level.com.photoalbumDEBUG or INFOdefault, dockerSpring properties files
logging.level.org.springframework.webDEBUG or WARNdefault, dockerSpring properties files
logging.level.org.hibernate.SQLDEBUGdocker onlyapplication-docker.properties
+

docker-compose service environment overrides

+
+ + + + + + + + + +
Property KeyDefaultProfilesSource
SPRING_PROFILES_ACTIVEdockerdockerdocker-compose.yml
SPRING_DATASOURCE_URLOracle JDBC URLdockerdocker-compose.yml
SPRING_DATASOURCE_USERNAMEphotoalbumdockerdocker-compose.yml
SPRING_DATASOURCE_PASSWORD[MASKED]dockerdocker-compose.yml
ORACLE_PASSWORD[MASKED]dockerdocker-compose.yml
APP_USERphotoalbumdockerdocker-compose.yml
APP_USER_PASSWORD[MASKED]dockerdocker-compose.yml
+

Startup Parameters & Resource Requirements

+
+ + + + +
ServiceJVM/Runtime OptionsMemoryInstance Count
photoalbum-java-appJAVA_OPTS="-Xmx512m -Xms256m"; exposes port 8080256 MB initial heap, 512 MB max heap1 container in Compose
oracle-dbNo JVM options; Oracle container health check scriptNo explicit memory limit configured1 container in Compose
+

Startup Dependency Chain

+
    +
  1. oracle-db starts first and initializes the database user via scripts mounted from oracle-init/.
  2. +
  3. Docker Compose waits for the Oracle container health check (healthcheck.sh) to report healthy.
  4. +
  5. photoalbum-java-app starts only after oracle-db is healthy because of depends_on.condition: service_healthy.
  6. +
  7. The Spring Boot application then connects to Oracle using the active datasource settings and creates the schema on startup.
  8. +
+

Secrets & Sensitive Configuration

+
+ + + + + + +
Secret ReferenceTypeStorage (masked)
spring.datasource.passwordDatabase passwordInline Spring properties value masked in report
SPRING_DATASOURCE_PASSWORDDatabase passwordDocker Compose environment variable masked in report
ORACLE_PASSWORDOracle system/admin passwordDocker Compose environment variable masked in report
APP_USER_PASSWORDOracle application user passwordDocker Compose environment variable masked in report
+

Secrets Provisioning Workflow

+

Secrets are provisioned locally and statically. Source values are checked into repository-managed Spring properties and Docker Compose environment variables, then injected directly into the application container and Oracle container at startup. No managed identity, external vault, RBAC-governed secret store, or deployment-time secret binding workflow was detected.

+

Feature Flags

+
+ + + +
Flag NameDefaultControlled By
None detectedN/ANo @ConditionalOnProperty, feature flag framework, or toggle file detected
+

Framework & Runtime Versions

+
+ + + + + + + + + + +
ComponentVersionSource
Spring Boot2.7.18pom.xml parent POM
Java target1.8pom.xml properties
Maven build image3.9.6-eclipse-temurin-8Dockerfile
Java runtime imageeclipse-temurin:8-jreDockerfile
Bootstrap5.3.0CDN reference in Thymeleaf templates
Oracle JDBCojdbc8 (managed)pom.xml dependency
Oracle database containergvenzl/oracle-free:latestdocker-compose.yml
Spring Data JPA / HibernateSpring Boot managedpom.xml dependency set
+
+
+

Core Business Workflows

+

The Photo Album application supports a simple user journey: upload image files, browse the gallery, inspect photo details, and remove photos when no longer needed. Its business logic is concentrated around validating upload eligibility, preserving metadata, and presenting stored photos in a user-friendly order.

+

Domain Entities

+
+ + + + +
EntityService / Bounded ContextDescriptionKey Relationships
PhotoPhoto ManagementCore business record representing a user-uploaded image plus its display metadataServes as the source of truth for gallery cards, detail views, navigation, and deletion
UploadResultPhoto ManagementTransient workflow object describing whether an individual file upload succeeded or failedProduced during upload processing and converted into the JSON response payload
+

Service-to-Domain Mapping

+
+ + + + +
ServiceDomain ContextOwned EntitiesExternal Dependencies
photoalbum-java-appPhoto ManagementPhoto, UploadResultOracle database for persistence; browser clients for UI-driven workflows
oracle-dbPersistence InfrastructurePhysical storage for Photo recordsNone beyond its role as the backing data store
+

Primary Workflows

+

Workflow 1: Upload Photos

+

A user drags photos into the gallery page or selects them through the file picker. The browser script performs a first pass of MIME type and file size validation, then posts the accepted files to /upload. The service layer validates each file again, rejects unsupported or empty uploads, extracts image dimensions when possible, assigns a UUID-based stored filename for compatibility, saves the photo bytes and metadata, and returns a per-file success or failure outcome.

+

Business rules involved:

+
    +
  • Only JPEG, PNG, GIF, and WebP files are accepted.
  • +
  • Each file must be non-empty and no larger than 10 MB.
  • +
  • Upload success is reported per file so mixed-result batches can still partially succeed.
  • +
  • Image dimension extraction is best-effort; failure to read dimensions does not block persistence.
  • +
+ +

When a user opens /, the application retrieves all photos ordered by most recent upload time and renders them as gallery cards. Selecting a card opens /detail/{id}, where the application loads the chosen photo, computes previous/next navigation targets based on upload timestamps, and displays metadata such as file size, MIME type, and image dimensions.

+

Business rules involved:

+
    +
  • Gallery ordering is newest-first.
  • +
  • Missing photo identifiers gracefully redirect the user back to the gallery.
  • +
  • Previous and next navigation is derived from upload time, not filename or insertion order.
  • +
+

Workflow 3: Delete a Photo

+

From the detail page, a user can submit the delete action for a specific photo. The service checks whether the record exists, removes it from the database when present, and redirects the user back to the gallery with a success or error flash message.

+

Business rules involved:

+
    +
  • Deletion only succeeds when the target photo exists.
  • +
  • The UI always returns the user to the gallery after the operation.
  • +
  • User confirmation is required in the browser before the delete form is submitted.
  • +
+

Cross-Service Data Flows

+

There is no multi-service business composition layer in this application. Business data flows from the browser to the Spring Boot application and then to Oracle, with the same service acting as the source of truth for validation, persistence, and presentation. When failures occur, the application degrades in business-visible ways: upload batches return per-file error messages, missing detail records redirect to the gallery, and delete attempts surface a user-facing error message instead of throwing a visible exception.

+

Business Workflow Sequence

+
+sequenceDiagram + participant User + participant Browser as "Gallery UI" + participant Home as "HomeController" + participant Service as "PhotoServiceImpl" + participant Repo as "PhotoRepository" + participant DB as "Oracle Database" + + User->>Browser: Drop image files into upload zone + Browser->>Browser: Validate file type and size client-side + Browser->>Home: Submit accepted files to /upload + Home->>Service: Process each file + Service->>Service: Enforce upload rules and extract metadata + alt File is valid + Service->>Repo: Save Photo record + Repo->>DB: Persist metadata and image BLOB + DB-->>Repo: Stored photo + Repo-->>Service: Persisted entity + Service-->>Home: Success with photo id + Home-->>Browser: JSON response with uploaded photo summary + Browser-->>User: Show success and prepend new gallery card + else File is invalid or save fails + Service-->>Home: Failure result with error message + Home-->>Browser: JSON response with failedUploads entry + Browser-->>User: Show upload error message + end +
+

Business Rules & Decision Logic

+
    +
  • Validation rules: The application accepts only JPEG, PNG, GIF, and WebP uploads, enforces a 10 MB per-file limit, and rejects empty files.
  • +
  • Decision logic: Upload processing follows a per-file success/failure model so one bad file does not block the entire batch.
  • +
  • Computed values: The service derives image width and height from image bytes when available, and the UI computes human-readable file size displays and formatted upload timestamps.
  • +
  • State transitions: A Photo record moves from not persisted to persisted on upload and from persisted to removed on delete; there are no intermediate approval states.
  • +
  • Transactions: PhotoServiceImpl is annotated with @Transactional, so persistence operations run inside Spring-managed transaction boundaries.
  • +
  • Error handling and authorization: The application logs operational failures and converts them into redirects or structured upload errors. No business-level authorization rules or role checks were detected.
  • +
+
+
+

Dependency Map

+

This project is a Spring Boot Maven application with 8 declared non-test dependencies plus 2 test-scoped dependencies. The dependency set is compact and centered around web delivery, JPA-based persistence, Oracle connectivity, and a small number of utility libraries.

+

Dependencies

+
+flowchart LR + App["photo-album application"] + Parent["spring-boot-starter-parent 2.7.18"] + + subgraph Web["Web Frameworks"] + WebStarter["spring-boot-starter-web 2.7.18"] + Thymeleaf["spring-boot-starter-thymeleaf 2.7.18"] + end + + subgraph Db["Database and ORM"] + Jpa["spring-boot-starter-data-jpa 2.7.18"] + OracleJdbc["ojdbc8 managed runtime"] + end + + subgraph Util["Utilities"] + Validation["spring-boot-starter-validation 2.7.18"] + Json["spring-boot-starter-json 2.7.18"] + CommonsIo["commons-io 2.11.0"] + Devtools["spring-boot-devtools 2.7.18 optional"] + end + + App -->|"web"| Web + App -->|"persistence"| Db + App -->|"support"| Util + Parent -.->|"manages versions"| WebStarter + Parent -.->|"manages versions"| Thymeleaf + Parent -.->|"manages versions"| Jpa + Parent -.->|"manages versions"| Validation + Parent -.->|"manages versions"| Json + Parent -.->|"manages versions"| Devtools + Parent -.->|"manages BOM for"| OracleJdbc +
+

Dependency Summary

+
+ + + + + +
CategoryCountKey LibrariesNotes
Web Frameworks2Spring Boot Web, Spring Boot ThymeleafServer-rendered MVC UI with template rendering
Database / ORM2Spring Data JPA, ojdbc8Hibernate-backed persistence against Oracle
Utilities4Validation, JSON, Commons IO, DevToolsValidation, JSON serialization, file helpers, and development reload support
+

Version & Compatibility Risks

+

The dependency set is tied to Spring Boot 2.7.18 and Java 8, both of which are older baselines for Azure modernization scenarios and are already highlighted by the generated AppCAT report. The Oracle JDBC runtime dependency also keeps the application closely coupled to Oracle-specific behavior, while the JPA repository contains native Oracle SQL that may complicate migration to other managed data platforms.

+

Notable Observations

+
    +
  • The Spring Boot parent POM centrally manages nearly all starter versions, so framework upgrades will have broad impact across web, JPA, validation, and JSON support.
  • +
  • commons-io is the only explicitly version-pinned third-party utility outside the Spring Boot managed dependency set.
  • +
  • The application declares Oracle JDBC only at runtime scope, which is appropriate for deployment but still creates a hard production dependency on Oracle drivers.
  • +
  • No dedicated messaging, caching, security, or observability libraries are declared in the build.
  • +
+

Test Dependencies

+
+ + + + +
FrameworkVersionNotes
spring-boot-starter-test2.7.18Provides JUnit 5 and the default Spring Boot test stack
h2Spring Boot managedIn-memory database dependency used for tests
+

Total test-scope dependencies: 2

+

The test toolchain is minimal and focused on application context startup validation. No dedicated integration-test libraries, containerized test dependencies, or contract-testing frameworks are declared.

+
+
+

Data Architecture & Persistence Layer

+

The data layer is intentionally small: a single JPA entity is stored in Oracle using Spring Data JPA and Hibernate. All persistence concerns are concentrated in one repository interface, with no caching layer or separate reporting store.

+

Database Configuration

+
+ + + + + +
Service/ModuleDB TypeProfileDriverConnectionMigration Tool
photoalbum-java-appOracle DatabasedefaultOracle JDBC (ojdbc8)JDBC URL points to oracle-db:1521/FREEPDB1None; Hibernate creates schema at startup
photoalbum-java-appOracle Database XEdockerOracle JDBC (ojdbc8)JDBC URL points to oracle-db:1521:XENone; Hibernate creates schema at startup
photoalbum-java-app testsH2 in-memory databasetestH2Spring Boot test auto-configuration with @ActiveProfiles("test")None detected
+

Data Ownership per Service

+
+ + + + +
ServiceTables OwnedORM FrameworkCachingNotes
photoalbum-java-appPHOTOSJPA / Hibernate via Spring Data JPANone detectedSingle-service ownership; stores both metadata and binary photo content
oracle-dbPHOTOS physical storageOracle engineN/ABacking store only; no independent domain ownership
+

Entity Model

+
+erDiagram + Photo { + string id PK + string originalFileName + bytes photoData + string storedFileName + string filePath + long fileSize + string mimeType + datetime uploadedAt + int width + int height + } +
+

The source entity model is defined in src/main/java/com/photoalbum/model/Photo.java. No JPA relationships to other entities were found; the model is a self-contained aggregate for photo content and metadata.

+

Key Repository Methods

+
+ + + + + + + + +
ServiceRepositoryNotable MethodsPurpose
photoalbum-java-appPhotoRepository (src/main/java/com/photoalbum/repository/PhotoRepository.java)findAllOrderByUploadedAtDesc()Returns gallery items ordered newest-first
photoalbum-java-appPhotoRepositoryfindPhotosUploadedBefore(LocalDateTime)Finds older photos for previous-photo navigation
photoalbum-java-appPhotoRepositoryfindPhotosUploadedAfter(LocalDateTime)Finds newer photos for next-photo navigation
photoalbum-java-appPhotoRepositoryfindPhotosByUploadMonth(String, String)Oracle-specific month-based filtering helper
photoalbum-java-appPhotoRepositoryfindPhotosWithPagination(int, int)Uses Oracle ROWNUM pagination for ordered slicing
photoalbum-java-appPhotoRepositoryfindPhotosWithStatistics()Uses Oracle analytical functions for ranking and running totals
+

Caching Strategy

+

No application-level caching was detected. There are no @Cacheable annotations, Spring Cache configuration classes, Redis dependencies, or Hibernate second-level cache settings in the repository. Every gallery, detail, and image request ultimately reads from Oracle, while uploaded image responses add explicit no-cache headers to avoid stale browser content.

+

Data Ownership Boundaries

+

The application uses a single shared Oracle database that is owned and accessed by one Spring Boot service. There is no database-per-service split, no CQRS read model, and no cross-service access pattern beyond the web application talking directly to its own database. Transaction management is centralized in PhotoServiceImpl, which is annotated with @Transactional so upload, lookup, and delete operations run inside Spring-managed transaction boundaries.

+

Data Classification & Sensitivity

+
+ + + +
EntitySensitive FieldsClassification (PII/PHI/PCI/None)Controls in Place
PhotooriginalFileName, photoDataPII (potential user-generated personal content)No explicit masking, field-level access control, or application-managed encryption detected
+
+ + + +
\ No newline at end of file diff --git a/.github/modernize/assessment/reports/report-20260521061104/report.json b/.github/modernize/assessment/reports/report-20260521061104/report.json new file mode 100644 index 000000000..ad74761d9 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/report.json @@ -0,0 +1,1021 @@ +{ + "version": "1.0.0", + "producer": "Java AppCAT CLI", + "metadata": { + "analysisStartTime": "2026-05-21T06:11:04.086914437Z", + "analysisEndTime": "2026-05-21T06:11:50.820300156Z", + "status": "Complete", + "privacyMode": "Protected", + "privacyModeHelpUrl": "https://aka.ms/appcat-privacy-mode", + "targetIds": [ + "azure-aks", + "azure-appservice", + "azure-container-apps" + ], + "targetDisplayNames": [ + "Azure Kubernetes Service", + "Azure App Service", + "Azure Container Apps" + ], + "capabilities": [], + "os": [] + }, + "summary": { + "totalProjects": 1, + "totalIssues": 9, + "totalIncidents": 27, + "totalEffort": 172, + "charts": { + "severity": { + "mandatory": 11, + "optional": 2, + "potential": 14, + "information": 0 + }, + "category": { + "database-migration": 6, + "framework-upgrade": 10, + "jakarta-migration": 1, + "java-version-upgrade": 3, + "local-credential": 3, + "spring-migration": 4 + } + } + }, + "projects": [ + { + "path": ".", + "issues": 9, + "storyPoints": 172, + "properties": { + "appName": "photo-album", + "jdkVersion": "1.8", + "frameworks": [ + "Spring Boot", + "Spring" + ], + "languages": [ + "Java", + "Python" + ], + "tools": [ + "Maven" + ] + }, + "incidents": [ + { + "ruleId": "azure-java-version-02000", + "incidentId": "18706532-081f-49f1-badc-4aade976a6c5", + "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": "9180d72e-330d-4ab5-abb3-c1112cd28d08", + "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": "99358692-970d-423f-8290-674bea48d9cf", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 24, + "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": "9bcd3042-1025-4c7e-beda-583651910211", + "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": "7f6411a1-1abc-4cc5-9c06-50073793415d", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 24, + "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": "8a9f0cd9-5e43-4576-ac10-6e3b9924429b", + "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": "da4bc4c0-e33f-40bf-855f-84869f9a8fbd", + "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": "eff335ba-52da-40dc-a4c5-a51cbbb20ce3", + "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": "1a234247-d219-456d-a286-c844db01e34e", + "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": "52aab8c8-018f-4c39-bcaf-2a941928d2d6", + "location": "pom.xml", + "locationKind": "File", + "line": 72, + "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": "f093a77b-2fc9-48a5-ba59-98ab2620d7b8", + "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": "499b61ac-cd15-432b-9c96-31baf0daa518", + "location": "pom.xml", + "locationKind": "File", + "line": 92, + "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": "ff5621f5-c530-4405-aafc-cbc5d5197986", + "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-database-microsoft-oracle-07000", + "incidentId": "661623e4-8d1c-4814-ae65-d6681cae26c8", + "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": "a92f04bf-7720-43ea-9d34-620153464eab", + "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": "2b98576a-091f-420d-9633-302b5d4b41a8", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 2, + "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": "29ec7c02-0656-4103-898f-8b6e51448e66", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 10, + "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": "a294ecb5-cfea-41a4-b6f6-1547519d8c29", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 5, + "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": "fe47f4ea-2db4-48de-9889-ad6b594e9dcf", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 13, + "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": "59e3556f-56b8-40d9-bc2a-f275c89ef829", + "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-framework-version-01000", + "incidentId": "bbe744b6-8dd9-40f3-9843-334fd5c24ad7", + "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": "6dc8f3bd-f4f9-40e6-91fc-e13c1885a3d3", + "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": "443f124e-5067-476d-a896-32b957c849f7", + "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": "abfd09b4-a2f1-4f0c-afce-ab1666835533", + "location": "src/main/resources/application-docker.properties", + "locationKind": "File", + "line": 4, + "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": "a882b99a-5fb8-4f66-81ab-d12f5dcf481f", + "location": "src/main/resources/application.properties", + "locationKind": "File", + "line": 12, + "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": "46babd76-99ea-4bb6-a566-797476c9d6a6", + "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": "jakarta-database-00002", + "incidentId": "aa7ff01e-3a02-4b38-9813-ecc05ebf42bc", + "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": { + "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's 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's 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 (`jakarta.persistence.*` or legacy `javax.persistence.*`), 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's 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" + ] + } + } +} \ No newline at end of file diff --git a/.github/modernize/assessment/reports/report-20260521061104/report.md b/.github/modernize/assessment/reports/report-20260521061104/report.md new file mode 100644 index 000000000..ab0a3c7c9 --- /dev/null +++ b/.github/modernize/assessment/reports/report-20260521061104/report.md @@ -0,0 +1,137 @@ +# photo-album + +## Summary + +| Metric | Value | +|--------|-------| +| Total Issues | 9 | +| Mandatory Blockers | 3 | +| Potential Issues | 5 | + +## Application Information + +| Property | Value | +|----------|-------| +| Language | Java, Python | +| 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 | [3](#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) | +| 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)` +- `docker-compose.yml (line 32)` +- `src/main/resources/application-docker.properties (line 2)` +- `src/main/resources/application.properties (line 10)` +- `src/main/resources/application-docker.properties (line 5)` +- `src/main/resources/application.properties (line 13)` + +
+ +
+Password found in configuration file — affected files + +- `src/test/resources/application-test.properties (line 5)` +- `src/main/resources/application-docker.properties (line 4)` +- `src/main/resources/application.properties (line 12)` + +
+ +
+Server port configuration found — affected files + +- `src/main/resources/application-docker.properties (line 24)` +- `src/main/resources/application.properties (line 2)` + +
+ +
+Restricted configurations found — affected files + +- `src/main/resources/application-docker.properties (line 24)` +- `src/main/resources/application.properties (line 2)` + +
+ +
+Detects usage of Jakarta Persistence (JPA) APIs — affected files + +- `pom.xml (line 46)` + +
+ +## Upgrade Issues + +| Issue Name | Criticality | Story Points | Occurrences | +|------------|-------------|--------------|-------------| +| Spring Boot Version Has Reached the End of OSS Support | Mandatory | 8 | [7](#Spring_Boot_Version_Has_Reached_the_End_of_OSS_Support) | +| Spring Framework Version Has Reached the End of OSS Support | Mandatory | 8 | [3](#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)` +- `pom.xml (line 78)` +- `pom.xml (line 34)` +- `pom.xml (line 72)` +- `pom.xml (line 59)` +- `pom.xml (line 92)` +- `pom.xml (line 46)` + +
+ +
+Spring Framework Version Has Reached the End of OSS Support — affected files + +- `pom.xml (line 78)` +- `pom.xml (line 34)` +- `pom.xml (line 46)` + +
+ +
+Java Version Has Reached the End of Support — affected files + +- `pom.xml (line 24)` + +
+ +
+Java Version is not the latest LTS — affected files + +- `pom.xml (line 25)` +- `pom.xml (line 26)` + +
+ +--- + +## 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)