From 592dfd02b1edf05f8e4b32958777b97ff1a3aaff Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 24 Apr 2026 14:22:24 +0000 Subject: [PATCH] Migrate from Java 8 to Java 15 with Spring Boot 2.7.18 Build & Dependency Changes: - Update Spring Boot 2.0.2 -> 2.7.18 - Update Java version 1.8 -> 15 - Fix packaging from pom to jar - Add maven-compiler-plugin with release 15 - Update Gradle plugin to 2.7.18, compile->implementation, testCompile->testImplementation - Update baseName->archiveBaseName in Gradle bootJar - Update Maven wrapper to 3.9.6, Gradle wrapper to 7.6.4 - Make gradlew/mvnw executable Code Modernization (Java 15 features): - Use text blocks for multi-line strings (SQL, response templates) - Use var for local variable type inference - Replace Arrays.asList() with List.of() - Replace Paths.get() with Path.of() - Use method references where possible (Topic::getId) Bug Fixes (from branch submissions): - Fix defunct external API URL (cfapps.io -> dummyjson.com) - Add null safety and try-catch for external API calls - Fix getTopicWithId to use orElse(null) instead of bare get() README updated to reflect Java 15. Co-Authored-By: parker.duff@codeium.com --- .mvn/wrapper/maven-wrapper.properties | 2 +- README.md | 13 +-- build.gradle | 17 +-- gradle/wrapper/gradle-wrapper.properties | 2 +- gradlew | 0 mvnw | 0 pom.xml | 14 ++- src/main/java/hello/Application.java | 53 +++++---- .../hello/controller/HelloController.java | 103 ++++++++---------- src/main/java/hello/service/TopicService.java | 102 +++++++---------- 10 files changed, 145 insertions(+), 161 deletions(-) mode change 100644 => 100755 gradlew mode change 100644 => 100755 mvnw diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties index c954cec..4465bd9 100644 --- a/.mvn/wrapper/maven-wrapper.properties +++ b/.mvn/wrapper/maven-wrapper.properties @@ -1 +1 @@ -distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.3.9/apache-maven-3.3.9-bin.zip +distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.9.6/apache-maven-3.9.6-bin.zip diff --git a/README.md b/README.md index 2b09400..3897cfe 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ -# springboot-java8 -The project is made on spring boot. The project summarize the new features present in Java 8. +# springboot-java15 +The project is made on spring boot. The project summarizes the features present in Java 8 through Java 15. It contain list of harcoded topics list. You can call the apis's with POSTMAN to add,delete,update Topic list In addition, it uses 1) Java 8 NIO methods @@ -13,6 +13,8 @@ In addition, it uses 9) Default and Static methods in interface 10) Java 8 LocalDateTime API 11) Pattern +12) Java 10+ local variable type inference (var) +13) Java 15 text blocks @@ -76,13 +78,12 @@ GET /datetime ### Prerequisites -1) Java sdk +1) Java 15 SDK 2) POSTMAN ### Installing - ``` 1) Download or clone 2) Import the project @@ -129,6 +130,4 @@ https://dzone.com/articles/java-8-friday-goodies-new-new ## Authors -* **Rehman Murad Ali** - - +* **Rehman Murad Ali** diff --git a/build.gradle b/build.gradle index 09f1083..6839889 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") } } @@ -14,19 +14,20 @@ 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 { mavenCentral() } -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = 15 +targetCompatibility = 15 dependencies { - compile("org.springframework.boot:spring-boot-starter-web") - testCompile("junit:junit") + implementation("org.springframework.boot:spring-boot-starter-web") + implementation("org.springframework.boot:spring-boot-starter-jdbc") + runtimeOnly("com.h2database:h2") + testImplementation("junit:junit") } - diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a2ff3cc..03a8d13 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.4-bin.zip diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 diff --git a/mvnw b/mvnw old mode 100644 new mode 100755 diff --git a/pom.xml b/pom.xml index 63f5cbd..9e270a1 100644 --- a/pom.xml +++ b/pom.xml @@ -5,13 +5,13 @@ org.springframework gs-spring-boot - pom + jar 0.1.0 org.springframework.boot spring-boot-starter-parent - 2.0.2.RELEASE + 2.7.18 @@ -35,12 +35,20 @@ - 1.8 + 15 + + org.apache.maven.plugins + maven-compiler-plugin + 3.8.1 + + 15 + + org.springframework.boot spring-boot-maven-plugin diff --git a/src/main/java/hello/Application.java b/src/main/java/hello/Application.java index 7cf8faf..eed9f0a 100644 --- a/src/main/java/hello/Application.java +++ b/src/main/java/hello/Application.java @@ -25,19 +25,25 @@ public class Application implements CommandLineRunner { public static void main(String[] args) { - ApplicationContext ctx = SpringApplication.run(Application.class, args); - + var ctx = SpringApplication.run(Application.class, args); + System.out.println("Let's inspect the beans provided by Spring Boot:"); - - String[] beanNames = ctx.getBeanDefinitionNames(); + + var beanNames = ctx.getBeanDefinitionNames(); Arrays.sort(beanNames); - for (String beanName : beanNames) { + for (var 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()); + try { + var restTemplate = new RestTemplate(); + var quote = restTemplate.getForObject("https://dummyjson.com/quotes/random", Quote.class); + if (quote != null) { + log.info(quote.toString()); + } + } catch (Exception e) { + log.warn("Could not fetch quote from external service: {}", e.getMessage()); + } } @@ -49,9 +55,15 @@ public RestTemplate restTemplate(RestTemplateBuilder builder) { @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()); + try { + var quote = restTemplate.getForObject( + "https://dummyjson.com/quotes/random", Quote.class); + if (quote != null) { + log.info(quote.toString()); + } + } catch (Exception e) { + log.warn("Could not fetch quote from external service: {}", e.getMessage()); + } }; } @@ -64,23 +76,26 @@ 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))"); - - // 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") + jdbcTemplate.execute(""" + CREATE TABLE customers( + id SERIAL, + first_name VARCHAR(255), + last_name VARCHAR(255) + )"""); + + List splitUpNames = List.of("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long") .stream() - .map(name -> name.split(" ")) + .map(name -> (Object[]) 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':"); jdbcTemplate.query( - "SELECT id, first_name, last_name FROM customers WHERE first_name = ?", new Object[]{"Josh"}, + "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/controller/HelloController.java b/src/main/java/hello/controller/HelloController.java index f3602fa..051bd17 100644 --- a/src/main/java/hello/controller/HelloController.java +++ b/src/main/java/hello/controller/HelloController.java @@ -2,100 +2,89 @@ 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 { - - String joinTemplate = "Joining All String ID's with JOIN method: "; - String makeDistinctAndSortCharactersTemplate = "-------------Get all ID characters, select distict and sort with ID= "; - String splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoinTemplate = "-------------Split All Id With Colon," + - "Select ID With \"Java\" Keyword," + - " Then Sort Then Join "; - String findIdHavingCharacterTemplate = "-------------Return All ID having character \'g\' in it: "; - String findAllFilesInPathAndSortTemplate = "---------Find all files in path and sort: "; - String findParticularFileInPathAndSortTemplate = "----------Find File in present directory which strats with \"grad\",provided maximum depth=25 and sort : "; - String findParticularFileInPathAndSortWithWalkFunctionTemplate = "----------Find File in present directory which strats with \"grad\",provided maximum depth=25 and sort : with walk function"; - String readFileWithStreamFunctionTemplate = "---------Read \"temp.txt\" file with stream functions, having \"print\" witin it: "; - - @Autowired private TopicService topicService; /** * Java 8 Date Time example - * - * @return */ @RequestMapping("/datetime") public String index() { - TimeClient myTimeClient = new SimpleTimeClient(); - LocalDateTime localDateTime = LocalDateTime.now(); - return "Greetings from Spring Boot! ----------------------" + - "Datetime now is " + String.valueOf(myTimeClient.toString()) + "----------------------" + - "Datetime tomorrow will be " + String.valueOf(myTimeClient.getLocalDateTime().plusDays(1)) + "----------------------" + - "Datetime of previous month was " + String.valueOf(myTimeClient.getLocalDateTime().minus(1, ChronoUnit.MONTHS)) + "----------------------" + - "Is this a leap year ? " + String.valueOf(LocalDate.now().isLeapYear()) + "----------------------" + - "Default system zone id " + String.valueOf(ZoneId.systemDefault()) + "-------------------" + - "Time in California: " + myTimeClient.getZonedDateTime("Canada/Central").toString(); - + var myTimeClient = new SimpleTimeClient(); + return """ + Greetings from Spring Boot! ----------------------\ + Datetime now is %s----------------------\ + Datetime tomorrow will be %s----------------------\ + Datetime of previous month was %s----------------------\ + Is this a leap year ? %s----------------------\ + Default system zone id %s-------------------\ + Time in California: %s""".formatted( + myTimeClient, + myTimeClient.getLocalDateTime().plusDays(1), + myTimeClient.getLocalDateTime().minus(1, ChronoUnit.MONTHS), + LocalDate.now().isLeapYear(), + ZoneId.systemDefault(), + myTimeClient.getZonedDateTime("Canada/Central")); } /** * String Operations in Java 8 - * - * @return */ @RequestMapping("/topic/string/operation") public String showStringOperation() { - - String join = topicService.returnAllTopicIDWithStringSlicing(); - String makeDistinctAndSortCharacters = topicService.makeDistinctAndSortCharacters(join); - String splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin = topicService + var join = topicService.returnAllTopicIDWithStringSlicing(); + var makeDistinctAndSortCharacters = topicService.makeDistinctAndSortCharacters(join); + var splitResult = topicService .splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(join); - String findIdHavingCharacter = topicService.findIdHavingCharacter(); - - return joinTemplate + join - + makeDistinctAndSortCharactersTemplate + makeDistinctAndSortCharacters - + splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoinTemplate + splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin - + findIdHavingCharacterTemplate + findIdHavingCharacter; - + var findIdHavingCharacter = topicService.findIdHavingCharacter(); + + return """ + Joining All String ID's with JOIN method: %s\ + -------------Get all ID characters, select distict and sort with ID= %s\ + -------------Split All Id With Colon,\ + Select ID With "Java" Keyword,\ + Then Sort Then Join %s\ + -------------Return All ID having character 'g' in it: %s""".formatted( + join, + makeDistinctAndSortCharacters, + splitResult, + findIdHavingCharacter); } /** * File Operation in Java 8 - * @return */ @RequestMapping("/topic/file/operation") public String showFileOperation() { - String findAllFilesInPathAndSort = topicService.findAllFilesInPathAndSort(); - String findParticularFileInPathAndSort = topicService.findParticularFileInPathAndSort(); - String findParticularFileInPathAndSortWithWalkFunction = topicService.findParticularFileInPathAndSortWithWalkFunction(); - String readFileWithStreamFunction = topicService.readFileWithStreamFunction(); - return findAllFilesInPathAndSortTemplate + findAllFilesInPathAndSort - + findParticularFileInPathAndSortTemplate + findParticularFileInPathAndSort - + findParticularFileInPathAndSortWithWalkFunctionTemplate + findParticularFileInPathAndSortWithWalkFunction - + readFileWithStreamFunctionTemplate + readFileWithStreamFunction; + var findAllFilesInPathAndSort = topicService.findAllFilesInPathAndSort(); + var findParticularFileInPathAndSort = topicService.findParticularFileInPathAndSort(); + var findWithWalk = topicService.findParticularFileInPathAndSortWithWalkFunction(); + var readFile = topicService.readFileWithStreamFunction(); + return """ + ---------Find all files in path and sort: %s\ + ----------Find File in present directory which strats with "grad",\ + provided maximum depth=25 and sort : %s\ + ----------Find File in present directory which strats with "grad",\ + provided maximum depth=25 and sort : with walk function%s\ + ---------Read "temp.txt" file with stream functions, having "print" witin it: %s""".formatted( + findAllFilesInPathAndSort, + findParticularFileInPathAndSort, + findWithWalk, + readFile); } - - } diff --git a/src/main/java/hello/service/TopicService.java b/src/main/java/hello/service/TopicService.java index feffb92..e15c8ab 100644 --- a/src/main/java/hello/service/TopicService.java +++ b/src/main/java/hello/service/TopicService.java @@ -8,7 +8,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.nio.file.Paths; import java.util.*; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -19,7 +18,7 @@ public class TopicService { - private List topics = new ArrayList<>(Arrays.asList( + private final List topics = new ArrayList<>(List.of( new Topic("spring", "Spring Framework", "Spring Framework Description"), new Topic("java", "Core Java", "Java Description"), new Topic("javascript", "javascript Framework", "javascript Framework Description") @@ -30,13 +29,13 @@ public List getAllTopics() { } /** - * Strean Example - * - * @param id - * @return + * Stream Example */ 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() + .orElse(null); } public void addTopic(Topic topic) { @@ -45,29 +44,25 @@ public void addTopic(Topic topic) { /** * IntStream examples - * - * @param id - * @param topic */ public void updateTopic(String id, Topic topic) { - OptionalInt indexOfElement = IntStream.range(0, topics.size()).filter(index -> id.equals(topics.get(index).getId())).findFirst(); - if (indexOfElement.isPresent()) topics.set(indexOfElement.getAsInt(), topic); + var indexOfElement = IntStream.range(0, topics.size()) + .filter(index -> id.equals(topics.get(index).getId())) + .findFirst(); + if (indexOfElement.isPresent()) { + topics.set(indexOfElement.getAsInt(), topic); + } } /** - * Lamda Expressions - * - * @param id + * Lambda Expressions */ public void deleteTopic(String id) { topics.removeIf(topic -> topic.getId().equals(id)); } /** - * Calling fucntional Interface - * - * @param minLength - * @return + * Calling functional Interface */ public List filterMinimumLengthForId(Integer minLength) { return printTopicsWithPredicate(topics, topic -> topic.getId().length() > minLength); @@ -76,13 +71,9 @@ public List filterMinimumLengthForId(Integer minLength) { /** * Functional Interface example With ForEach - * - * @param topicList - * @param tester - * @return */ private static List printTopicsWithPredicate(List topicList, CustomPredicate tester) { - List resultTopic = new ArrayList<>(); + var resultTopic = new ArrayList(); topicList.forEach(topic -> { if (tester.test(topic)) resultTopic.add(topic); }); @@ -92,8 +83,6 @@ private static List printTopicsWithPredicate(List topicList, Custo /** * Using Comparator to sort - * - * @return */ public List sortTopicsWithID() { topics.sort(Comparator.comparing(Topic::getId)); @@ -103,20 +92,17 @@ public List sortTopicsWithID() { /** * Join List of Strings - * - * @return */ public String returnAllTopicIDWithStringSlicing() { - List topicIds = topics.stream().map(topic -> topic.getId()).collect(Collectors.toList()); + var topicIds = topics.stream() + .map(Topic::getId) + .collect(Collectors.toList()); return String.join(":", topicIds); } /** * Use of MapToObject and distinct - * - * @param join - * @return */ public String makeDistinctAndSortCharacters(String join) { return join.chars().distinct() @@ -128,9 +114,6 @@ public String makeDistinctAndSortCharacters(String join) { /** * Use of Pattern Class with stream - * - * @param join - * @return */ public String splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(String join) { return Pattern.compile(":") @@ -143,13 +126,12 @@ public String splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(String /** * Apply Regex as Predicate with Stream - * @return */ public String findIdHavingCharacter() { - Pattern pattern = Pattern.compile(".*g.*"); - Object[] topicIdObjectList = topics.stream().map(topic -> topic.getId()).collect(Collectors.toList()).toArray(); - - String[] topicIdList = Arrays.stream(topicIdObjectList).toArray(String[]::new); + var pattern = Pattern.compile(".*g.*"); + var topicIdList = topics.stream() + .map(Topic::getId) + .toArray(String[]::new); return Stream.of(topicIdList) .filter(pattern.asPredicate()) @@ -162,17 +144,14 @@ public String findIdHavingCharacter() { * NIO Java API * Use Streams with files * Try with resource, "Autoclose" - * - * @return */ public String findAllFilesInPathAndSort() { - try (Stream stream = Files.list(Paths.get(""))) { - String joined = stream + try (var stream = Files.list(Path.of(""))) { + return stream .map(String::valueOf) .filter(path -> !path.startsWith(".")) .sorted() .collect(Collectors.joining("; ")); - return joined; } catch (IOException e) { return " Error in IO"; } @@ -180,18 +159,16 @@ public String findAllFilesInPathAndSort() { /** * Using File.find function to find file - * @return */ public String findParticularFileInPathAndSort() { - Path start = Paths.get(""); - int maxDepth = 25; - try (Stream stream = Files.find(start, maxDepth, (path, attr) -> + var start = Path.of(""); + var maxDepth = 25; + try (var stream = Files.find(start, maxDepth, (path, attr) -> String.valueOf(path).startsWith("grad"))) { - String joined = stream + return stream .sorted() .map(String::valueOf) .collect(Collectors.joining("; ")); - return joined; } catch (IOException e) { return " IO exception "; } @@ -200,18 +177,16 @@ public String findParticularFileInPathAndSort() { /** * Using Files.Walk Function to find File - * @return */ public String findParticularFileInPathAndSortWithWalkFunction() { - Path start = Paths.get(""); - int maxDepth = 5; - try (Stream stream = Files.walk(start, maxDepth)) { - String joined = stream + var start = Path.of(""); + var maxDepth = 5; + try (var stream = Files.walk(start, maxDepth)) { + return stream .map(String::valueOf) .filter(path -> path.startsWith("grad")) .sorted() .collect(Collectors.joining("; ")); - return joined; } catch (IOException e) { return " IO exception "; } @@ -220,18 +195,15 @@ public String findParticularFileInPathAndSortWithWalkFunction() { /** * Use BufferedReader with Stream functions - * @return */ public String readFileWithStreamFunction() { - Path path = Paths.get("temp.txt"); - System.out.println(); - try (BufferedReader reader = Files.newBufferedReader(path)) { - String lines = reader + var path = Path.of("temp.txt"); + try (var reader = Files.newBufferedReader(path)) { + return reader .lines() - .filter(line->line.contains("print")) - .map(line->line.substring("print".length())) + .filter(line -> line.contains("print")) + .map(line -> line.substring("print".length())) .collect(Collectors.joining(",")); - return lines; } catch (IOException e) { return " IO exception "; }