Migrate to Java 21 / Spring Boot 3.2.5#22
Conversation
- Bump spring-boot-gradle-plugin from 2.0.2.RELEASE to 3.2.5 - Set source/target compatibility to 21 - Replace deprecated compile/testCompile with implementation/testImplementation - Rename bootJar baseName/version to archiveBaseName/archiveVersion - Bump Gradle wrapper from 4.6 to 8.7 (required for Java 21) Co-Authored-By: Phil Bedford <phil.bedford@cognition.ai>
- Bump Spring Boot parent from 2.0.2.RELEASE to 3.2.5 - Bump java.version from 1.8 to 21 - Remove spring-boot-properties-migrator (only needed for 1.x→2.x) - Bump Maven wrapper distribution from 3.3.9 to 3.9.6 (required for Java 21) Co-Authored-By: Phil Bedford <phil.bedford@cognition.ai>
- Fix H2 2.x DROP TABLE syntax (DROP TABLE IF EXISTS customers) - Update deprecated JdbcTemplate.query() to varargs overload - Replace defunct gturnquist-quoters URL with jsonplaceholder placeholder Co-Authored-By: Phil Bedford <phil.bedford@cognition.ai>
🤖 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:
|
|
|
||
| RestTemplate restTemplate = new RestTemplate(); | ||
| Quote quote = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class); | ||
| Quote quote = restTemplate.getForObject("https://jsonplaceholder.typicode.com/posts/1", Quote.class); |
There was a problem hiding this comment.
🔴 New API endpoint response schema doesn't match Quote model, resulting in all-null deserialization
The API URL was changed from the old quoters service to https://jsonplaceholder.typicode.com/posts/1, but the Quote model class was not updated. The JSONPlaceholder endpoint returns {userId, id, title, body}, while Quote (src/main/java/hello/model/Quote.java:3-34) expects type (String) and value (Value), where Value (src/main/java/hello/model/Value.java:3-33) expects id (Long) and quote (String). Since Jackson's FAIL_ON_UNKNOWN_PROPERTIES defaults to false in Jackson 2.12+, the unknown fields are silently ignored and the Quote object is created with type=null and value=null. This affects both the manual RestTemplate call in main() (line 39) and the Spring-managed bean call in run() (lines 52-53), causing the application to log meaningless Quote{type='null', value=null} output instead of actual data.
Prompt for agents
The Quote and Value model classes need to be updated to match the JSONPlaceholder /posts/1 API response format. The API returns JSON like: {userId: 1, id: 1, title: "...", body: "..."}. Either: (1) create a new model class (e.g., Post) with fields userId (int), id (int), title (String), body (String), and update both getForObject calls in Application.java to use Post.class instead of Quote.class, or (2) find a different API that returns data matching the Quote/Value model structure. The affected locations are Application.java line 39 (in main()) and lines 52-53 (in the run() CommandLineRunner bean).
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Acknowledged — this is intentional and matches the original task spec, which permitted "a working placeholder or mock endpoint (e.g., https://jsonplaceholder.typicode.com/posts/1 or a local stub)" precisely because the original gturnquist-quoters.cfapps.io service is permanently dead. The bot's analysis is correct that Quote will deserialize with all null fields against the JSONPlaceholder response shape.
I deliberately did NOT modify the Quote / Value model classes because:
- The migration scope is "Java 8 → Java 21 / Spring Boot 2.0.2 → 3.2.5" — changing the domain model is out of scope.
Quoteis only used in two startup-timeRestTemplatecalls that just log the result; they are not part of any user-facing flow.- Repointing the URL keeps the build green and the app reachable, which is the migration's actual goal. The "all-null
Quote" log line is acceptable as a stub and is explicitly called out in the PR description's testing checklist (item 4).
If the team wants real data flowing through Quote again, the right follow-up is one of: (a) add a tiny local @RestController stub under src/main/java/hello/ returning a Quote-shaped JSON payload and point the RestTemplate at http://localhost:8080/..., or (b) introduce a new Post model and switch getForObject to use it. Either is a separate, non-migration change.
Summary
End-to-end migration of this repo from Java 8 / Spring Boot 2.0.2 to Java 21 / Spring Boot 3.2.5. Work was split across three child Devin sessions and merged onto an integration branch (
devin/1777913688-java21-migration-base); this PR brings that integration branch intomaster.Build configuration — Maven (child PR #20)
pom.xml2.0.2.RELEASE→3.2.5<java.version>:1.8→21spring-boot-properties-migrator(1.x→2.x migration aid; not published for 3.x)spring-boot-starter-web,spring-boot-starter-jdbc,h2) inherit compatible versions from the new parent BOM.mvn/wrapper/maven-wrapper.properties: Maven3.3.9→3.9.6(3.9+ required for Java 21)Build configuration — Gradle (child PR #19)
build.gradlespring-boot-gradle-plugin:2.0.2.RELEASE→3.2.5sourceCompatibility/targetCompatibility:1.8→21compile(...)→implementation(...),testCompile(...)→testImplementation(...)bootJar { baseName / version }→bootJar { archiveBaseName / archiveVersion }gradle/wrapper/gradle-wrapper.properties: Gradle4.6→8.7(8.5+ required for Java 21)Source —
Application.java(child PR #21)DROP TABLE customers IF EXISTS→DROP TABLE IF EXISTS customersJdbcTemplate.query(sql, Object[], RowMapper)with the varargs-lastquery(sql, RowMapper, Object...)http://gturnquist-quoters.cfapps.io/api/random(both occurrences) with the placeholderhttps://jsonplaceholder.typicode.com/posts/1.RestTemplateretained.javax.*imports undersrc/main/java/hello/(none found, nojakarta.*rewrites needed)Local verification under JDK 21
./mvnw clean package -DskipTests→ BUILD SUCCESS. Maven 3.9.6 was auto-downloaded by the wrapper, the Spring Boot 3.2.5 BOM resolved cleanly, all dependencies fetched. Caveat: thepom.xmlcarries<packaging>pom</packaging>(pre-existing), so this phase does NOT actually compilesrc/main/java/hello/*.java— it just runs the lifecycle topackagefor a pom-typed artifact. See "Pre-existing issues" below.javacof all 13 source files under JDK 21 against the maven-resolved classpath → succeeds (all 13.classfiles produced under/tmp/javac-out). This is the strongest signal that the migration's source changes compile cleanly under Java 21 + Spring Boot 3.2.5../gradlew build(Gradle 8.7 auto-downloaded) → fails at:compileJavabecauseorg.springframework.jdbc.core.JdbcTemplateis missing on the classpath. This is pre-existing, not introduced by this migration — see below.Pre-existing issues (NOT introduced by this migration, but worth flagging)
pom.xmlhas<packaging>pom</packaging>— wrong packaging for a Spring Boot app;./mvnw packagedoesn't runmaven-compiler-plugin. Out of scope per the original task spec, but a one-character follow-up (pom→jar) would make./mvnw clean packageactually compile and produce a runnable jar.build.gradleis missingspring-boot-starter-jdbcandh2dependencies — onlyspring-boot-starter-webis declared, so./gradlew compileJavacannot resolveJdbcTemplate. Confirmed present onmasterbefore this PR. The Mavenpom.xmlhas these deps, so Maven has always been the working build path for this repo.Both are tiny pre-existing follow-ups; they do not block the Java 21 migration itself, which is verified by the direct
javaccompile under JDK 21.Review & Testing Checklist for Human
./mvnw clean packageunder JDK 21 locally — should printBUILD SUCCESS(with the pom-packaging caveat above; see follow-up Fix build and startup: correct packaging type, handle defunct external API #1 if you want a real compile + executable jar).spring-boot-starter-jdbcandh2tobuild.gradleso./gradlew buildworks as a second verification path. The fix is trivial: addimplementation("org.springframework.boot:spring-boot-starter-jdbc")andruntimeOnly("com.h2database:h2"). Not in this PR because the user's task spec didn't authorize editingbuild.gradlebeyond the four explicit bullets — happy to do it as a follow-up if desired../mvnw spring-boot:run(or convert packaging tojarfirst), then hitGET /,GET /topic,GET /datetime. Confirm Spring Boot 3.2.5 starts, thecustomerstable loads (logs showInserting customer record for ...), and theJdbcTemplate.query(...)forfirst_name = 'Josh'returns rows — that's the live exercise of both H2 2.xDROP TABLE IF EXISTS customersand the new varargsJdbcTemplate.query().jsonplaceholder.typicode.com/posts/1returns{userId, id, title, body}, which won't fully populateQuote'stype/valuefields. The startup log line will print aQuote{type='null', value=null}. If you want real data flowing, swap to a local@RestControllerstub or introduce a newPostmodel — both out of scope for this PR.Notes
src/test/was absent), so there was nothing to exercise on the test path.Link to Devin session: https://app.devin.ai/sessions/e43cac2398914ee9a147ef4405dcffb3
Requested by: @dr-phil
Devin Review