From f21ef859c378b6ad9ae4571f5ac7c518c64ec86c Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:34:26 +0000 Subject: [PATCH 1/2] Upgrade to Java 25 and Spring Boot 3.5; modernize code - Java 8 -> Java 25 (LTS), Spring Boot 2.0.2 -> 3.5.0 - Remove legacy Gradle config (keep Maven) - Drop obsolete spring-boot-properties-migrator - Add Spring Boot Actuator and springdoc-openapi - Convert DTOs to records (Topic, Greeting, Value, Customer, Quote) - Switch to specific HTTP mappings (@GetMapping/@PostMapping/...) - Constructor injection; RequestMapping base path on TopicController - Fix NPE in getTopicWithId: return Optional, 404 via ResponseStatusException - CopyOnWriteArrayList for thread-safe in-memory store; immutable getAllTopics - Remove dead gturnquist-quoters.cfapps.io REST call from Application - Guard JDBC CommandLineRunner with @ConditionalOnBean(JdbcTemplate.class) - Add JUnit 5 tests (TopicService + TopicController @WebMvcTest + context) - Add GitHub Actions CI on JDK 25 - Hygiene: expand .gitignore; remove IDE/.iml/target/.idea; refresh README Co-Authored-By: Bobby Nobakht --- .github/workflows/ci.yml | 23 + .gitignore | 19 +- .gitignore.txt | 1 - .idea/.name | 1 - .idea/compiler.xml | 8 - .idea/encodings.xml | 7 - ...__ch_qos_logback_logback_classic_1_2_3.xml | 13 - ...ven__ch_qos_logback_logback_core_1_2_3.xml | 13 - .../Maven__com_fasterxml_classmate_1_3_4.xml | 13 - ...jackson_core_jackson_annotations_2_9_0.xml | 13 - ...terxml_jackson_core_jackson_core_2_9_5.xml | 13 - ...ml_jackson_core_jackson_databind_2_9_5.xml | 13 - ...n_datatype_jackson_datatype_jdk8_2_9_5.xml | 13 - ...datatype_jackson_datatype_jsr310_2_9_5.xml | 13 - ...e_jackson_module_parameter_names_2_9_5.xml | 13 - .../Maven__com_h2database_h2_1_4_197.xml | 13 - .../Maven__com_zaxxer_HikariCP_2_7_9.xml | 13 - ..._annotation_javax_annotation_api_1_3_2.xml | 13 - ..._validation_validation_api_2_0_1_Final.xml | 13 - ..._apache_logging_log4j_log4j_api_2_10_0.xml | 13 - ...he_logging_log4j_log4j_to_slf4j_2_10_0.xml | 13 - ..._tomcat_embed_tomcat_embed_core_8_5_31.xml | 13 - ...he_tomcat_embed_tomcat_embed_el_8_5_31.xml | 13 - ...at_embed_tomcat_embed_websocket_8_5_31.xml | 13 - ...idator_hibernate_validator_6_0_9_Final.xml | 13 - ...boss_logging_jboss_logging_3_3_2_Final.xml | 13 - .../Maven__org_slf4j_jul_to_slf4j_1_7_25.xml | 13 - .../Maven__org_slf4j_slf4j_api_1_7_25.xml | 13 - ...amework_boot_spring_boot_2_0_2_RELEASE.xml | 13 - ...pring_boot_autoconfigure_2_0_2_RELEASE.xml | 13 - ...boot_spring_boot_starter_2_0_2_RELEASE.xml | 13 - ...spring_boot_starter_jdbc_2_0_2_RELEASE.xml | 13 - ...spring_boot_starter_json_2_0_2_RELEASE.xml | 13 - ...ing_boot_starter_logging_2_0_2_RELEASE.xml | 13 - ...ring_boot_starter_tomcat_2_0_2_RELEASE.xml | 13 - ..._spring_boot_starter_web_2_0_2_RELEASE.xml | 13 - ...ringframework_spring_aop_5_0_6_RELEASE.xml | 13 - ...ngframework_spring_beans_5_0_6_RELEASE.xml | 13 - ...framework_spring_context_5_0_6_RELEASE.xml | 13 - ...ingframework_spring_core_5_0_6_RELEASE.xml | 13 - ...mework_spring_expression_5_0_6_RELEASE.xml | 13 - ...ringframework_spring_jcl_5_0_6_RELEASE.xml | 13 - ...ingframework_spring_jdbc_5_0_6_RELEASE.xml | 13 - ...pringframework_spring_tx_5_0_6_RELEASE.xml | 13 - ...ringframework_spring_web_5_0_6_RELEASE.xml | 13 - ...gframework_spring_webmvc_5_0_6_RELEASE.xml | 13 - .../Maven__org_yaml_snakeyaml_1_19.xml | 13 - .idea/misc.xml | 18 - .idea/modules.xml | 8 - .idea/uiDesigner.xml | 124 --- .idea/workspace.xml | 943 ------------------ README.md | 61 +- application.properties | 1 - build.gradle | 32 - gradle/wrapper/gradle-wrapper.jar | Bin 53556 -> 0 bytes gradle/wrapper/gradle-wrapper.properties | 6 - gradlew | 164 --- gradlew.bat | 90 -- gs-spring-boot.iml | 58 -- pom.xml | 50 +- src/main/java/hello/Application.java | 85 +- .../hello/controller/GreetingController.java | 11 +- .../hello/controller/HelloController.java | 115 +-- .../hello/controller/TopicController.java | 77 +- .../java/hello/declaration/TimeClient.java | 17 +- src/main/java/hello/model/Customer.java | 43 +- src/main/java/hello/model/Greeting.java | 19 +- src/main/java/hello/model/Quote.java | 32 +- src/main/java/hello/model/Topic.java | 41 +- src/main/java/hello/model/Value.java | 31 +- src/main/java/hello/service/TopicService.java | 175 +--- src/main/resources/application.properties | 6 + src/test/java/hello/ApplicationTests.java | 12 + .../hello/controller/TopicControllerTest.java | 84 ++ .../java/hello/service/TopicServiceTest.java | 97 ++ target/classes/hello/Application.class | Bin 6460 -> 0 bytes .../hello/controller/GreetingController.class | Bin 1219 -> 0 bytes .../hello/controller/HelloController.class | Bin 4496 -> 0 bytes .../hello/controller/TopicController.class | Bin 2344 -> 0 bytes .../hello/declaration/CustomPredicate.class | Bin 248 -> 0 bytes .../hello/declaration/TimeClient.class | Bin 1383 -> 0 bytes target/classes/hello/model/Customer.class | Bin 1407 -> 0 bytes target/classes/hello/model/Greeting.class | Bin 588 -> 0 bytes target/classes/hello/model/Quote.class | Bin 1128 -> 0 bytes .../hello/model/SimpleTimeClient.class | Bin 1720 -> 0 bytes target/classes/hello/model/Topic.class | Bin 1121 -> 0 bytes target/classes/hello/model/Value.class | Bin 1111 -> 0 bytes .../classes/hello/service/TopicService.class | Bin 13054 -> 0 bytes target/classes/public/index.html | 10 - temp.txt | 9 - 90 files changed, 472 insertions(+), 2539 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .gitignore.txt delete mode 100644 .idea/.name delete mode 100644 .idea/compiler.xml delete mode 100644 .idea/encodings.xml delete mode 100644 .idea/libraries/Maven__ch_qos_logback_logback_classic_1_2_3.xml delete mode 100644 .idea/libraries/Maven__ch_qos_logback_logback_core_1_2_3.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_classmate_1_3_4.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_9_0.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_9_5.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_9_5.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_9_5.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_9_5.xml delete mode 100644 .idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_9_5.xml delete mode 100644 .idea/libraries/Maven__com_h2database_h2_1_4_197.xml delete mode 100644 .idea/libraries/Maven__com_zaxxer_HikariCP_2_7_9.xml delete mode 100644 .idea/libraries/Maven__javax_annotation_javax_annotation_api_1_3_2.xml delete mode 100644 .idea/libraries/Maven__javax_validation_validation_api_2_0_1_Final.xml delete mode 100644 .idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_10_0.xml delete mode 100644 .idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_10_0.xml delete mode 100644 .idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_8_5_31.xml delete mode 100644 .idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_8_5_31.xml delete mode 100644 .idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_8_5_31.xml delete mode 100644 .idea/libraries/Maven__org_hibernate_validator_hibernate_validator_6_0_9_Final.xml delete mode 100644 .idea/libraries/Maven__org_jboss_logging_jboss_logging_3_3_2_Final.xml delete mode 100644 .idea/libraries/Maven__org_slf4j_jul_to_slf4j_1_7_25.xml delete mode 100644 .idea/libraries/Maven__org_slf4j_slf4j_api_1_7_25.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_starter_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_starter_jdbc_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_2_0_2_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_aop_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_beans_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_context_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_core_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_expression_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_jcl_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_jdbc_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_tx_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_web_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_springframework_spring_webmvc_5_0_6_RELEASE.xml delete mode 100644 .idea/libraries/Maven__org_yaml_snakeyaml_1_19.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/uiDesigner.xml delete mode 100644 .idea/workspace.xml delete mode 100644 application.properties delete mode 100644 build.gradle delete mode 100644 gradle/wrapper/gradle-wrapper.jar delete mode 100644 gradle/wrapper/gradle-wrapper.properties delete mode 100644 gradlew delete mode 100644 gradlew.bat delete mode 100644 gs-spring-boot.iml create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/hello/ApplicationTests.java create mode 100644 src/test/java/hello/controller/TopicControllerTest.java create mode 100644 src/test/java/hello/service/TopicServiceTest.java delete mode 100644 target/classes/hello/Application.class delete mode 100644 target/classes/hello/controller/GreetingController.class delete mode 100644 target/classes/hello/controller/HelloController.class delete mode 100644 target/classes/hello/controller/TopicController.class delete mode 100644 target/classes/hello/declaration/CustomPredicate.class delete mode 100644 target/classes/hello/declaration/TimeClient.class delete mode 100644 target/classes/hello/model/Customer.class delete mode 100644 target/classes/hello/model/Greeting.class delete mode 100644 target/classes/hello/model/Quote.class delete mode 100644 target/classes/hello/model/SimpleTimeClient.class delete mode 100644 target/classes/hello/model/Topic.class delete mode 100644 target/classes/hello/model/Value.class delete mode 100644 target/classes/hello/service/TopicService.class delete mode 100644 target/classes/public/index.html delete mode 100644 temp.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..ad174f5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: CI + +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up JDK 25 + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: '25' + cache: maven + + - name: Build and test + run: ./mvnw -B -ntp verify diff --git a/.gitignore b/.gitignore index 57f1cb2..4a9ac2f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,18 @@ -/.idea/ \ No newline at end of file +target/ +build/ +out/ + +.idea/ +*.iml +*.ipr +*.iws +.vscode/ +.project +.classpath +.settings/ + +.DS_Store +Thumbs.db + +*.log +*.class diff --git a/.gitignore.txt b/.gitignore.txt deleted file mode 100644 index 57f1cb2..0000000 --- a/.gitignore.txt +++ /dev/null @@ -1 +0,0 @@ -/.idea/ \ No newline at end of file diff --git a/.idea/.name b/.idea/.name deleted file mode 100644 index a156ef4..0000000 --- a/.idea/.name +++ /dev/null @@ -1 +0,0 @@ -gs-spring-boot \ No newline at end of file diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index daa2f90..0000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index ba88c4b..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_2_3.xml b/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_2_3.xml deleted file mode 100644 index 6fec8f4..0000000 --- a/.idea/libraries/Maven__ch_qos_logback_logback_classic_1_2_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__ch_qos_logback_logback_core_1_2_3.xml b/.idea/libraries/Maven__ch_qos_logback_logback_core_1_2_3.xml deleted file mode 100644 index 9eb8596..0000000 --- a/.idea/libraries/Maven__ch_qos_logback_logback_core_1_2_3.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_classmate_1_3_4.xml b/.idea/libraries/Maven__com_fasterxml_classmate_1_3_4.xml deleted file mode 100644 index 33c30b4..0000000 --- a/.idea/libraries/Maven__com_fasterxml_classmate_1_3_4.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_9_0.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_9_0.xml deleted file mode 100644 index 06441f4..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_annotations_2_9_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_9_5.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_9_5.xml deleted file mode 100644 index 1205e0d..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_core_2_9_5.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_9_5.xml b/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_9_5.xml deleted file mode 100644 index 467b779..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_core_jackson_databind_2_9_5.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_9_5.xml b/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_9_5.xml deleted file mode 100644 index 65b251d..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jdk8_2_9_5.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_9_5.xml b/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_9_5.xml deleted file mode 100644 index c85f307..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_datatype_jackson_datatype_jsr310_2_9_5.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_9_5.xml b/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_9_5.xml deleted file mode 100644 index 4ace22a..0000000 --- a/.idea/libraries/Maven__com_fasterxml_jackson_module_jackson_module_parameter_names_2_9_5.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_h2database_h2_1_4_197.xml b/.idea/libraries/Maven__com_h2database_h2_1_4_197.xml deleted file mode 100644 index 87c599a..0000000 --- a/.idea/libraries/Maven__com_h2database_h2_1_4_197.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__com_zaxxer_HikariCP_2_7_9.xml b/.idea/libraries/Maven__com_zaxxer_HikariCP_2_7_9.xml deleted file mode 100644 index 6d6865c..0000000 --- a/.idea/libraries/Maven__com_zaxxer_HikariCP_2_7_9.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_3_2.xml b/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_3_2.xml deleted file mode 100644 index e74f3ab..0000000 --- a/.idea/libraries/Maven__javax_annotation_javax_annotation_api_1_3_2.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__javax_validation_validation_api_2_0_1_Final.xml b/.idea/libraries/Maven__javax_validation_validation_api_2_0_1_Final.xml deleted file mode 100644 index 6978c0b..0000000 --- a/.idea/libraries/Maven__javax_validation_validation_api_2_0_1_Final.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_10_0.xml b/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_10_0.xml deleted file mode 100644 index cbe5ca7..0000000 --- a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_api_2_10_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_10_0.xml b/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_10_0.xml deleted file mode 100644 index 1407c58..0000000 --- a/.idea/libraries/Maven__org_apache_logging_log4j_log4j_to_slf4j_2_10_0.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_8_5_31.xml b/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_8_5_31.xml deleted file mode 100644 index a6693a2..0000000 --- a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_core_8_5_31.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_8_5_31.xml b/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_8_5_31.xml deleted file mode 100644 index bc00163..0000000 --- a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_el_8_5_31.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_8_5_31.xml b/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_8_5_31.xml deleted file mode 100644 index a47a3be..0000000 --- a/.idea/libraries/Maven__org_apache_tomcat_embed_tomcat_embed_websocket_8_5_31.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_6_0_9_Final.xml b/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_6_0_9_Final.xml deleted file mode 100644 index 5a26bec..0000000 --- a/.idea/libraries/Maven__org_hibernate_validator_hibernate_validator_6_0_9_Final.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_3_2_Final.xml b/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_3_2_Final.xml deleted file mode 100644 index 5f7dd01..0000000 --- a/.idea/libraries/Maven__org_jboss_logging_jboss_logging_3_3_2_Final.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_1_7_25.xml b/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_1_7_25.xml deleted file mode 100644 index 6073e53..0000000 --- a/.idea/libraries/Maven__org_slf4j_jul_to_slf4j_1_7_25.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_25.xml b/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_25.xml deleted file mode 100644 index 20e8163..0000000 --- a/.idea/libraries/Maven__org_slf4j_slf4j_api_1_7_25.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_2_0_2_RELEASE.xml deleted file mode 100644 index 216fa5d..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_2_0_2_RELEASE.xml deleted file mode 100644 index 416393d..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_autoconfigure_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_2_0_2_RELEASE.xml deleted file mode 100644 index f4f17f8..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_jdbc_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_jdbc_2_0_2_RELEASE.xml deleted file mode 100644 index 1fe0fd1..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_jdbc_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_2_0_2_RELEASE.xml deleted file mode 100644 index 7f64d70..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_json_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_2_0_2_RELEASE.xml deleted file mode 100644 index 73c5114..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_logging_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_2_0_2_RELEASE.xml deleted file mode 100644 index 15cdbc9..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_tomcat_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_2_0_2_RELEASE.xml b/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_2_0_2_RELEASE.xml deleted file mode 100644 index ab4ca86..0000000 --- a/.idea/libraries/Maven__org_springframework_boot_spring_boot_starter_web_2_0_2_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_aop_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_aop_5_0_6_RELEASE.xml deleted file mode 100644 index 45ed002..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_aop_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_beans_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_beans_5_0_6_RELEASE.xml deleted file mode 100644 index c046b8d..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_beans_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_context_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_context_5_0_6_RELEASE.xml deleted file mode 100644 index 9b830cd..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_context_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_core_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_core_5_0_6_RELEASE.xml deleted file mode 100644 index 597e34e..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_core_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_expression_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_expression_5_0_6_RELEASE.xml deleted file mode 100644 index ffca824..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_expression_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_jcl_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_jcl_5_0_6_RELEASE.xml deleted file mode 100644 index c0a9cc7..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_jcl_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_jdbc_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_jdbc_5_0_6_RELEASE.xml deleted file mode 100644 index 671875f..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_jdbc_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_tx_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_tx_5_0_6_RELEASE.xml deleted file mode 100644 index 6e29e18..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_tx_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_web_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_web_5_0_6_RELEASE.xml deleted file mode 100644 index 521bb85..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_web_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_springframework_spring_webmvc_5_0_6_RELEASE.xml b/.idea/libraries/Maven__org_springframework_spring_webmvc_5_0_6_RELEASE.xml deleted file mode 100644 index e4279b7..0000000 --- a/.idea/libraries/Maven__org_springframework_spring_webmvc_5_0_6_RELEASE.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/libraries/Maven__org_yaml_snakeyaml_1_19.xml b/.idea/libraries/Maven__org_yaml_snakeyaml_1_19.xml deleted file mode 100644 index 33ccf19..0000000 --- a/.idea/libraries/Maven__org_yaml_snakeyaml_1_19.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 01b5b5c..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 751276b..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml deleted file mode 100644 index e96534f..0000000 --- a/.idea/uiDesigner.xml +++ /dev/null @@ -1,124 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml deleted file mode 100644 index e90adcd..0000000 --- a/.idea/workspace.xml +++ /dev/null @@ -1,943 +0,0 @@ - - - - - - - - - - - - hello.controller.* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - HelloController - aslist - compata - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1526804939552 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - file://$PROJECT_DIR$/src/main/java/hello/controller/GreetingController.java - 15 - - - - file://$PROJECT_DIR$/src/main/java/hello/controller/HelloController.java - 55 - - - - - - - - - ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString)) - JAVA - CODE_FRAGMENT - - - ZonedDateTime.of(getLocalDateTime(), getZoneId("UTC-04:00")) - JAVA - CODE_FRAGMENT - - - - - getZoneId(zoneString) - JAVA - EXPRESSION - - - topics.stream().filter(topic -> topic.getId().equals(id)).findFirst().get() - JAVA - EXPRESSION - - - topics.stream().filter(topic -> topic.getId().equals(id)) - JAVA - EXPRESSION - - - topics.stream().filter(topicId -> topicId.equals(id)) - JAVA - EXPRESSION - - - topics.stream().filter(topicId -> topicId.equals(id)).findFirst().get() - JAVA - EXPRESSION - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - No facets are configured - - - - - - - - - - - - - - - 1.8 - - - - - - - - gs-spring-boot - - - - - - - - Maven: ch.qos.logback:logback-classic:1.2.3 - - - - - - - - \ No newline at end of file diff --git a/README.md b/README.md index 2b09400..da59331 100644 --- a/README.md +++ b/README.md @@ -1,28 +1,51 @@ -# springboot-java8 -The project is made on spring boot. The project summarize the new features present in Java 8. -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 +# springboot-java-modern (formerly springboot-java8) +Spring Boot REST service showcasing modern Java APIs. Originally a Java 8 demo, +upgraded to **Java 25 (LTS)** and **Spring Boot 3.4** with a batch of quality +improvements. + +The service exposes a CRUD API over an in-memory list of topics plus endpoints +that demonstrate stream, file, string, and date-time APIs. Call the endpoints +with `curl`, Postman, or the bundled Swagger UI. + +Language/library features demonstrated: + +1) NIO (`java.nio.file`) streams 2) String operations -3) Stream operations -4) IntStream functions -5) Functional interface -6) Lambda functions -7) Optional datatype -8) Foreach loops -9) Default and Static methods in interface -10) Java 8 LocalDateTime API -11) Pattern +3) `Stream` operations +4) `IntStream` +5) Functional interfaces +6) Lambdas +7) `Optional` +8) `forEach` +9) Default & static methods on interfaces +10) `java.time` (LocalDateTime, ZonedDateTime) +11) `Pattern` +12) Records (Java 16+) +13) Text blocks (Java 15+) +14) `String.formatted` (Java 15+) +15) `Stream#toList` (Java 16+) +16) `Pattern#asMatchPredicate` (Java 11+) ## Getting Started -1) Download or clone the project with link -(https://github.com/RehmanMuradAli/springboot-java8/) -## Available API's +```bash +# build + test +./mvnw verify + +# run +./mvnw spring-boot:run +``` + +Then visit: +- `http://localhost:8080/` — Greeting endpoint +- `http://localhost:8080/swagger-ui.html` — OpenAPI UI +- `http://localhost:8080/actuator/health` — Health check + +## Available APIs -Greetings +Greeting ``` GET / ``` @@ -129,6 +152,6 @@ https://dzone.com/articles/java-8-friday-goodies-new-new ## Authors -* **Rehman Murad Ali** +* **Rehman Murad Ali** diff --git a/application.properties b/application.properties deleted file mode 100644 index d3baf26..0000000 --- a/application.properties +++ /dev/null @@ -1 +0,0 @@ -#server.port = 8081 \ No newline at end of file diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 09f1083..0000000 --- a/build.gradle +++ /dev/null @@ -1,32 +0,0 @@ -buildscript { - repositories { - mavenCentral() - } - dependencies { - classpath("org.springframework.boot:spring-boot-gradle-plugin:2.0.2.RELEASE") - } -} - -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' -} - -repositories { - mavenCentral() -} - -sourceCompatibility = 1.8 -targetCompatibility = 1.8 - -dependencies { - compile("org.springframework.boot:spring-boot-starter-web") - testCompile("junit:junit") -} - diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index ca78035ef0501d802d4fc55381ef2d5c3ce0ec6e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53556 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�H$uJM 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi -done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null - -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" - -warn ( ) { - echo "$*" -} - -die ( ) { - echo - echo "$*" - echo - exit 1 -} - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD="java" - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." -fi - -# Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi -fi - -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi - -# For Cygwin, switch paths to Windows format before running java -if $cygwin ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi - # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" - fi - i=$((i+1)) - done - case $i in - (0) set -- ;; - (1) set -- "$args0" ;; - (2) set -- "$args0" "$args1" ;; - (3) set -- "$args0" "$args1" "$args2" ;; - (4) set -- "$args0" "$args1" "$args2" "$args3" ;; - (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac -fi - -# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules -function splitJvmOpts() { - JVM_OPTS=("$@") -} -eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS -JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" - -exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat deleted file mode 100644 index 832fdb6..0000000 --- a/gradlew.bat +++ /dev/null @@ -1,90 +0,0 @@ -@if "%DEBUG%" == "" @echo off -@rem ########################################################################## -@rem -@rem Gradle startup script for Windows -@rem -@rem ########################################################################## - -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal - -set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. -set APP_BASE_NAME=%~n0 -set APP_HOME=%DIRNAME% - -@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= - -@rem Find java.exe -if defined JAVA_HOME goto findJavaFromJavaHome - -set JAVA_EXE=java.exe -%JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto init - -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:findJavaFromJavaHome -set JAVA_HOME=%JAVA_HOME:"=% -set JAVA_EXE=%JAVA_HOME%/bin/java.exe - -if exist "%JAVA_EXE%" goto init - -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. - -goto fail - -:init -@rem Get command-line arguments, handling Windows variants - -if not "%OS%" == "Windows_NT" goto win9xME_args -if "%@eval[2+2]" == "4" goto 4NT_args - -:win9xME_args -@rem Slurp the command line arguments. -set CMD_LINE_ARGS= -set _SKIP=2 - -:win9xME_args_slurp -if "x%~1" == "x" goto execute - -set CMD_LINE_ARGS=%* -goto execute - -:4NT_args -@rem Get arguments from the 4NT Shell from JP Software -set CMD_LINE_ARGS=%$ - -:execute -@rem Setup the command line - -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar - -@rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal - -:omega diff --git a/gs-spring-boot.iml b/gs-spring-boot.iml deleted file mode 100644 index ad195a8..0000000 --- a/gs-spring-boot.iml +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 63f5cbd..178a3d3 100644 --- a/pom.xml +++ b/pom.xml @@ -1,43 +1,61 @@ - + 4.0.0 - org.springframework - gs-spring-boot - pom - 0.1.0 + hello + springboot-java-modern + 1.0.0 + jar + + springboot-java-modern + Spring Boot REST service showcasing modern Java APIs (upgraded from Java 8) org.springframework.boot spring-boot-starter-parent - 2.0.2.RELEASE + 3.5.0 + + + 25 + 25 + UTF-8 + 2.7.0 + + org.springframework.boot - spring-boot-properties-migrator - runtime + spring-boot-starter-web org.springframework.boot - spring-boot-starter-web + spring-boot-starter-jdbc org.springframework.boot - spring-boot-starter-jdbc + spring-boot-starter-actuator com.h2database h2 + runtime + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${springdoc-openapi.version} - - - - 1.8 - + + org.springframework.boot + spring-boot-starter-test + test + + diff --git a/src/main/java/hello/Application.java b/src/main/java/hello/Application.java index 7cf8faf..44090f5 100644 --- a/src/main/java/hello/Application.java +++ b/src/main/java/hello/Application.java @@ -1,88 +1,51 @@ package hello; -import java.util.Arrays; -import java.util.List; -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; 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.ApplicationContext; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.context.annotation.Bean; import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.web.client.RestTemplate; + +import java.util.List; @SpringBootApplication -public class Application implements CommandLineRunner { +public class Application { 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 { + @ConditionalOnBean(JdbcTemplate.class) + public CommandLineRunner seedCustomers(JdbcTemplate jdbcTemplate) { return args -> { - Quote quote = restTemplate.getForObject( - "http://gturnquist-quoters.cfapps.io/api/random", Quote.class); - log.info(quote.toString()); - }; - } - + log.info("Creating tables"); - @Autowired - JdbcTemplate jdbcTemplate; + jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); + jdbcTemplate.execute("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); - @Override - public void run(String... args) throws Exception { - log.info("Creating tables"); + List splitUpNames = List.of("John Woo", "Jeff Dean", "Josh Bloch", "Josh Long").stream() + .map(name -> name.split(" ")) + .map(parts -> new Object[] { parts[0], parts[1] }) + .toList(); - jdbcTemplate.execute("DROP TABLE customers IF EXISTS"); - jdbcTemplate.execute("CREATE TABLE customers(id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))"); + splitUpNames.forEach(name -> + log.info("Inserting customer record for {} {}", name[0], name[1])); - // 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':"); - 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())); + 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 = ?", + (rs, rowNum) -> new Customer(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name")), + "Josh") + .forEach(customer -> log.info(customer.toString())); + }; } } diff --git a/src/main/java/hello/controller/GreetingController.java b/src/main/java/hello/controller/GreetingController.java index cf52f8d..d59c0af 100644 --- a/src/main/java/hello/controller/GreetingController.java +++ b/src/main/java/hello/controller/GreetingController.java @@ -1,7 +1,7 @@ package hello.controller; import hello.model.Greeting; -import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @@ -9,12 +9,11 @@ @RestController public class GreetingController { - private static final String template = "Hello, %s!"; + private static final String TEMPLATE = "Hello, %s!"; private final AtomicLong counter = new AtomicLong(); - @RequestMapping("/") - public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) { - return new Greeting(counter.incrementAndGet(), - String.format(template, name)); + @GetMapping("/") + public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) { + return new Greeting(counter.incrementAndGet(), TEMPLATE.formatted(name)); } } diff --git a/src/main/java/hello/controller/HelloController.java b/src/main/java/hello/controller/HelloController.java index f3602fa..5ab17f8 100644 --- a/src/main/java/hello/controller/HelloController.java +++ b/src/main/java/hello/controller/HelloController.java @@ -2,100 +2,71 @@ 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.GetMapping; 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 { + private static final String JOIN_TEMPLATE = "Joining All String ID's with JOIN method: "; + private static final String DISTINCT_SORTED_TEMPLATE = "-------------Get all ID characters, select distict and sort with ID= "; + private static final String SPLIT_JAVA_KEYWORD_TEMPLATE = "-------------Split All Id With Colon,Select ID With \"Java\" Keyword, Then Sort Then Join "; + private static final String FIND_ID_HAVING_CHARACTER_TEMPLATE = "-------------Return All ID having character 'g' in it: "; + private static final String FIND_ALL_FILES_TEMPLATE = "---------Find all files in path and sort: "; + private static final String FIND_PARTICULAR_FILE_TEMPLATE = "----------Find File in present directory which starts with \"pom\", provided maximum depth=25 and sort : "; + private static final String FIND_PARTICULAR_FILE_WALK_TEMPLATE = "----------Find File in present directory which starts with \"pom\", provided maximum depth=25 and sort : with walk function "; + private static final String READ_FILE_TEMPLATE = "---------Read \"README.md\" file with stream functions, having \"spring\" within it: "; - 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: "; + private final TopicService topicService; + public HelloController(TopicService topicService) { + this.topicService = topicService; + } - @Autowired - private TopicService topicService; - - /** - * Java 8 Date Time example - * - * @return - */ - @RequestMapping("/datetime") - public String index() { + @GetMapping("/datetime") + public String datetime() { 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(); - + LocalDateTime now = myTimeClient.getLocalDateTime(); + 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 Canada/Central: %s""".formatted( + myTimeClient, + now.plusDays(1), + now.minus(1, ChronoUnit.MONTHS), + LocalDate.now().isLeapYear(), + ZoneId.systemDefault(), + myTimeClient.getZonedDateTime("Canada/Central")); } - - /** - * String Operations in Java 8 - * - * @return - */ - @RequestMapping("/topic/string/operation") + @GetMapping("/topic/string/operation") public String showStringOperation() { - String join = topicService.returnAllTopicIDWithStringSlicing(); - String makeDistinctAndSortCharacters = topicService.makeDistinctAndSortCharacters(join); - String splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin = topicService - .splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(join); - String findIdHavingCharacter = topicService.findIdHavingCharacter(); - - return joinTemplate + join - + makeDistinctAndSortCharactersTemplate + makeDistinctAndSortCharacters - + splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoinTemplate + splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin - + findIdHavingCharacterTemplate + findIdHavingCharacter; - + String distinct = topicService.makeDistinctAndSortCharacters(join); + String split = topicService.splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(join); + String findG = topicService.findIdHavingCharacter(); + return JOIN_TEMPLATE + join + + DISTINCT_SORTED_TEMPLATE + distinct + + SPLIT_JAVA_KEYWORD_TEMPLATE + split + + FIND_ID_HAVING_CHARACTER_TEMPLATE + findG; } - - /** - * File Operation in Java 8 - * @return - */ - @RequestMapping("/topic/file/operation") + @GetMapping("/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; + return FIND_ALL_FILES_TEMPLATE + topicService.findAllFilesInPathAndSort() + + FIND_PARTICULAR_FILE_TEMPLATE + topicService.findParticularFileInPathAndSort() + + FIND_PARTICULAR_FILE_WALK_TEMPLATE + topicService.findParticularFileInPathAndSortWithWalkFunction() + + READ_FILE_TEMPLATE + topicService.readFileWithStreamFunction(); } - - } diff --git a/src/main/java/hello/controller/TopicController.java b/src/main/java/hello/controller/TopicController.java index c2e8273..0f7666a 100644 --- a/src/main/java/hello/controller/TopicController.java +++ b/src/main/java/hello/controller/TopicController.java @@ -2,89 +2,66 @@ import hello.model.Topic; import hello.service.TopicService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.*; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.server.ResponseStatusException; import java.util.List; @RestController +@RequestMapping("/topic") public class TopicController { - @Autowired - private TopicService topicService; + private final TopicService topicService; + public TopicController(TopicService topicService) { + this.topicService = topicService; + } - /** - * Get all Topic - * @return - */ - @RequestMapping("/topic") + @GetMapping public List getAllTopics() { return topicService.getAllTopics(); } - /** - * get Topic with ID - * @param id - * @return - */ - @RequestMapping("/topic/{id}") + @GetMapping("/{id}") public Topic getTopicWithID(@PathVariable String id) { - return topicService.getTopicWithId(id); + return topicService.getTopicWithId(id) + .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND, "Topic not found: " + id)); } - /** - * Add a new topic in list - * @param topic - */ - @RequestMapping(method = RequestMethod.POST, value = "/topic") + @PostMapping + @ResponseStatus(HttpStatus.CREATED) public void addTopic(@RequestBody Topic topic) { topicService.addTopic(topic); } - /** - * Update Topic in List with id - * @param id - * @param topic - */ - @RequestMapping(method = RequestMethod.PUT, value = "/topic/{id}") + @PutMapping("/{id}") public void updateTopic(@PathVariable String id, @RequestBody Topic topic) { topicService.updateTopic(id, topic); } - - /** - * Delete a topic with ID - * @param id - */ - @RequestMapping(method = RequestMethod.DELETE, value = "/topic/{id}") + @DeleteMapping("/{id}") + @ResponseStatus(HttpStatus.NO_CONTENT) public void deleteTopic(@PathVariable String id) { topicService.deleteTopic(id); } - /** - * Get all topics with Id length greater then minimum length - * @param minLength - * @return - */ - @RequestMapping(value = "/topic/minimum/length/{minLength}") + @GetMapping("/minimum/length/{minLength}") public List filterMinimumLengthForId(@PathVariable Integer minLength) { return topicService.filterMinimumLengthForId(minLength); } - - /** - * Sort with Id - * @return - */ - @RequestMapping("/topic/sort") + @GetMapping("/sort") public List sortTopicsWithID() { return topicService.sortTopicsWithID(); } - - - - - } diff --git a/src/main/java/hello/declaration/TimeClient.java b/src/main/java/hello/declaration/TimeClient.java index 80aa106..92e3c7b 100644 --- a/src/main/java/hello/declaration/TimeClient.java +++ b/src/main/java/hello/declaration/TimeClient.java @@ -4,35 +4,30 @@ import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; - /** - * Implementing Defaults and Static Functions in Interface + * Demonstrates default and static methods on interfaces. */ - public interface TimeClient { void setTime(int hour, int minute, int second); + void setDate(int day, int month, int year); + void setDateAndTime(int day, int month, int year, int hour, int minute, int second); + LocalDateTime getLocalDateTime(); - static ZoneId getZoneId (String zoneString) { + static ZoneId getZoneId(String zoneString) { try { return ZoneId.of(zoneString); } catch (DateTimeException e) { - System.err.println("Invalid time zone: " + zoneString + - "; using default time zone instead."); + System.err.println("Invalid time zone: " + zoneString + "; using default time zone instead."); return ZoneId.systemDefault(); } } - default ZonedDateTime getZonedDateTime(String zoneString) { return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString)); } - } diff --git a/src/main/java/hello/model/Customer.java b/src/main/java/hello/model/Customer.java index a6df80e..640db05 100644 --- a/src/main/java/hello/model/Customer.java +++ b/src/main/java/hello/model/Customer.java @@ -1,45 +1,4 @@ package hello.model; -public class Customer { - private long id; - private String firstName, lastName; - - @Override - public String toString() { - return "Customer{" + - "id=" + id + - ", firstName='" + firstName + '\'' + - ", lastName='" + lastName + '\'' + - '}'; - } - - public Customer(long id, String firstName, String lastName) { - this.id = id; - this.firstName = firstName; - this.lastName = lastName; - } - - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getFirstName() { - return firstName; - } - - public void setFirstName(String firstName) { - this.firstName = firstName; - } - - public String getLastName() { - return lastName; - } - - public void setLastName(String lastName) { - this.lastName = lastName; - } +public record Customer(long id, String firstName, String lastName) { } diff --git a/src/main/java/hello/model/Greeting.java b/src/main/java/hello/model/Greeting.java index f2e7772..135b2fb 100644 --- a/src/main/java/hello/model/Greeting.java +++ b/src/main/java/hello/model/Greeting.java @@ -1,21 +1,4 @@ package hello.model; -public class Greeting { - private final long id; - private final String content; - - public Greeting(long id, String content) { - this.id = id; - this.content = content; - } - - - public long getId() { - - return id; - } - - public String getContent() { - return content; - } +public record Greeting(long id, String content) { } diff --git a/src/main/java/hello/model/Quote.java b/src/main/java/hello/model/Quote.java index 2178a6d..b548a67 100644 --- a/src/main/java/hello/model/Quote.java +++ b/src/main/java/hello/model/Quote.java @@ -1,34 +1,4 @@ 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() { - - } +public record Quote(String type, Value value) { } diff --git a/src/main/java/hello/model/Topic.java b/src/main/java/hello/model/Topic.java index 483a4a7..59740dc 100644 --- a/src/main/java/hello/model/Topic.java +++ b/src/main/java/hello/model/Topic.java @@ -1,43 +1,4 @@ package hello.model; - -public class Topic { - private String id; - private String subjectName; - private String subjectDescription; - - public Topic() { - } - - public String getId() { - return id; - } - - public void setId(String id) { - this.id = id; - } - - public String getSubjectName() { - return subjectName; - } - - public void setSubjectName(String subjectName) { - this.subjectName = subjectName; - } - - public String getSubjectDescription() { - return subjectDescription; - } - - public void setSubjectDescription(String subjectDescription) { - this.subjectDescription = subjectDescription; - } - - public Topic(String id, String subjectName, String subjectDescription) { - super(); - this.id = id; - this.subjectName = subjectName; - this.subjectDescription = subjectDescription; - } - +public record Topic(String id, String subjectName, String subjectDescription) { } diff --git a/src/main/java/hello/model/Value.java b/src/main/java/hello/model/Value.java index 272be70..b795170 100644 --- a/src/main/java/hello/model/Value.java +++ b/src/main/java/hello/model/Value.java @@ -1,33 +1,4 @@ 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; - } +public record Value(Long id, String quote) { } diff --git a/src/main/java/hello/service/TopicService.java b/src/main/java/hello/service/TopicService.java index feffb92..a223d6a 100644 --- a/src/main/java/hello/service/TopicService.java +++ b/src/main/java/hello/service/TopicService.java @@ -9,229 +9,146 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.*; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.OptionalInt; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Pattern; -import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; @Service public class TopicService { - - private List topics = new ArrayList<>(Arrays.asList( + private final List topics = new CopyOnWriteArrayList<>(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") )); public List getAllTopics() { - return topics; + return List.copyOf(topics); } - /** - * Strean Example - * - * @param id - * @return - */ - public Topic getTopicWithId(String id) { - return topics.stream().filter(topic -> topic.getId().equals(id)).findFirst().get(); + public Optional getTopicWithId(String id) { + return topics.stream().filter(topic -> topic.id().equals(id)).findFirst(); } public void addTopic(Topic topic) { topics.add(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); + OptionalInt indexOfElement = IntStream.range(0, topics.size()) + .filter(index -> id.equals(topics.get(index).id())) + .findFirst(); + if (indexOfElement.isPresent()) { + topics.set(indexOfElement.getAsInt(), topic); + } } - /** - * Lamda Expressions - * - * @param id - */ public void deleteTopic(String id) { - topics.removeIf(topic -> topic.getId().equals(id)); + topics.removeIf(topic -> topic.id().equals(id)); } - /** - * Calling fucntional Interface - * - * @param minLength - * @return - */ public List filterMinimumLengthForId(Integer minLength) { - return printTopicsWithPredicate(topics, topic -> topic.getId().length() > minLength); + return filterTopicsWithPredicate(topics, topic -> topic.id().length() > minLength); } - - /** - * Functional Interface example With ForEach - * - * @param topicList - * @param tester - * @return - */ - private static List printTopicsWithPredicate(List topicList, CustomPredicate tester) { + private static List filterTopicsWithPredicate(List topicList, CustomPredicate tester) { List resultTopic = new ArrayList<>(); topicList.forEach(topic -> { - if (tester.test(topic)) resultTopic.add(topic); + if (tester.test(topic)) { + resultTopic.add(topic); + } }); return resultTopic; } - - /** - * Using Comparator to sort - * - * @return - */ public List sortTopicsWithID() { - topics.sort(Comparator.comparing(Topic::getId)); - return topics; + return topics.stream() + .sorted(Comparator.comparing(Topic::id)) + .toList(); } - - /** - * Join List of Strings - * - * @return - */ public String returnAllTopicIDWithStringSlicing() { - List topicIds = topics.stream().map(topic -> topic.getId()).collect(Collectors.toList()); - return String.join(":", topicIds); + return topics.stream() + .map(Topic::id) + .collect(java.util.stream.Collectors.joining(":")); } - - /** - * Use of MapToObject and distinct - * - * @param join - * @return - */ public String makeDistinctAndSortCharacters(String join) { return join.chars().distinct() .mapToObj(id -> String.valueOf((char) id)) .sorted() - .collect(Collectors.joining()); + .collect(java.util.stream.Collectors.joining()); } - - /** - * Use of Pattern Class with stream - * - * @param join - * @return - */ public String splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(String join) { return Pattern.compile(":") .splitAsStream(join) .filter(s -> s.contains("java")) .sorted() - .collect(Collectors.joining(":")); + .collect(java.util.stream.Collectors.joining(":")); } - - /** - * 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); - - return Stream.of(topicIdList) - .filter(pattern.asPredicate()) - .collect(Collectors.toList()) + return topics.stream() + .map(Topic::id) + .filter(pattern.asMatchPredicate()) + .toList() .toString(); } - - /** - * 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 + return stream .map(String::valueOf) .filter(path -> !path.startsWith(".")) .sorted() - .collect(Collectors.joining("; ")); - return joined; + .collect(java.util.stream.Collectors.joining("; ")); } catch (IOException e) { return " Error in IO"; } } - /** - * 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) -> - String.valueOf(path).startsWith("grad"))) { - String joined = stream + String.valueOf(path).startsWith("pom"))) { + return stream .sorted() .map(String::valueOf) - .collect(Collectors.joining("; ")); - return joined; + .collect(java.util.stream.Collectors.joining("; ")); } catch (IOException e) { return " IO exception "; } } - - /** - * 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 + return stream .map(String::valueOf) - .filter(path -> path.startsWith("grad")) + .filter(path -> path.startsWith("pom")) .sorted() - .collect(Collectors.joining("; ")); - return joined; + .collect(java.util.stream.Collectors.joining("; ")); } catch (IOException e) { return " IO exception "; } } - - /** - * Use BufferedReader with Stream functions - * @return - */ public String readFileWithStreamFunction() { - Path path = Paths.get("temp.txt"); - System.out.println(); + Path path = Paths.get("README.md"); try (BufferedReader reader = Files.newBufferedReader(path)) { - String lines = reader - .lines() - .filter(line->line.contains("print")) - .map(line->line.substring("print".length())) - .collect(Collectors.joining(",")); - return lines; + return reader.lines() + .filter(line -> line.contains("spring")) + .collect(java.util.stream.Collectors.joining(",")); } catch (IOException e) { return " IO exception "; } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 0000000..29d1979 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,6 @@ +spring.application.name=springboot-java-modern + +management.endpoints.web.exposure.include=health,info,metrics + +springdoc.api-docs.path=/v3/api-docs +springdoc.swagger-ui.path=/swagger-ui.html diff --git a/src/test/java/hello/ApplicationTests.java b/src/test/java/hello/ApplicationTests.java new file mode 100644 index 0000000..094b69e --- /dev/null +++ b/src/test/java/hello/ApplicationTests.java @@ -0,0 +1,12 @@ +package hello; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ApplicationTests { + + @Test + void contextLoads() { + } +} diff --git a/src/test/java/hello/controller/TopicControllerTest.java b/src/test/java/hello/controller/TopicControllerTest.java new file mode 100644 index 0000000..8782dd2 --- /dev/null +++ b/src/test/java/hello/controller/TopicControllerTest.java @@ -0,0 +1,84 @@ +package hello.controller; + +import com.fasterxml.jackson.databind.ObjectMapper; +import hello.model.Topic; +import hello.service.TopicService; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; + +import java.util.List; +import java.util.Optional; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.verify; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(TopicController.class) +class TopicControllerTest { + + @Autowired + MockMvc mockMvc; + + @MockBean + TopicService topicService; + + @Autowired + ObjectMapper objectMapper; + + @Test + void getAllTopics_returnsList() throws Exception { + given(topicService.getAllTopics()).willReturn(List.of( + new Topic("spring", "Spring Framework", "desc"))); + + mockMvc.perform(get("/topic")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$[0].id").value("spring")) + .andExpect(jsonPath("$[0].subjectName").value("Spring Framework")); + } + + @Test + void getTopicById_whenMissing_returns404() throws Exception { + given(topicService.getTopicWithId("missing")).willReturn(Optional.empty()); + + mockMvc.perform(get("/topic/missing")) + .andExpect(status().isNotFound()); + } + + @Test + void getTopicById_whenPresent_returnsTopic() throws Exception { + given(topicService.getTopicWithId("java")) + .willReturn(Optional.of(new Topic("java", "Core Java", "desc"))); + + mockMvc.perform(get("/topic/java")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id").value("java")); + } + + @Test + void addTopic_returns201() throws Exception { + Topic topic = new Topic("go", "Go", "desc"); + mockMvc.perform(post("/topic") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(topic))) + .andExpect(status().isCreated()); + + verify(topicService).addTopic(any(Topic.class)); + } + + @Test + void deleteTopic_returns204() throws Exception { + mockMvc.perform(delete("/topic/java")) + .andExpect(status().isNoContent()); + + verify(topicService).deleteTopic("java"); + } +} diff --git a/src/test/java/hello/service/TopicServiceTest.java b/src/test/java/hello/service/TopicServiceTest.java new file mode 100644 index 0000000..bcfb5dd --- /dev/null +++ b/src/test/java/hello/service/TopicServiceTest.java @@ -0,0 +1,97 @@ +package hello.service; + +import hello.model.Topic; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; + +class TopicServiceTest { + + TopicService topicService; + + @BeforeEach + void setUp() { + topicService = new TopicService(); + } + + @Test + void getAllTopics_containsSeedData() { + List all = topicService.getAllTopics(); + assertThat(all).hasSize(3); + assertThat(all).extracting(Topic::id).containsExactlyInAnyOrder("spring", "java", "javascript"); + } + + @Test + void getTopicWithId_returnsEmptyOptionalWhenMissing() { + assertThat(topicService.getTopicWithId("does-not-exist")).isEmpty(); + } + + @Test + void addAndDeleteTopic_roundTrip() { + Topic topic = new Topic("kotlin", "Kotlin", "desc"); + topicService.addTopic(topic); + assertThat(topicService.getTopicWithId("kotlin")).contains(topic); + + topicService.deleteTopic("kotlin"); + assertThat(topicService.getTopicWithId("kotlin")).isEmpty(); + } + + @Test + void updateTopic_replacesExistingEntry() { + Topic updated = new Topic("java", "Modern Java", "updated"); + topicService.updateTopic("java", updated); + assertThat(topicService.getTopicWithId("java")).contains(updated); + } + + @Test + void filterMinimumLengthForId_filtersByIdLength() { + assertThat(topicService.filterMinimumLengthForId(6)) + .extracting(Topic::id).containsExactlyInAnyOrder("javascript"); + assertThat(topicService.filterMinimumLengthForId(5)) + .extracting(Topic::id).containsExactlyInAnyOrder("spring", "javascript"); + } + + @Test + void sortTopicsWithID_sortsLexicographically() { + List sorted = topicService.sortTopicsWithID(); + assertThat(sorted).extracting(Topic::id).containsExactly("java", "javascript", "spring"); + } + + @Test + void returnAllTopicIDWithStringSlicing_joinsWithColon() { + assertThat(topicService.returnAllTopicIDWithStringSlicing()) + .contains("spring").contains("java").contains("javascript"); + } + + @Test + void findIdHavingCharacter_returnsIdsContainingG() { + String result = topicService.findIdHavingCharacter(); + assertThat(result).contains("spring"); + } + + @Test + void splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin_filtersJavaIds() { + String joined = topicService.returnAllTopicIDWithStringSlicing(); + String result = topicService.splitAllIdWithColonSelectIDWithJavaKeywordThenSortThenJoin(joined); + assertThat(result).contains("java"); + assertThat(result).doesNotContain("spring"); + } + + @Test + void makeDistinctAndSortCharacters_returnsDistinctSortedChars() { + String result = topicService.makeDistinctAndSortCharacters("ccbbaa"); + assertThat(result).isEqualTo("abc"); + } + + @Test + void getAllTopicsList_isImmutableCopy() { + List all = topicService.getAllTopics(); + Optional firstBefore = topicService.getTopicWithId("spring"); + assertThat(firstBefore).isPresent(); + assertThat(all).isUnmodifiable(); + } +} diff --git a/target/classes/hello/Application.class b/target/classes/hello/Application.class deleted file mode 100644 index 245a020cdb693ce4216257a9f5aa3bd787671f48..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6460 zcmb7Jd3+RS8GgQ8v)K%gE+mwDxHloNlu{_91w%+A=>|$RKoM&@*_|Wejybqa!877`CZ1J~>+$Ruo)gCncy0rphv&!f0=!Vci{hxqbuqkH!AlgpR6gBU zk1D(@4iz^w;N^Hl95>^Z1ckmfhS$k=uaDskal8?4is8))-V(!GW4JYjx5e;wDfJx< zSc7*aAn=|Bych3_+w|D8Xh+6b^-NxZZJDwj282{YY4RYG)3Di3=i4*sBzk|PoyMGDr?zBsy9SU z?nV8jf@_W%hfT*E%o`nrg5~P2X%!rSOCv=M8hXJ=4e43evd2<-nIY9ta;?**ZR9An z?lQAry1Qt2vgNQqO{bNkZS&G*!Ps9K9W?9#o!UH)vU>ioZkzHsSgdhJOsb!s9x?KH zE7egf=FO~U6eZV;>SjTpIe9F7TtB6!@_J!7m2suJ?NU`t+P9y6OW`vp$gkVOQg{SA ztJO1qRPD)< zB@;R6{wPD2PaTwtd^;!O>e&;0deMuOf&&V^EHILcR5EB;ZfX`ydrD?LM*y`%a?Zj; z1C&9ZQD8?T=i|{S5FM1s5QjjuhuNgoYP-awAB%Q#He;h*^9<~?Mo0BRPU4XTQ1A*V z-gPEx6g})K_zLsr5O?mOq68Du+UTRH2PMpY^9Vg@i@*rH3AHPRZmI$MNmubh z{7A*+*rnpf_=$p_s`!~S_U9^ofrrTl_IGs* zbWJBsnmH}g)!);RZqtTL+i|av(A5ri^mp#-=ud9B zzOQw(wT>zhVm^DIriukPrHCd~G>e4-tx=^Fz0t6;qKGHAzEGP8rVCC2Q&m*nq6C_} zRoUA!2h*YT*=_OLL`Z|cV7TRWG9*rWRCkPQcatX5POsa^Vhjln%;zo_>H?WD$2Hh2 zs;!bs7yP}3jpRzsZ!QbR&_~JOJ6yTU$wRg-H_sAk3R@j)3bKPY4;!u=81@=NvXOI& z4fgJ)WJ`owla_f!)M4BD827&#$FgafN>SaG?RPDK-UyAp@!n=NGjfw#V2G%^?ZsSc zvPUdPwuC4GS7_JtIiGf!g&}s5?NQ+IkTy#v59U11mEPbckfO>cA+zwe$Hj$WQ5;ob z@hmuxK3gzf#u+19lA~;$?#PKrU~R?JUKyiuDb#EV6{6 zB!|J^9UEkrm-BF1Zr|n6oB?Imk;A|&Ev}c_7AgF22}H#(obMp40?B0W%q}x?MP{jv zDIL|B{T+`l4w3q5y^|B`XnGs#_P-kVZunm?WK#&4;&;-ZLxn$dC}M>wG_gj-X`E3} zlGD*iIqe+hP=Fyh2&|fdgjqOcoiI{q?|8*YQV+gY5Qd{uPl{MakJFZQqQt6Jd*ngT zGeDmA9t@im$n$5?eVlkXcdqdn1K5V~67r{laeybpF!|$u>8t$Tyx~*?k~{T$K4bF3 zfg;v3&@s;Zr?&aRpb%Eb96BOyQc5sA@fj>hztcNz{&` zZo>o=fxfnLh_&tCc;B{~>TR`6wM{j*V&TT7+ATZlNVuqugg4ZR+a7!P9@HN_i}?PH ziH3w)eJ|#WWA6P>ch=mAd5dbs(Kvw%svwQ4_Ke#^AAqG)y&UtP(ae?fcoo0Zt;VIi z?!r3Kt;Z20kwc5;#4ftD88M{M$2*#g{W!p@!qdx@I7o+@`P|Px8GseYV1U|=U@i{f zFx@;t+Qq1Pj4J9BT&3Ws_j{FKoF3sDL5?SpSH^S)pQ?DR-7t>C?Ys%cbdzUJvriDj zl%zO@CzGv%)H3#0^D5-QibhD0?SV*T!+FdHrVG6|BvdI2RXi`)JSm`t0`v;;*Hioz zzQr7y>!=90g%M9I@WS1CH<}X7<5=k3T*w>cUMxCVomiYXT9a6kIa-@onmJmRSe7}5 z3Ih|?j|_q{`q7XZet) z)UTX5G{^)fgiVpmQpf;-Cn2KUgUv@<6BkWj3*(cxcmiAH^%C!T7TcyLdLWc&4S9@G z2Bn|?i$-0`duecyy4ZzzCw1+kE@|*`8T9S7UZh*Aw=j*ZHCvdyOCvortHD>t= diff --git a/target/classes/hello/controller/GreetingController.class b/target/classes/hello/controller/GreetingController.class deleted file mode 100644 index 1149660667b8bd33f1ed25bd2d4d556a0184f111..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1219 zcmb7DYflqF6g|^-OLKy#_<+zlxxYx|+pLNS!% z@K8IhW&6?&l@d~0T>GA5TWis;=0_lz$BuOLDnqhdsWYV3{U$*T)*LCGhh9UdSG?gy z41Alrb*>y^cbqA`?*t5UwSD2bzA2@Z@47-+8%hXGG>KxzMTB#$j+dd@q1fF~r32#; z1@{zz@=}s}L}7f3+H*WncLIkvtV!u>t{oDA;bF~JEh}gnY4#L_ANlI7btD>A!;wvk zpT=5S;$0{L{fxKUv5e`%SUzSk%=DIc$qf`q+EZ)!ytN`2>Z8~cdpvZ1zgPOTuiPd> zVcQRtEuJ_==drWOEt$7JUyFh6626EFC^5`OBlORiC}I#p48!r0_P#|Cw{eGI_CKDR z+Inh+VsUCNm}VIIGruny2g24_+%3{h+&AQwY2X}bE8)?UuF2+x(9t|@_T6H*_d$YJ`L{RaAwBD4`Y72|k}VvH}nYdEC!4K(-JWfYr$d+wjzujAq7}jAk4~Of#YPfGtv&s&a7u< zB}5MAuyfz{?cDcaJC*>G*faD{qV)P=sBg)cP7Aj~0;N)8Ro zXRwgNHc@(A=%So(BZm*;BUyZO13rdVgze*5e4-nl#HZxr)hu4i;nVnx44)O=&t>uX z9KL`rX7QygzAR#2$>FP_Xd7-dHY-<087GJIQx@5u078NMgO_cQoG20zrG zU-fKv%Bs~JBeXQ+C$1XvM$s|cxneo=ZFlathDU0~HEYZcLffr`Bd%HY{BX2t_(mnP z{Gcg(GN?OtIN~@Z^O7A_M?J@L%a&tR!qS*5j8pZq*5ZQan^RTG6*WS~S6Q%U)^^R3 zdB&LM;pr*OLwaSOXPmMfD=4|=jj)=S)a2B+p6c^0!xYWJLRVQvZ7q`?dUd-}w)}a!LhtULs9KKW6@z%8IMuRCKkvNIa6`Lh zU9+C085 zvAp8Bq3=14?_ zu0hz6jXK8gehmdFE{wBc#ZYlTRcWk0Pz$`ZYJ9SEPSvci>Y2kz$CDV+aQa~D-)SpU z21QF_s*+Z`K@~(*O_@>?E97uZ6|kDcT1#Uml%l%#inBzHheceU`$n`8rn2m_0RPyirV3@ z*mqpyo8qT!$bx@*YOMdQp4d@66r@O8hSO)_bI<%^KQ&oDuZ9?_%&YF z@f-YB!?9Fk)g+boW>wv{=54PLs2V#+RV^3+mHbY}@9_s6f5e|!Gjo-aQlO+IDMLAy zQCEwW;j3p9h5szxdPB#XcuQdXg>`I|VEQp;%^D3SRKa4vII0_-%j7kg-5I>CV+qR| z`cvlSM4FH!`KaO8v!3tT#<0@xz^LIGrcoSa-us55<0fupa9hXwFp|MLI__XX$3^^A z$0ZqFkk2bJyePv5WOxY^8dRJM_inHnHaGKNa^@;;*h*@wZHa95jIV}WE3J4f3%lle zHbKiZ>Fa^D4SQ`5PtuNp+0cuN=_;dEmNHRCot)LMzg2H+^YOKMsW7<-?cV}CWX=j# zwS1aTcN)Q%vB<~C?t$?{XN;?~)g(Y|2g*aivX-# zu0nqO5;BE+b_v~ud~OLF3VD4A8w>ddmawUi-@Jq^Z&I)Wr#bGAjNL)yJ;-4fHe)xo zV=qUAJ^~uxcnJG&hP--wAW9hLmxgCCK`mm;ztDwsq_5pUe)=Z1PTazSXYXJe=iBo; zmhsS|%h-7fJ)Grt$>(kCW@as8PqO%MbMYN1}!O$#%bp zj$!S)3zK9gCR6d1{q-JiwLb_bAXZsBw-!0i!$X#%{=#Jqxs*tmMxx0>LlQ*h65U%+kV i%8Q(Jkb8u@0Sv6Q}7zpJZ!eFO?-%S|!6U z{ZH*oC!Og-e?Wg!r{AvRON2ay2kmO5bI$jD=d%C&``6z@v`kiosx{>a}|Mwj*?Pl_%- z5TR(gyzY6vw59O9kkKQ{587tf3xwA`4D2pH_Jg0zCbzxNJhWTV58j&gz`?9X(mxgf zcM!I?COje68Rct>2aF0Ez5_02E#dL+qi&N2`!>)RON>o#)gSYw*%Y2LDtwRs67f*JvU@#b3a%4Jwi^MgXzE%pngUBr@kb#$JDZ9| zOw+Z!w%L#>!>fxKL>T3Sqtc{BjaoCR#?j4p!ub{1I$g+JTNO<5;8aB)WL7jw)2=N$ z{jxw&*>)T)fYEF%gQqI;L?U)3<3Dl%HXR>Y9)2)~eqOaBNECKoH})B=d~AT{zVjCD z)u`v#k|#2185PJNu7u@xe*kP1?SX_Q?iVuLm2Giz%i7xCVl;OsT%`XM=AavOE$+2t zXWI{Ukb#?{zB``eZ62Wd@Aqw2c(Eg+nUN0YyD)nEA(6&4_(IY@Kv5J;S93Jq4N%cl z=R!Y-J2&jNZolo+A4Zji9|bMGEz~rh*U1^q=RGCq<0dJTL-$pK&e10Z8FZ#ZFAcg# za|V4!*9^Kzw+#A%?iutYePz(sv~18fG{z|E z_i+{XGe%P}!#;&m5&K!}W$Yl0bs8rVJHtM)^e3_3@sgt%tjd@Wx^Y~WLD({|9F%7*8Nh=z9KDHc4tt3+6 zO^QTL=QK^iOi0A?&*TR3V=Cy*gi1OIr8JXuB;o0pBn*?D#=n0Ge(j~_PCTdfyJDG;u(~xoN|9|T~M)Frt#vH8;X6*^le`Q|( AuK)l5 diff --git a/target/classes/hello/declaration/CustomPredicate.class b/target/classes/hello/declaration/CustomPredicate.class deleted file mode 100644 index e35803f64936c9dac92044b2074162814cb240fe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 248 zcmZXPu?~Vj5JYDYLD0fNKfn$fudz~NV`62Z2~>9Hals=eByjj~7Jh&qWjwLNVw0Vh z$?WXw{dfXcV&o(AF(kM~)`l>R4mrPbrg@RE<0Y5EtmkJ!5UZ@;(I#2_ z+qeIS5XNOw3AtC=226HMZOYrJlBp8h$i??0VRn^Tmn@Y+^NJf)7OZ0=jJxeT0>QDr W0OS&SaINlJ<-oJ^+k6aK5_|y1tUr+e diff --git a/target/classes/hello/declaration/TimeClient.class b/target/classes/hello/declaration/TimeClient.class deleted file mode 100644 index a077c3b314ee91fac7a75e48cd1d292b119702c1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1383 zcma)6YflqF6g|_H-L@%Al)A1LI*PW0fBdOc3!3FdX=bSyCF6(M)+l3>uxd_F(D7n;_%BvUsz)o$6fLQFzo z>agBJAq!rSI|>oHN6hKMF+bs1NsQTZ&KyI60^WMA$Xg6kW(ah+dzjskzU?07+G9f; z1;ti9i z66kH0!O*oMdGXyQF9)ntVg{A0(quIl8D{?@g(h5OB9H6}!+7CHIF6UKMA6|sSE912 zdaI5tT&eW!cvZhBHf)t;Ps=r@l0krnj*M1BHXX-8?5~{&qxI+6+h$2F(nW=%D7Cs~T9kDlG z(2>&4pl9ew#p!j1#2Jz`be>WiQK5sPq8OolicTQD4&u|uavftBCCeZoG)&NGJcu!I z6=||xqelZ<*U85)N%&C73gMNK?#x#h3^O%!)zE!PmPqg&ilO~vlbevZfhn@7o#JNn JyGf@A%-=-ZS;_za diff --git a/target/classes/hello/model/Customer.class b/target/classes/hello/model/Customer.class deleted file mode 100644 index 06e7307b01f2d26b0448a3cb8b12af11a1a0577c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1407 zcmaJ-=K;NhX@e zNHp;W_@j*HY4<`#F->}(mwV51&bjx`-=Dt$tYJNcN(Kt1j%8V^X-r~8qE#K1Bo8Hd zq~oz9Pb68(U(KW0?Y1jjrWSF=$vkjHiD&=#Y*Ft`(GOiQDHA}=M%M}^#o#=C=8O20guu=%g$(4FY=QCj;J1FG7u-b$1Ex^CQe=$b)Di7p^XJWSK;&R}hh_5hA7F4$A)kZ~E($ diff --git a/target/classes/hello/model/Greeting.class b/target/classes/hello/model/Greeting.class deleted file mode 100644 index f875277914303b411d510e45ee094e441fbdc76c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 588 zcmZuu%SyvQ6g`tiV{7^_t@Vw%YXh2%JH<^wu>@UI3NF)hXs4tJ#MHkM6fA-ZKfsR? z?0D}nny=iIsZ`u_L?aE5~tbZix&VOyFV1G@(H7!)yNU|j~okHUn934`TMyeH2I zyl~|7l30YJbB4l&2t{(q&}?;A`|Uo1)`^Ckq3Vi|-_E839^ZR|fGU<7`CicXVj=Uq zs3l|ZNNnzy2SMaa2^%=qG3QC9r;m7YL#wH+HZ518th3m!(Q2=5Kr_8)7W@24NX_zs z>QuhPL<##Q3NTEpp~%qqPnDte_wc*Hg!^<1((mJ#E~e8N6sn zK)FV~YQKZohjS?Q$sFn{l@yf8T4~!LE8#^{2wb815djHCUyZzsqzp?VIVoi}2%X6| s#3p;X{RZWkf{eV710R diff --git a/target/classes/hello/model/Quote.class b/target/classes/hello/model/Quote.class deleted file mode 100644 index 3ec6c6c7d3ee5544ff04f2ad4753558e032a5fd4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1128 zcmZ`%?@!ZE6g_XWYgdbm!3OFO5OCf2Q5C)+e2~OXi^f1^{Mc>HC@JkSS`uUYpES{t zXyPB>A7#Avt+LsbGLW4Yq(1eeAmerRIfm?byB(^z74U;03)y0rlGPW4c^up1Zyu+LvM1_3aiX z=~j=zQKi75RnO{IPN+d(uIUBtoA|Qr4$hpmFWJJ5& zZgeJS`KV^%8J?TCk2yZQAhA1Z(&Zx)Ma*+@-$V%u z0`rr?C5Aw8O!c;X;dUaD>Gb<<(53&HWyh35i8ivdYMo5~wej_Ta3iwAyX&z|Z)}#{ zVvatVPM&BW+Vk}0Xdqe_DSA)$g7)2?5Fc)kE6og{G`>Nltymi(U(!YieTYKwGbM&F zzDT%o0tOUNp|d#x!V;D#5_oV+VpGH}>2h@KX*oGIinRk8T5GR;+Hc>#d&NqAkt8#ZQb%-NV%80vbdzZiAbf}XF?Sb6Tf^xTwzyApT2@Gei~XilL#A4EaA zpipehJW(hu#a&kHT?r!p(N=HWPu9HkkUL|#@QIfM5}VCZx*7Bp4zFzbVHmqT)`sqC z(0joQ!Y_q^AEhL*`+k~9DYurFmo?F5yp<@ZWlHD;(N;>1a^LU7QJ3;NTS>xF(Wy|+ zlTXx&G_)44`e{y%X-}9llw#L=tzh-yDBWbAuYHg774gYH{3OkU4x7S(i4{?9#E)$F z>2F3VOtfb5WEvSx8n32F5N#;z|Ctzi(T1DtvYF~C{pk4j12Mb4+rx7*z#8^q#Kz4U z7BEx89t_*KRl{v8+PG8067I_5o;>c$_JbPQIIS@Gujwd^=*ywiJYIk9cj%_6==z<| zOT09QBUc*9=BY6Lm+Qn!6veL;_T`+@@@GlhQ~#EoKThUy z-K0;uv9oCv;%5qn^xfKlu191haBnB=V0Lr4*}2>XC!a*F7{&c>ZyFoZ{D}`ip~i1P z#>@YJGS{3spQDTxxUcxGwLe3(w^8_r;(NvgjPW^)qMV_`+95{!bfojKA9Y3w2QY51 z-Y~n&y{YyG6t_{DElo^LZNr+i+Kp1f+D7>|@G-)IG!?>*5^j=!La>m57y`oKAR1ca z6b^A!5DH9iRb;KyO%Z97G7qB(ifgs5S(W_PU^U6(8XO*?p!EXAL2_w zw|Y7X&Zgr*kevI2P?~%3&<`*CM9V!dTIrRZDD3vGf-sD|F?&PrJigIG4lj*@^fQ$; z+MOb8rOEtrv%UURl+@VXzb(JUhNVJtfmnJ{a{*kczb%-wK6||)YMg#NO@_fIExT~C zC%%$}yQso-QAWkZh6D!{h5Fh)6t)-se926~Ax*StuL(m4crt_|%cNnhfF8nS+~n;w zvtllFe?ooFU^PaEZD6Ct(-uzxCkWf9Gt)WjG75pmJSX@8;~NVGguu>^XiySz;Xw+e-rt3sNELhcm{F;|7`QOJWkgvfG5$o)bg z)~b;GLLqy_LabFG;_lHMLd0DWQl-?nH6gcVak~xiHrsIb#SCWmCujCoiFXb8J^8;WJmk&5qkoNMrN96H diff --git a/target/classes/hello/model/Value.class b/target/classes/hello/model/Value.class deleted file mode 100644 index 862a68f17d303c4c370907d46f8531959e3c75f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1111 zcmZ`%?@!ZU5PsgW{+dO`3QPrM0=jN6s=_yf50aReEE}@YKL1C#6$o%YBJ@^a$};#&e+Cd*F{P*H$}6^e`$gbMD^N$^I< za)bAT*PwcA&R9hWQMr)l?o*AAZEku$3vQwf|nt1$5ipn}LHC`MKULUE=6Va!M gtj;7-rMpO1H9K@9)jq+1Z0b|0o}wnfKoBzTWS> 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 diff --git a/target/classes/public/index.html b/target/classes/public/index.html deleted file mode 100644 index 566549b..0000000 --- a/target/classes/public/index.html +++ /dev/null @@ -1,10 +0,0 @@ - - - - - Title - - - - - \ No newline at end of file diff --git a/temp.txt b/temp.txt deleted file mode 100644 index 8b07638..0000000 --- a/temp.txt +++ /dev/null @@ -1,9 +0,0 @@ -print Hello -print this -print is -temp -file -my -name -is -print Rehman From 7a19b6eca3fd88285beb0f7399aac426ccfd1daa Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:38:02 +0000 Subject: [PATCH 2/2] ci: use system mvn instead of ./mvnw (missing exec bit) Co-Authored-By: Bobby Nobakht --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ad174f5..dcd4165 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,4 +20,4 @@ jobs: cache: maven - name: Build and test - run: ./mvnw -B -ntp verify + run: mvn -B -ntp verify