Skip to content

Upgrade to Spring Boot 3.4.5 and Java 21#4

Open
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
upgrade/springboot3-java21
Open

Upgrade to Spring Boot 3.4.5 and Java 21#4
devin-ai-integration[bot] wants to merge 1 commit into
mainfrom
upgrade/springboot3-java21

Conversation

@devin-ai-integration
Copy link
Copy Markdown

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

Summary

Upgrades the backend from Spring Boot 2.7.18 / Java 11 to Spring Boot 3.4.5 / Java 21 and migrates the breaking API changes that ride along with it.

pom.xml

  • Parent → spring-boot-starter-parent 3.4.5
  • java.version / compiler source & target → 21
  • springdoc-openapi-ui 1.7.0 → springdoc-openapi-starter-webmvc-ui 2.8.6
  • jjwt-* 0.11.5 → 0.12.6
  • Added flyway-database-postgresql (required by Flyway 10+ to run against Postgres)
  • maven-compiler-plugin 3.11.0 → 3.13.0

Code

  • Bulk rename of javax.persistence, javax.validation, and javax.servlet imports to their jakarta.* equivalents across 33 files (kept javax.crypto intact — it's a JDK package, not Jakarta EE).
  • SecurityConfig rewritten to the Spring Security 6 lambda DSL (cors(Customizer.withDefaults()), csrf(csrf -> csrf.disable()), sessionManagement(...), authorizeHttpRequests(...), requestMatchers(...)).
  • JwtTokenProvider updated to the jjwt 0.12.x fluent API (claims/subject/issuedAt/expiration, Jwts.parser().verifyWith(...).parseSignedClaims(...).getPayload()).
  • Removed explicit hibernate.dialect from application.yml, application-dev.yml, and application-test.yml (Hibernate 6 auto-detects).

CI

  • .github/workflows/ci.yml bumped to java-version: '21'.

Local mvn clean compile succeeds on JDK 21. Tests have not been run locally yet; relying on CI (mvn test, mvn verify) for runtime validation.

Review & Testing Checklist for Human

  • JWT signing algorithm change. signWith(key, SignatureAlgorithm.HS512) was replaced with signWith(key), which in jjwt 0.12.x auto-selects the algorithm based on the key's bit length. The default dev secret (dev-secret-key-change-in-production, 36 bytes) will now select HS256, not HS512. Any existing HS512 tokens in flight will fail verification after deploy, and downstream consumers that assert on the alg header will see a different value. If HS512 is required, either restore the explicit algorithm or ensure JWT_SECRET is ≥ 64 bytes in every environment.
  • Springdoc path change. springdoc-openapi 2.x moved the Swagger UI from /swagger-ui.html to /swagger-ui/index.html (and the API docs default changed). Verify any bookmarks, reverse-proxy rules, frontend links, or healthchecks that point at the old URLs.
  • Flyway 10 on Postgres. The new flyway-database-postgresql module is required at runtime; without it Flyway fails to start against Postgres. Confirm migrations in src/main/resources/db/migration/V1..V3 still apply cleanly against the Neon dev DB (the dev profile uses baseline-on-migrate: true).
  • Runtime smoke test. Start the app with mvn spring-boot:run -Dspring-boot.run.profiles=dev, hit /api/actuator/health, log in via AuthController to verify JWT issuance + validation end-to-end, and exercise a patient read (GET /api/v1/patients/{id}) to confirm JPA entities still map correctly under Jakarta Persistence / Hibernate 6.
  • Test suite under H2. application-test.yml no longer sets hibernate.dialect; Hibernate 6 should auto-detect H2 from the driver, but please confirm mvn test passes in CI and that no test relies on the removed dialect property.

Notes

  • SecurityConfig still permits all requests (requestMatchers("/**").permitAll()) — unchanged from before the upgrade, but worth a second look now that the DSL has been rewritten.
  • Checkstyle emits several pre-existing AvoidStarImport warnings on files touched by the javax → jakarta rename; these were warnings before the change (violationSeverity=error filters them out) and the build still reports 0 violations.
  • Lombok @Builder warnings on User.java and MapStruct Unmapped target properties warnings on PatientMapper are pre-existing and not introduced here.

Link to Devin session: https://app.devin.ai/sessions/80d0566784c642d49d7d97e1f88c9b49
Requested by: @clivingston-cognition


Open in Devin Review

- Bump Spring Boot parent from 2.7.18 to 3.4.5
- Upgrade Java target from 11 to 21
- Switch springdoc to springdoc-openapi-starter-webmvc-ui 2.8.6
- Upgrade jjwt from 0.11.5 to 0.12.6 and update API usage (claims/subject/issuedAt/expiration, parser().verifyWith().parseSignedClaims())
- Add flyway-database-postgresql module (required in Flyway 10.x for Postgres)
- Bump maven-compiler-plugin to 3.13.0
- Migrate javax.persistence/validation/servlet -> jakarta.*
- Rewrite SecurityConfig to lambda DSL (cors/csrf/sessionManagement/authorizeHttpRequests, requestMatchers, Customizer)
- Drop explicit Hibernate dialect in application*.yml (Hibernate 6 auto-detects)
- Bump CI Java version to 21
@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

Copy link
Copy Markdown
Author

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 5 additional findings in Devin Review.

Open in Devin Review

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Redis property namespace not updated for Spring Boot 3

In Spring Boot 3.x, Redis configuration properties moved from spring.redis.* to spring.data.redis.*. The application.yml still uses the old namespace (spring.redis.host, spring.redis.port at lines 24-26), which Spring Boot 3.4.5 will silently ignore. Redis auto-configuration will fall back to defaults. Currently this has no visible impact because (a) the configured values match the defaults (localhost:6379) and (b) dev/test profiles exclude Redis auto-configuration entirely — but the configuration is effectively dead and would silently fail if someone changes these values expecting them to take effect.

(Refers to lines 24-26)

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

.subject(subject)
.issuedAt(now)
.expiration(expiryDate)
.signWith(getSigningKey())
Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 JWT signing algorithm silently downgraded from HS512 to HS256

The migration from jjwt 0.11.5 to 0.12.6 changed .signWith(getSigningKey(), SignatureAlgorithm.HS512) to .signWith(getSigningKey()). In jjwt 0.12.x, the single-argument signWith(Key) auto-selects the HMAC algorithm based on key length: HS256 for 256-383 bit keys, HS384 for 384-511 bit keys, HS512 for ≥512 bit keys. The default secret dev-secret-key-change-in-production is 35 bytes (280 bits), and the test secret aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa is 34 bytes (272 bits) — both fall in the HS256 range. This silently downgrades the signing algorithm from HS512 to HS256. Additionally, any existing tokens signed with HS512 by the old code will have "alg":"HS512" in their header, while new tokens will have "alg":"HS256" — a mixed-algorithm state after deployment.

Correct jjwt 0.12.x migration for explicit HS512

The explicit algorithm form in 0.12.x is signWith(key, Jwts.SIG.HS512), but this requires the secret key to be at least 64 bytes. If keeping the shorter secrets, the downgrade to HS256 should be an intentional, documented decision.

Prompt for agents
In JwtTokenProvider.java line 52, the migration changed .signWith(getSigningKey(), SignatureAlgorithm.HS512) to .signWith(getSigningKey()), which auto-selects the algorithm based on key size. For the default secrets (34-35 bytes), this results in HS256 instead of HS512.

To fix this properly:
1. If you want to keep HS512: change to .signWith(getSigningKey(), Jwts.SIG.HS512) AND ensure the JWT secret is at least 64 bytes (512 bits) in all environments (application.yml medchart.security.jwt.secret, and application-test.yml).
2. If HS256 is acceptable: add a code comment explicitly noting the algorithm change from HS512, and document this as a known behavioral change of the migration.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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