Skip to content

feat: migrate eShopLegacyMVC from .NET Framework 4.7.2 to Java 21 / Spring Boot 3.5#2

Open
devin-ai-integration[bot] wants to merge 7 commits into
mainfrom
feature/java-migration
Open

feat: migrate eShopLegacyMVC from .NET Framework 4.7.2 to Java 21 / Spring Boot 3.5#2
devin-ai-integration[bot] wants to merge 7 commits into
mainfrom
feature/java-migration

Conversation

@devin-ai-integration
Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot commented May 21, 2026

Summary

Complete 8-phase migration of the eShopLegacyMVC ASP.NET MVC 5 application (.NET Framework 4.7.2) to Java 21 with Spring Boot 3.5.x. All new Java code lives in eShopModernizedJava/ — the legacy .NET code is untouched.

What's included:

Phase Component Details
1 Project Scaffolding Maven + Spring Boot 3.5.0 parent, Java 21, WebJars, Maven Wrapper
2 Domain Model JPA entities (CatalogItem, CatalogBrand, CatalogType) with HiLo sequences, Flyway migrations
3 Data Seeding Spring Data JPA repos, PreconfiguredData (12 items, 5 brands, 4 types), CSV import support
4 Service Layer CatalogService interface + JPA impl + in-memory mock (@ConditionalOnProperty)
5 MVC + Views CatalogController (full CRUD), PicController, 6 Thymeleaf templates, GlobalExceptionHandler
6 REST API BrandsRestController, FilesRestController (JSON replaces BinaryFormatter), CatalogRestController
7 Observability Actuator, Micrometer Prometheus, OpenTelemetry tracing, MDC-based request logging
8 Deployment Spring Security (permit all + CSRF), HttpSession listener, Dockerfile, docker-compose.yml, README

Breaking change: /api/files now returns JSON instead of binary-serialized data. The legacy BinaryFormatter in eShopLegacy.Utilities/Serializing.cs was a known security vulnerability (CVE-2021-24112) and has been eliminated entirely.

Build verified: ./mvnw clean test passes (compilation + Spring Boot context loads with H2 in mock data mode).

Review feedback addressed:

  • Fixed JPA FK write bug: CatalogServiceImpl and CatalogDbInitializer now resolve @ManyToOne entity references before saving
  • Fixed path traversal in PicController: rejects filenames containing .., /, or \\

Review & Testing Checklist for Human

  • Run ./mvnw clean package in eShopModernizedJava/ to verify the build compiles and tests pass with Java 21
  • Run with mock data mode (./mvnw spring-boot:run -Dspring-boot.run.arguments="--app.use-mock-data=true") and verify:
    • Home page loads at http://localhost:8080/ with 12 catalog items
    • Pagination works (Previous/Next)
    • CRUD flow: Create, Edit, Delete an item
    • Product images load via /items/{id}/pic
  • Test REST API endpoints:
    • GET /api/brands returns JSON array of 5 brands
    • GET /api/brands/1 returns single brand
    • GET /api/files returns JSON list of BrandDto (NOT binary)
    • DELETE /api/brands/1 returns 200 (no-op)
  • Verify observability: GET /actuator/health and GET /actuator/prometheus respond correctly
  • Docker Compose test (requires Docker + SQL Server): docker-compose up in eShopModernizedJava/ — verify full CRUD against SQL Server with Flyway migrations

Recommended test plan: Start with mock data mode (no database required) to verify UI and API. Then test with Docker Compose for full SQL Server integration including Flyway migrations and data seeding.

Notes

  • The @Pattern annotation was removed from the BigDecimal price field in CatalogItem since Jakarta Validation's @Pattern only applies to CharSequence types. The @DecimalMin/@DecimalMax constraints provide equivalent validation.
  • WebJar version for jquery-validation uses 1.19.0 (1.19.1 is not available in Maven Central).
  • Entity mapping uses @EntityGraph for eager loading of CatalogBrand and CatalogType relationships, replacing EF's .Include().
  • The legacy .NET solution files are completely untouched by this PR.

Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/fdea619c44c04acfbe300b74485c468d
Requested by: @mbatchelor81


Open in Devin Review

…pring Boot 3.5

Complete 8-phase migration of the eShopLegacyMVC ASP.NET MVC 5 application to
Java 21 with Spring Boot 3.5.x. All new Java code is in eShopModernizedJava/.

Phase 1 - Project Scaffolding:
- Maven project with spring-boot-starter-parent 3.5.0, Java 21
- Spring Boot application entry point (CatalogApplication)
- application.yml with SQL Server, Flyway, and app config
- Logback logging config ported from log4net.xml
- Static assets (CSS, product images, setup CSVs) copied

Phase 2 - Domain Model:
- JPA entities: CatalogItem, CatalogBrand, CatalogType
- HiLo sequence generators matching legacy SQL Server sequences
- Flyway migrations for schema and sequences
- PaginatedItemsDto replacing PaginatedItemsViewModel

Phase 3 - Repository & Seeding:
- Spring Data JPA repositories with EntityGraph for eager loading
- PreconfiguredData with 12 items, 5 brands, 4 types
- CatalogDbInitializer (ApplicationRunner) with CSV import support

Phase 4 - Service Layer:
- CatalogService interface matching ICatalogService.cs
- CatalogServiceImpl with JPA (ConditionalOnProperty)
- CatalogServiceMock in-memory implementation
- AppConfig for custom configuration properties

Phase 5 - MVC Controllers & Views:
- CatalogController with full CRUD (/, details, create, edit, delete)
- PicController for product image serving with MIME type detection
- Thymeleaf templates with layout dialect replacing Razor views
- GlobalExceptionHandler replacing HandleErrorAttribute

Phase 6 - REST API:
- BrandsRestController (/api/brands) - GET all, GET by id, DELETE (no-op)
- FilesRestController (/api/files) - returns JSON instead of BinaryFormatter
- CatalogRestController (/api) - Hello World endpoint
- BrandDto record replacing BinaryFormatter-serialized BrandDTO

Phase 7 - Observability:
- Spring Boot Actuator (health, metrics, prometheus)
- Micrometer Prometheus registry
- OpenTelemetry tracing via micrometer-tracing-bridge-otel
- RequestLoggingInterceptor with MDC (replaces ActivityIdHelper)

Phase 8 - Deployment:
- Spring Security (permit all, CSRF for forms)
- HttpSessionListener (replaces Global.asax Session_Start)
- Multi-stage Dockerfile with eclipse-temurin:21
- Docker Compose with SQL Server 2022
- Migration documentation in README.md
@devin-ai-integration
Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

devin-ai-integration[bot]

This comment was marked as resolved.

… protection

- CatalogServiceImpl: resolve CatalogBrand/CatalogType entity references
  via repository lookups before calling save() in create/update operations.
  Fixes PropertyValueException from null @manytoone(optional=false) refs.

- CatalogDbInitializer: resolve entity references on seed items after
  brands and types are persisted, before saving catalog items.

- PicController: reject pictureFileName containing '..', '/', or '\' to
  prevent path traversal attacks via crafted filenames.
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

…, validate CSV headers

- V2__create_sequences.sql: START WITH 100 for all three sequences so
  generated IDs don't collide with preconfigured seed data (items 1-12,
  brands 1-5, types 1-4).

- CatalogDbInitializer: validate CSV header indices before use to prevent
  ArrayIndexOutOfBoundsException with unexpected CSV formats.
devin-ai-integration[bot]

This comment was marked as resolved.

- CatalogController: clamp pageSize to minimum 1 and pageIndex to minimum 0
- PaginatedItemsDto: defend against division by zero when pageSize <= 0
devin-ai-integration[bot]

This comment was marked as resolved.

- CatalogItem, CatalogBrand, CatalogType: change @id from primitive int
  to Integer so Spring Data's isNew() returns true for new entities (null
  id) triggering persist(), and false for seed data with explicit IDs
  triggering merge() which preserves the pre-set IDs.

- CatalogServiceMock: change == to .equals() for Integer comparisons in
  updateCatalogItem() and removeCatalogItem() to avoid reference equality
  issues with Integer wrapper objects.
devin-ai-integration[bot]

This comment was marked as resolved.

…error

- edit.html: add hidden field for onReorder to prevent silent reset to false
- CatalogController: call addUriPlaceholder() before re-rendering edit form
  on validation errors so product image displays correctly
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant