From 4ea66b59ce73d9b79db070e4f24075807b7ca782 Mon Sep 17 00:00:00 2001 From: Ralf Ueberfuhr <40685729+ueberfuhr@users.noreply.github.com> Date: Thu, 24 Nov 2022 09:47:54 +0100 Subject: [PATCH] BAEL-5951: Spring Boot 3 Sample for Native Image builds incl. Runtime Hints (#13047) * Update Spring Boot, Spring Native and Native Maven Plugin versions * BAEL-5951: Spring Boot 3 Sample for Native Image builds incl. Runtime Hints * BAEL-5951: Configure POMs and add Swagger UI runtime hints * BAEL-5951: Remove Swagger UI runtime hints * BAEL-5951: Remove Spring Boot3 parent POM from profiles because of JDK17 dependency during build (building the parent POM is even not necessary) * BAEL-5951: Add tests * BAEL-5951: Fix tests (PMD naming conventions) --- parent-boot-3/pom.xml | 193 +++++++++++++++++- pom.xml | 8 +- .../spring-boot-3-native/pom.xml | 86 ++++++++ .../sample/BoundaryConfiguration.java | 23 +++ .../baeldung/sample/GraphQlRuntimeHints.java | 22 ++ .../baeldung/sample/JacksonRuntimeHints.java | 30 +++ .../com/baeldung/sample/TodoApplication.java | 11 + .../main/java/com/baeldung/sample/User.java | 31 +++ .../com/baeldung/sample/UserController.java | 19 ++ .../src/main/resources/application.yml | 6 + .../src/main/resources/graphql/todo.graphqls | 8 + .../src/main/resources/static/index.html | 17 ++ ...cksonAutoConfigurationIntegrationTest.java | 23 +++ .../sample/JacksonRuntimeHintsUnitTest.java | 27 +++ spring-native/pom-nativeimage.xml | 8 +- spring-native/pom.xml | 6 +- 16 files changed, 494 insertions(+), 24 deletions(-) create mode 100644 spring-boot-modules/spring-boot-3-native/pom.xml create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/BoundaryConfiguration.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/GraphQlRuntimeHints.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/JacksonRuntimeHints.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/TodoApplication.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/User.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/UserController.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/resources/application.yml create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/resources/graphql/todo.graphqls create mode 100644 spring-boot-modules/spring-boot-3-native/src/main/resources/static/index.html create mode 100644 spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonAutoConfigurationIntegrationTest.java create mode 100644 spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonRuntimeHintsUnitTest.java diff --git a/parent-boot-3/pom.xml b/parent-boot-3/pom.xml index 26d353f2ae..8f891ec788 100644 --- a/parent-boot-3/pom.xml +++ b/parent-boot-3/pom.xml @@ -1,7 +1,7 @@ + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 parent-boot-3 0.0.1-SNAPSHOT @@ -34,6 +34,26 @@ + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-core + ${logback.version} + + + org.slf4j + slf4j-api + ${slf4j.version} + + + org.slf4j + jcl-over-slf4j + ${slf4j.version} + org.springframework.boot spring-boot-starter-test @@ -44,26 +64,166 @@ + + org.apache.maven.plugins + maven-clean-plugin + ${maven-clean-plugin.version} + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + ${start-class} + true + + + + + + org.apache.maven.plugins + maven-surefire-plugin + ${maven-surefire-plugin.version} + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + org.graalvm.buildtools + native-maven-plugin + ${native-build-tools-plugin.version} + true + org.springframework.boot spring-boot-maven-plugin ${spring-boot.version} ${start-class} - - - - - repackage - - - + + + org.springframework.boot + spring-boot-maven-plugin + + + repackage + + repackage + + + + + + + + native + + + + org.springframework.boot + spring-boot-maven-plugin + + + paketobuildpacks/builder:tiny + + true + + + + + + process-aot + + process-aot + + + + + + org.graalvm.buildtools + native-maven-plugin + + ${project.build.outputDirectory} + + true + + 22.3 + + + + add-reachability-metadata + + add-reachability-metadata + + + + + + + + + nativeTest + + + org.junit.platform + junit-platform-launcher + test + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + process-test-aot + + process-test-aot + + + + + + org.graalvm.buildtools + native-maven-plugin + + ${project.build.outputDirectory} + + true + + 22.3 + + + + native-test + + test + + + + + + + + + + spring-milestones @@ -87,10 +247,21 @@ + 3.2.0 + 3.10.1 + 3.3.0 + 3.3.0 + 2.22.2 + 3.0.0-M3 5.8.2 3.0.0-M7 + 0.9.17 17 + ${java.version} + ${java.version} + 1.4.4 + 2.0.3 - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index d51b357c46..ba1f778614 100644 --- a/pom.xml +++ b/pom.xml @@ -326,7 +326,6 @@ parent-boot-1 parent-boot-2 - parent-boot-3 parent-spring-4 parent-spring-5 parent-java @@ -533,7 +532,6 @@ parent-boot-1 parent-boot-2 - parent-boot-3 parent-spring-4 parent-spring-5 parent-java @@ -676,7 +674,6 @@ parent-boot-1 parent-boot-2 - parent-boot-3 parent-spring-4 parent-spring-5 parent-java @@ -731,7 +728,6 @@ parent-boot-1 parent-boot-2 - parent-boot-3 parent-spring-4 parent-spring-5 parent-java @@ -928,7 +924,6 @@ parent-boot-1 parent-boot-2 - parent-boot-3 parent-spring-4 parent-spring-5 parent-java @@ -1064,7 +1059,6 @@ parent-boot-1 parent-boot-2 - parent-boot-3 parent-spring-4 parent-spring-5 parent-java @@ -1202,6 +1196,7 @@ spring-boot-modules/spring-boot-cassandre spring-boot-modules/spring-boot-camel spring-boot-modules/spring-boot-3 + spring-boot-modules/spring-boot-3-native spring-swagger-codegen/custom-validations-opeanpi-codegen testing-modules/testing-assertions persistence-modules/fauna @@ -1279,6 +1274,7 @@ spring-boot-modules/spring-boot-cassandre spring-boot-modules/spring-boot-camel spring-boot-modules/spring-boot-3 + spring-boot-modules/spring-boot-3-native spring-swagger-codegen/custom-validations-opeanpi-codegen testing-modules/testing-assertions persistence-modules/fauna diff --git a/spring-boot-modules/spring-boot-3-native/pom.xml b/spring-boot-modules/spring-boot-3-native/pom.xml new file mode 100644 index 0000000000..9e664efc02 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/pom.xml @@ -0,0 +1,86 @@ + + + 4.0.0 + spring-boot-3-native + 0.0.1-SNAPSHOT + spring-boot-3-native + Demo project for Spring Boot + + + com.baeldung + parent-boot-3 + 0.0.1-SNAPSHOT + ../../parent-boot-3/pom.xml + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springdoc + springdoc-openapi-starter-webmvc-ui + ${spring-doc.version} + + + org.springframework.boot + spring-boot-starter-graphql + + + org.springframework.boot + spring-boot-starter-test + test + + + + + + + + org.graalvm.buildtools + native-maven-plugin + + + true + + + + + + + + + + + native + + + + org.graalvm.buildtools + native-maven-plugin + + + build-native + + compile-no-fork + + package + + + + + + + + + + 3.0.0-RC2 + 2.0.0-RC1 + 0.9.17 + com.baeldung.sample.TodoApplication + + + diff --git a/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/BoundaryConfiguration.java b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/BoundaryConfiguration.java new file mode 100644 index 0000000000..1b7894abf9 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/BoundaryConfiguration.java @@ -0,0 +1,23 @@ +package com.baeldung.sample; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class BoundaryConfiguration { + + @Bean + public WebMvcConfigurer webMvcConfigurer() { + return new WebMvcConfigurer() { + @Override + public void addViewControllers(ViewControllerRegistry registry) { + registry + .addViewController("/") + .setViewName("redirect:/index.html"); + } + }; + } + +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/GraphQlRuntimeHints.java b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/GraphQlRuntimeHints.java new file mode 100644 index 0000000000..c20ff4cafa --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/GraphQlRuntimeHints.java @@ -0,0 +1,22 @@ +package com.baeldung.sample; + +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; + +@ImportRuntimeHints(GraphQlRuntimeHints.GraphQlResourcesRegistrar.class) +@Configuration +public class GraphQlRuntimeHints { + + static class GraphQlResourcesRegistrar implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints.resources() + .registerPattern("graphql/**/") + .registerPattern("graphiql/index.html"); + } + } + +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/JacksonRuntimeHints.java b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/JacksonRuntimeHints.java new file mode 100644 index 0000000000..9c16ab0009 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/JacksonRuntimeHints.java @@ -0,0 +1,30 @@ +package com.baeldung.sample; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportRuntimeHints; + +@Configuration +@ImportRuntimeHints(JacksonRuntimeHints.PropertyNamingStrategyRegistrar.class) +public class JacksonRuntimeHints { + + private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(JacksonRuntimeHints.class); + + static class PropertyNamingStrategyRegistrar implements RuntimeHintsRegistrar { + + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + try { + hints + .reflection() + .registerField(PropertyNamingStrategies.class.getDeclaredField("SNAKE_CASE")); + log.info("Registered native hint for SNAKE_CASE!"); + } catch (NoSuchFieldException e) { + log.warn("Unable to register native hint for SNAKE_CASE!"); + } + } + } + +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/TodoApplication.java b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/TodoApplication.java new file mode 100644 index 0000000000..72c9c0e482 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/TodoApplication.java @@ -0,0 +1,11 @@ +package com.baeldung.sample; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +@SpringBootApplication +public class TodoApplication { + public static void main(String[] args) { + SpringApplication.run(TodoApplication.class, args); + } + +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/User.java b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/User.java new file mode 100644 index 0000000000..fd5597e3d2 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/User.java @@ -0,0 +1,31 @@ +package com.baeldung.sample; + +public class User { + + private String firstName; + private String lastName; + + public User(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } + + public User() { + } + + 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; + } +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/UserController.java b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/UserController.java new file mode 100644 index 0000000000..449bc09c46 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/java/com/baeldung/sample/UserController.java @@ -0,0 +1,19 @@ +package com.baeldung.sample; + +import org.springframework.graphql.data.method.annotation.QueryMapping; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/v1/user") +public class UserController { + + @QueryMapping("getUser") + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + public User getUser() { + return new User("John", "Doe"); + } + +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/resources/application.yml b/spring-boot-modules/spring-boot-3-native/src/main/resources/application.yml new file mode 100644 index 0000000000..0e206a6cd5 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/resources/application.yml @@ -0,0 +1,6 @@ +spring: + graphql: + graphiql: + enabled: true + jackson: + property-naming-strategy: SNAKE_CASE diff --git a/spring-boot-modules/spring-boot-3-native/src/main/resources/graphql/todo.graphqls b/spring-boot-modules/spring-boot-3-native/src/main/resources/graphql/todo.graphqls new file mode 100644 index 0000000000..c2dd5c1565 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/resources/graphql/todo.graphqls @@ -0,0 +1,8 @@ +type User { + firstName: String! + lastName: String! +} + +type Query { + getUser: User! +} diff --git a/spring-boot-modules/spring-boot-3-native/src/main/resources/static/index.html b/spring-boot-modules/spring-boot-3-native/src/main/resources/static/index.html new file mode 100644 index 0000000000..d8ab49c5ea --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/main/resources/static/index.html @@ -0,0 +1,17 @@ + + + + + Spring Native Demo Application + + +

Spring Native Demo Application

+

+ This is a sample application that can be built as native executable. +

+ + + diff --git a/spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonAutoConfigurationIntegrationTest.java b/spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonAutoConfigurationIntegrationTest.java new file mode 100644 index 0000000000..83ec6b3923 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonAutoConfigurationIntegrationTest.java @@ -0,0 +1,23 @@ +package com.baeldung.sample; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import static org.assertj.core.api.Assertions.assertThat; + +@SpringBootTest +class JacksonAutoConfigurationIntegrationTest { + + @Autowired + ObjectMapper mapper; + + @Test + void shouldUseSnakeCasePropertyNamingStrategy() { + assertThat(mapper.getPropertyNamingStrategy()) + .isSameAs(PropertyNamingStrategies.SNAKE_CASE); + } + +} diff --git a/spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonRuntimeHintsUnitTest.java b/spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonRuntimeHintsUnitTest.java new file mode 100644 index 0000000000..eb45db9407 --- /dev/null +++ b/spring-boot-modules/spring-boot-3-native/src/test/java/com/baeldung/sample/JacksonRuntimeHintsUnitTest.java @@ -0,0 +1,27 @@ +package com.baeldung.sample; + +import com.fasterxml.jackson.databind.PropertyNamingStrategies; +import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.predicate.RuntimeHintsPredicates; + +import static org.assertj.core.api.Assertions.assertThat; + +class JacksonRuntimeHintsUnitTest { + + @Test + void shouldRegisterSnakeCasePropertyNamingStrategy() { + // arrange + final var hints = new RuntimeHints(); + final var expectSnakeCaseHint = RuntimeHintsPredicates + .reflection() + .onField(PropertyNamingStrategies.class, "SNAKE_CASE"); + // act + new JacksonRuntimeHints.PropertyNamingStrategyRegistrar() + .registerHints(hints, getClass().getClassLoader()); + // assert + assertThat(expectSnakeCaseHint).accepts(hints); + + } + +} diff --git a/spring-native/pom-nativeimage.xml b/spring-native/pom-nativeimage.xml index 1b2cc3944a..6d42bedbe1 100644 --- a/spring-native/pom-nativeimage.xml +++ b/spring-native/pom-nativeimage.xml @@ -111,12 +111,12 @@ - 2.5.1 - 0.10.0 - 0.9.0 + 2.7.1 + 0.12.1 + 0.9.17 1.8 1.8 1.8 - \ No newline at end of file + diff --git a/spring-native/pom.xml b/spring-native/pom.xml index f54d7b5308..0cd502fbc1 100644 --- a/spring-native/pom.xml +++ b/spring-native/pom.xml @@ -70,12 +70,12 @@ paketobuildpacks/builder:tiny - 2.5.1 - 0.10.0 + 2.7.1 + 0.12.1 1.8 1.8 1.8 2.17.1 - \ No newline at end of file +