App Modernization: Java 11 → 21, Spring Boot 2.6.3 → 3.4.4#148
App Modernization: Java 11 → 21, Spring Boot 2.6.3 → 3.4.4#148devin-ai-integration[bot] wants to merge 8 commits into
Conversation
…ndency versions - Spring Boot 2.6.3 -> 3.4.4 - Gradle 7.4 -> 8.12 - Java 11 -> 21 - DGS codegen 5.0.6 -> 5.12.4, DGS runtime 4.9.21 -> 9.1.3 - jjwt 0.11.2 -> 0.12.6 - REST-Assured 4.5.1 -> 5.5.0 - MyBatis starter 2.2.2 -> 3.0.4 - Spotless 6.2.1 -> 6.25.0 - JaCoCo toolVersion 0.8.12 - Remove joda-time dependency - Remove explicit javax.validation:validation-api
- javax.validation -> jakarta.validation (20 files) - javax.servlet -> jakarta.servlet (JwtTokenFilter) - Updated GraphQL exception handler for DGS 9.x handleException API
- WebSecurityConfigurerAdapter -> SecurityFilterChain bean - Lambda DSL for csrf, cors, sessionManagement, authorizeHttpRequests - antMatchers -> requestMatchers - authorizeRequests -> authorizeHttpRequests - HttpStatus -> HttpStatusCode in handleMethodArgumentNotValid override
- joda-time DateTime -> java.time.OffsetDateTime (all domain + data classes) - DateTimeHandler: MyBatis TypeHandler rewritten for OffsetDateTime - DateTimeCursor: use Instant/OffsetDateTime for cursor pagination - JacksonCustomizations: OffsetDateTimeSerializer with ISO format - DefaultJwtService: jjwt 0.12 API (subject/expiration/signWith/parseSignedClaims) - Keys.hmacShaKeyFor with padding for minimum key size requirement
- Replace graphql.relay.DefaultPageInfo with DGS-generated PageInfo type - Remove graphql.relay.DefaultConnectionCursor usage - ISODateTimeFormat (joda) -> DateTimeFormatter.ISO_OFFSET_DATE_TIME (java.time) - DataFetcherExceptionHandler.onException -> handleException (CompletableFuture)
- joda DateTime -> java.time.OffsetDateTime in all test fixtures - ISODateTimeFormat -> DateTimeFormatter in ArticleApiTest assertions - REST-Assured 4.x -> 5.x (compatible, no API changes needed) - Selenium WebDriverWait: long timeout -> Duration (Selenium 4 API) - Let Spring Boot manage httpclient5 version to avoid classpath conflicts
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
Update setup-java from JDK 11 to JDK 21 to match the upgraded project.
| implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:3.0.4' | ||
| implementation(platform('com.netflix.graphql.dgs:graphql-dgs-platform-dependencies:9.1.3')) | ||
| implementation 'com.netflix.graphql.dgs:graphql-dgs-spring-graphql-starter' | ||
| implementation 'org.flywaydb:flyway-core' |
There was a problem hiding this comment.
🚩 Flyway + SQLite may require additional dependency with Flyway 10.x
Spring Boot 3.4.4 manages Flyway 10.x, which modularized database support. The build only declares implementation 'org.flywaydb:flyway-core' (build.gradle:77), but the application uses SQLite (application.properties:1 — jdbc:sqlite:dev.db). In Flyway 10+, database-specific support was extracted into separate modules. SQLite may require org.flywaydb:flyway-community-db-support or a similar module. Without it, the application could fail at startup with a "No database found to handle jdbc:sqlite" error. I could not verify the exact Flyway version managed by Spring Boot 3.4.4 or whether SQLite is still bundled in core, so this needs investigation.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Investigated — this is not an issue for this project. Spring Boot 3.4.4 manages Flyway 10.22.x, and the Flyway 10.x modularization extracted support for commercial databases into separate modules. SQLite support remains in flyway-core for community databases. All 68 tests pass including the ones that exercise Flyway migrations against the SQLite dev.db, confirming that the current flyway-core dependency is sufficient.
…ding Replace zero-byte padding with a minimum-length validation (32 bytes for HMAC-SHA). Keys.hmacShaKeyFor() auto-selects HS256/HS384/HS512 based on key length. Short secrets now throw IllegalArgumentException at startup rather than silently weakening key material. Addresses Devin Review feedback.
| @MappedTypes(OffsetDateTime.class) | ||
| public class DateTimeHandler implements TypeHandler<OffsetDateTime> { | ||
|
|
||
| private static final Calendar UTC_CALENDAR = Calendar.getInstance(TimeZone.getTimeZone("UTC")); |
There was a problem hiding this comment.
🚩 Static mutable Calendar instance shared across threads in DateTimeHandler
The UTC_CALENDAR at line 19 is a static final Calendar instance shared by all threads. Calendar is mutable and not thread-safe — JDBC drivers may call mutating methods like set() or clear() on it during getTimestamp/setTimestamp calls, potentially causing race conditions under concurrent requests. This is a pre-existing issue (not introduced by this PR), but worth noting since it wasn't addressed during the migration. A safer approach would be to create a new Calendar instance per call, or use ThreadLocal<Calendar>.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Agreed this is a valid thread-safety concern, but as noted it's pre-existing behavior carried over from the original joda-time DateTimeHandler — not a regression from this migration. Fixing it would be a separate improvement outside the scope of this upgrade PR. A follow-up could either create a new Calendar per call or switch to Timestamp.from(instant) / Timestamp.valueOf(localDateTime) which avoids Calendar entirely.
Summary
Major version upgrade from Java 11 / Spring Boot 2.6.3 to Java 21 / Spring Boot 3.4.4, done in 6 verified layers. Each layer committed independently and verified against
./gradlew clean test spotlessCheck. All 68 tests pass on Java 21.Why 3.4.4 instead of 3.5.x: Spring Boot 3.5.0's
spring-graphql:1.4.0requiresjava-dataloaderwithDataLoaderOptions$Builder, which conflicts with DGS 9.1.3'sstrictly [3.3.0]constraint. 3.4.4 is the latest Boot 3 release compatible with the DGS 9.x ecosystem.Layer 1 — Build Config
Gradle 7.4→8.12,
sourceCompatibility/targetCompatibility11→21, Spring Boot plugin 2.6.3→3.4.4, dependency-management 1.0.11→1.1.7, Spotless 6.2.1→6.25.0, JaCoCo 0.8.12. Removed explicitjavax.validation:validation-api(Boot 3 providesjakarta.validation-apitransitively). Spotless target narrowed tosrc/**/*.javato avoid Gradle 8 implicit-dependency warnings with generated sources.Layer 2 — Namespace Migration
javax.validation.*→jakarta.validation.*,javax.servlet.*→jakarta.servlet.*across 19 source files.Layer 3 — Security Config
CustomizeExceptionHandler.handleMethodArgumentNotValidsignature updated forHttpStatusCode(Boot 3).Layer 4 — Infrastructure
Jwts.builder().subject().expiration().signWith(key),Jwts.parser().verifyWith(key).build().parseSignedClaims(). Key padded to 64 bytes forKeys.hmacShaKeyFor()minimum.DateTime→OffsetDateTimeacross domain entities (Article,Comment), data classes, query services. CustomDateTimeHandler(MyBatis TypeHandler) rewritten forOffsetDateTime.DateTimeCursorusesInstant/OffsetDateTime.JacksonCustomizationsserializer usesDateTimeFormatter.ISO_OFFSET_DATE_TIME.Layer 5 — DGS GraphQL 4.x → 9.x
graphql.relay.DefaultPageInfo→DGS-generatedio.spring.graphql.types.PageInfo(builder API).DataFetcherExceptionHandler.onException→handleExceptionreturningCompletableFuture<DataFetcherExceptionHandlerResult>. Platform BOMgraphql-dgs-platform-dependencies:9.1.3+eachDependencyresolution strategy to aligngraphql-java:22.3/java-dataloader:3.3.0with DGS codegen 5.12.4.Layer 6 — Test Dependencies
REST-Assured 4.5→5.5, Selenium
WebDriverWait(driver, long)→WebDriverWait(driver, Duration), httpclient5 version managed by Boot (was explicit 5.2.1, conflicted with Boot-managed version causingTlsSocketStrategyClassNotFoundException). Test fixtures updated forOffsetDateTime.Verification
Note: JaCoCo coverage verification (80% threshold) was already failing on the baseline
mainbranch (0% coverage ratio due to class mismatch) — not a regression introduced by this PR.Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/e99d31e33ea14a2ab366ca4338bfb7d4
Requested by: @bsmitches