From e7bd5ce2c76ccee9254f7d683519131d34d9fbf3 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 14:03:58 +0000 Subject: [PATCH 1/5] Phase 1: Upgrade to Spring Boot 2.7.18 + Java 17 - Update pom.xml: spring-boot-starter-parent 2.0.2.RELEASE -> 2.7.18, java.version 1.8 -> 17 - Update build.gradle: plugin 2.0.2.RELEASE -> 2.7.18, source/targetCompatibility -> 17, compile -> implementation, testCompile -> testImplementation - Remove broken external API call to retired gturnquist-quoters.cfapps.io - Remove Quote and Value model classes (no longer needed) - Simplify main() to just SpringApplication.run() - Remove redundant RestTemplate instantiation and CommandLineRunner bean for quotes Co-Authored-By: Bobby Nobakht --- build.gradle | 10 ++++---- pom.xml | 4 ++-- src/main/java/hello/Application.java | 32 +------------------------- src/main/java/hello/model/Quote.java | 34 ---------------------------- src/main/java/hello/model/Value.java | 33 --------------------------- 5 files changed, 8 insertions(+), 105 deletions(-) delete mode 100644 src/main/java/hello/model/Quote.java delete mode 100644 src/main/java/hello/model/Value.java diff --git a/build.gradle b/build.gradle index 09f1083..668b89c 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { mavenCentral() } dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.2.RELEASE") + classpath("org.springframework.boot:spring-boot-gradle-plugin:2.7.18") } } @@ -22,11 +22,11 @@ repositories { mavenCentral() } -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = 17 +targetCompatibility = 17 dependencies { - compile("org.springframework.boot:spring-boot-starter-web") - testCompile("junit:junit") + implementation("org.springframework.boot:spring-boot-starter-web") + testImplementation("junit:junit") } diff --git a/pom.xml b/pom.xml index 63f5cbd..c84565b 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.0.2.RELEASE + 2.7.18 @@ -35,7 +35,7 @@ - 1.8 + 17 diff --git a/src/main/java/hello/Application.java b/src/main/java/hello/Application.java index 7cf8faf..005e291 100644 --- a/src/main/java/hello/Application.java +++ b/src/main/java/hello/Application.java @@ -5,7 +5,6 @@ import java.util.stream.Collectors; import hello.model.Customer; -import hello.model.Quote; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -13,7 +12,6 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.web.client.RestTemplate; @@ -24,38 +22,14 @@ public class Application implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(Application.class); public static void main(String[] args) { - - ApplicationContext ctx = SpringApplication.run(Application.class, args); - - System.out.println("Let's inspect the beans provided by Spring Boot:"); - - String[] beanNames = ctx.getBeanDefinitionNames(); - Arrays.sort(beanNames); - for (String beanName : beanNames) { - System.out.println(beanName); - } - - RestTemplate restTemplate = new RestTemplate(); - Quote quote = restTemplate.getForObject("http://gturnquist-quoters.cfapps.io/api/random", Quote.class); - log.info(quote.toString()); + SpringApplication.run(Application.class, args); } - @Bean public RestTemplate restTemplate(RestTemplateBuilder builder) { return builder.build(); } - @Bean - public CommandLineRunner run(RestTemplate restTemplate) throws Exception { - return args -> { - Quote quote = restTemplate.getForObject( - "http://gturnquist-quoters.cfapps.io/api/random", Quote.class); - log.info(quote.toString()); - }; - } - - @Autowired JdbcTemplate jdbcTemplate; @@ -66,16 +40,13 @@ public void run(String... args) throws Exception { jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); jdbcTemplate.execute("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); - // Split up the array of whole names into an array of first/last names List splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long") .stream() .map(name -> name.split(" ")) .collect(Collectors.toList()); - // Use a Java 8 stream to print out each tuple of the list splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1]))); - // Uses JdbcTemplate's batchUpdate operation to bulk load data jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames); log.info("Querying for customer records where first_name = 'Josh':"); @@ -83,6 +54,5 @@ public void run(String... args) throws Exception { "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{"Josh"}, (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")) ).forEach(customer -> log.info(customer.toString())); - } } diff --git a/src/main/java/hello/model/Quote.java b/src/main/java/hello/model/Quote.java deleted file mode 100644 index 2178a6d..0000000 --- a/src/main/java/hello/model/Quote.java +++ /dev/null @@ -1,34 +0,0 @@ -package hello.model; - -public class Quote { - private String type; - private Value value; - - @Override - public String toString() { - return "Quote{" + - "type='" + type + '\'' + - ", value=" + value + - '}'; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Value getValue() { - return value; - } - - public void setValue(Value value) { - this.value = value; - } - - public Quote() { - - } -} diff --git a/src/main/java/hello/model/Value.java b/src/main/java/hello/model/Value.java deleted file mode 100644 index 272be70..0000000 --- a/src/main/java/hello/model/Value.java +++ /dev/null @@ -1,33 +0,0 @@ -package hello.model; - -public class Value { - private Long id; - private String quote; - - public Value() { - } - - @Override - public String toString() { - return "Value{" + - "id=" + id + - ", quote='" + quote + '\'' + - '}'; - } - - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getQuote() { - return quote; - } - - public void setQuote(String quote) { - this.quote = quote; - } -} From 18498f11cc34b4f6b6416a8cc7cf7fc5504b3d77 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 14:04:17 +0000 Subject: [PATCH 2/5] Phase 2: Upgrade to Spring Boot 3.2.5 + Jakarta EE - Update pom.xml: spring-boot-starter-parent 2.7.18 -> 3.2.5 - Update build.gradle: replace buildscript/apply plugin with modern plugins DSL, Spring Boot 3.2.5, dependency-management 1.1.5 - Replace deprecated bootJar baseName/version with archiveBaseName/archiveVersion - No javax imports found requiring jakarta migration Co-Authored-By: Bobby Nobakht --- build.gradle | 24 ++++++++---------------- pom.xml | 2 +- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/build.gradle b/build.gradle index 668b89c..5c8c142 100644 --- a/build.gradle +++ b/build.gradle @@ -1,21 +1,14 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:2.7.18") - } +plugins { + id 'java' + id 'eclipse' + id 'idea' + id 'org.springframework.boot' version '3.2.5' + id 'io.spring.dependency-management' version '1.1.5' } -apply plugin: 'java' -apply plugin: 'eclipse' -apply plugin: 'idea' -apply plugin: 'org.springframework.boot' -apply plugin: 'io.spring.dependency-management' - bootJar { - baseName = 'gs-spring-boot' - version = '0.1.0' + archiveBaseName = 'gs-spring-boot' + archiveVersion = '0.1.0' } repositories { @@ -29,4 +22,3 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") testImplementation("junit:junit") } - diff --git a/pom.xml b/pom.xml index c84565b..919afd1 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ org.springframework.boot spring-boot-starter-parent - 2.7.18 + 3.2.5 From 527fbebb530ded41bb2475ae8a958b9cc35174f2 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 14:04:56 +0000 Subject: [PATCH 3/5] Phase 3: Code quality improvements - Fix unsafe Optional.get() with orElseThrow() in TopicService.getTopicWithId() - Add toString(), equals(), hashCode() to Topic model - Remove unused imports (Pattern, Collector, ChronoPeriod, etc.) from HelloController - Fix thread safety: ArrayList -> CopyOnWriteArrayList in TopicService - Return sorted copy instead of sorting shared list in sortTopicsWithID() - Extract JDBC DataInitializer into separate @Component class - Extract RestTemplate @Bean into AppConfig configuration class - Simplify Application.java to just main() with SpringApplication.run() - Remove bean-listing debug System.out.println loop from main() Co-Authored-By: Bobby Nobakht --- src/main/java/hello/Application.java | 48 +------------------ src/main/java/hello/DataInitializer.java | 45 +++++++++++++++++ src/main/java/hello/config/AppConfig.java | 15 ++++++ .../hello/controller/HelloController.java | 8 ---- src/main/java/hello/model/Topic.java | 24 ++++++++++ src/main/java/hello/service/TopicService.java | 11 +++-- 6 files changed, 92 insertions(+), 59 deletions(-) create mode 100644 src/main/java/hello/DataInitializer.java create mode 100644 src/main/java/hello/config/AppConfig.java diff --git a/src/main/java/hello/Application.java b/src/main/java/hello/Application.java index 005e291..5abd411 100644 --- a/src/main/java/hello/Application.java +++ b/src/main/java/hello/Application.java @@ -1,58 +1,12 @@ package hello; -import java.util.Arrays; -import java.util.List; -import java.util.stream.Collectors; - -import hello.model.Customer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.client.RestTemplateBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.client.RestTemplate; @SpringBootApplication -public class Application implements CommandLineRunner { - - private static final Logger log = LoggerFactory.getLogger(Application.class); +public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } - - @Bean - public RestTemplate restTemplate(RestTemplateBuilder builder) { - return builder.build(); - } - - @Autowired - JdbcTemplate jdbcTemplate; - - @Override - public void run(String... args) throws Exception { - log.info("Creating tables"); - - jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); - jdbcTemplate.execute("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); - - List splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long") - .stream() - .map(name -> name.split(" ")) - .collect(Collectors.toList()); - - splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1]))); - - jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames); - - log.info("Querying for customer records where first_name = 'Josh':"); - jdbcTemplate.query( - "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{"Josh"}, - (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")) - ).forEach(customer -> log.info(customer.toString())); - } } diff --git a/src/main/java/hello/DataInitializer.java b/src/main/java/hello/DataInitializer.java new file mode 100644 index 0000000..88592e9 --- /dev/null +++ b/src/main/java/hello/DataInitializer.java @@ -0,0 +1,45 @@ +package hello; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import hello.model.Customer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +@Component +public class DataInitializer implements CommandLineRunner { + + private static final Logger log = LoggerFactory.getLogger(DataInitializer.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Override + public void run(String... args) throws Exception { + log.info("Creating tables"); + + jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); + jdbcTemplate.execute("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); + + List splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long") + .stream() + .map(name -> name.split(" ")) + .collect(Collectors.toList()); + + splitUpNames.forEach(name -> log.info(String.format("Inserting customer record for %s %s", name[0], name[1]))); + + jdbcTemplate.batchUpdate("INSERT INTO customers(first_name, last_name) VALUES (?,?)", splitUpNames); + + log.info("Querying for customer records where first_name = 'Josh':"); + jdbcTemplate.query( + "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{"Josh"}, + (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")) + ).forEach(customer -> log.info(customer.toString())); + } +} diff --git a/src/main/java/hello/config/AppConfig.java b/src/main/java/hello/config/AppConfig.java new file mode 100644 index 0000000..e7dd37c --- /dev/null +++ b/src/main/java/hello/config/AppConfig.java @@ -0,0 +1,15 @@ +package hello.config; + +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +@Configuration +public class AppConfig { + + @Bean + public RestTemplate restTemplate(RestTemplateBuilder builder) { + return builder.build(); + } +} diff --git a/src/main/java/hello/controller/HelloController.java b/src/main/java/hello/controller/HelloController.java index f3602fa..f261683 100644 --- a/src/main/java/hello/controller/HelloController.java +++ b/src/main/java/hello/controller/HelloController.java @@ -2,22 +2,14 @@ import hello.declaration.TimeClient; import hello.model.SimpleTimeClient; -import hello.model.Topic; import hello.service.TopicService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.LocalTime; import java.time.ZoneId; -import java.time.chrono.ChronoPeriod; import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.regex.Pattern; -import java.util.stream.Collector; -import java.util.stream.Collectors; @RestController public class HelloController { diff --git a/src/main/java/hello/model/Topic.java b/src/main/java/hello/model/Topic.java index 483a4a7..6f04ef3 100644 --- a/src/main/java/hello/model/Topic.java +++ b/src/main/java/hello/model/Topic.java @@ -1,5 +1,6 @@ package hello.model; +import java.util.Objects; public class Topic { private String id; @@ -40,4 +41,27 @@ public Topic(String id, String subjectName, String subjectDescription) { this.subjectDescription = subjectDescription; } + @Override + public String toString() { + return "Topic{" + + "id='" + id + '\'' + + ", subjectName='" + subjectName + '\'' + + ", subjectDescription='" + subjectDescription + '\'' + + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Topic topic = (Topic) o; + return Objects.equals(id, topic.id) && + Objects.equals(subjectName, topic.subjectName) && + Objects.equals(subjectDescription, topic.subjectDescription); + } + + @Override + public int hashCode() { + return Objects.hash(id, subjectName, subjectDescription); + } } diff --git a/src/main/java/hello/service/TopicService.java b/src/main/java/hello/service/TopicService.java index feffb92..9bc62f4 100644 --- a/src/main/java/hello/service/TopicService.java +++ b/src/main/java/hello/service/TopicService.java @@ -10,6 +10,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -19,7 +20,7 @@ public class TopicService { - private List topics = new ArrayList<>(Arrays.asList( + private List topics = new CopyOnWriteArrayList<>(Arrays.asList( new Topic("spring", "Spring Framework", "Spring Framework Description"), new Topic("java", "Core Java", "Java Description"), new Topic("javascript", "javascript Framework", "javascript Framework Description") @@ -36,7 +37,8 @@ public List getAllTopics() { * @return */ public Topic getTopicWithId(String id) { - return topics.stream().filter(topic -> topic.getId().equals(id)).findFirst().get(); + return topics.stream().filter(topic -> topic.getId().equals(id)).findFirst() + .orElseThrow(() -> new NoSuchElementException("Topic not found with id: " + id)); } public void addTopic(Topic topic) { @@ -96,8 +98,9 @@ private static List printTopicsWithPredicate(List topicList, Custo * @return */ public List sortTopicsWithID() { - topics.sort(Comparator.comparing(Topic::getId)); - return topics; + List sorted = new ArrayList<>(topics); + sorted.sort(Comparator.comparing(Topic::getId)); + return sorted; } From c2652a215c3cbb415ec44c328dfd83d683b014ce Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 14:09:16 +0000 Subject: [PATCH 4/5] Fix packaging type and restore LocalDateTime import - Change packaging from 'pom' to 'jar' so Maven properly compiles sources - Restore LocalDateTime import that was incorrectly removed (used in index() method) Co-Authored-By: Bobby Nobakht --- pom.xml | 2 +- src/main/java/hello/controller/HelloController.java | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 919afd1..f0b6dbf 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework gs-spring-boot - pom + jar 0.1.0 diff --git a/src/main/java/hello/controller/HelloController.java b/src/main/java/hello/controller/HelloController.java index f261683..853f222 100644 --- a/src/main/java/hello/controller/HelloController.java +++ b/src/main/java/hello/controller/HelloController.java @@ -8,6 +8,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.ZoneId; import java.time.temporal.ChronoUnit; From 236cdf47fee287ac630340e185ccd0bc7deb0e2b Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 14 May 2026 14:10:43 +0000 Subject: [PATCH 5/5] Fix H2 2.x SQL syntax for DROP TABLE H2 2.x (bundled with Spring Boot 3.x) removed the proprietary 'DROP TABLE customers IF EXISTS' syntax. Use standard SQL 'DROP TABLE IF EXISTS customers' instead. Co-Authored-By: Bobby Nobakht --- src/main/java/hello/DataInitializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/hello/DataInitializer.java b/src/main/java/hello/DataInitializer.java index 88592e9..f310b41 100644 --- a/src/main/java/hello/DataInitializer.java +++ b/src/main/java/hello/DataInitializer.java @@ -24,7 +24,7 @@ public class DataInitializer implements CommandLineRunner { public void run(String... args) throws Exception { log.info("Creating tables"); - jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); + jdbcTemplate.execute("DROP TABLE IF EXISTS customers"); jdbcTemplate.execute("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); List splitUpNames = Arrays.asList("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long")