For Josh’s examples, see his Spring Native 0.11.x repo.
This repository contains five apps we (@joshlong and @mraible) used to figure out how to make Spring Native work with JHipster.
-
spring-native-webflux
- no client, no db, just WebFlux -
spring-native-mvc
- no client, no db, just Spring MVC -
angular-webflux
- Angular client, no db -
postgres-webflux
- Angular, WebFlux, R2DBC + PostgreSQL -
postgres-mvc
- Angular, Spring MVC, JPA + PostgreSQL
If you already have a JHipster app with OIDC, you can make the changes below to make it work with Spring Native.
⚡️ You can also use the JHipster Native blueprint to generate an app with all of these changes already included!
-
Modify
pom.xml
to add Spring Native support:<repository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <pluginRepository> <id>spring-releases</id> <name>Spring Releases</name> <url>https://repo.spring.io/release</url> <snapshots> <enabled>false</enabled> </snapshots> </pluginRepository> ... <repackage.classifier/> <spring-native.version>0.11.3</spring-native.version> ... <!-- If reactive, comment out boringssl. This allows Spring Native to build the image and gets around this error: Error: Classes that should be initialized at run time got initialized during image building: io.netty.buffer.UnpooledUnsafeDirectByteBuf the class was requested to be initialized at run time (subtype of io.netty.buffer.AbstractReferenceCountedByteBuf). To see why io.netty.buffer.UnpooledUnsafeDirectByteB got initialized use trace-class-initialization=io.netty.buffer.UnpooledUnsafeDirectByteBuf --> <!--dependency> <groupId>io.netty</groupId> <artifactId>netty-tcnative-boringssl-static</artifactId> </dependency--> <dependency> <groupId>org.springframework.experimental</groupId> <artifactId>spring-native</artifactId> <version>${spring-native.version}</version> </dependency> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <classifier>${repackage.classifier}</classifier> <image> <builder>paketobuildpacks/builder:tiny</builder> <env> <BP_NATIVE_IMAGE>true</BP_NATIVE_IMAGE> </env> </image> </configuration> </plugin> <plugin> <groupId>org.springframework.experimental</groupId> <artifactId>spring-aot-maven-plugin</artifactId> <version>${spring-native.version}</version> <executions> <execution> <id>test-generate</id> <goals> <goal>test-generate</goal> </goals> </execution> <execution> <id>generate</id> <goals> <goal>generate</goal> </goals> </execution> </executions> </plugin> <profile> <id>native</id> <properties> <repackage.classifier>exec</repackage.classifier> <native-buildtools.version>0.9.10</native-buildtools.version> </properties> <dependencies> <dependency> <groupId>org.junit.platform</groupId> <artifactId>junit-platform-launcher</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.graalvm.buildtools</groupId> <artifactId>native-maven-plugin</artifactId> <version>${native-buildtools.version}</version> <extensions>true</extensions> <executions> <execution> <id>test-native</id> <phase>test</phase> <goals> <goal>test</goal> </goals> </execution> <execution> <id>build-native</id> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build> </profile>
-
Delete
src/main/resources/logback-spring.xml
and tone down logging. Removesrc/test/resources/logback.xml
too.logging: level: root: ERROR io.netty: ERROR liquibase: ERROR org.hibernate: ERROR org.springframework: ERROR com.zaxxer.hikari: ERROR org.apache.catalina: ERROR org.apache.tomcat: ERROR tech.jhipster.config: ERROR jdk.event.security: ERROR java.net: ERROR sun.net.www: ERROR
-
There’s an issue when using Spring WebFlux if you don’t use
-DskipTests
when running./mvnw package -Pnative
:[ERROR] Failed to execute goal org.springframework.experimental:spring-aot-maven-plugin:0.11.2:test-generate (test-generate) on project jhipster: Build failed during Spring AOT test code generation: Unable to execute mojo: Unable to parse configuration of mojo org.apache.maven.plugins:maven-compiler-plugin:3.9.0:testCompile for parameter compilePath: Cannot find 'compilePath' in class org.apache.maven.plugin.compiler.TestCompilerMojo -> [Help 1] [ERROR]
The error seems to be better when using Spring MVC:
Caused by: java.lang.IllegalStateException: @MockBean is not supported yet by Spring AOT and has been detected on type org.springframework.web.client.RestTemplate
-
If using Spring MVC, swap Undertow dependencies for Tomcat (in
pom.xml
) and modifyWebConfigurer
to comment outsetLocationForStaticAssets(server)
. -
Update main
App.java
to add hints for Micrometer:import org.springframework.nativex.hint.TypeHint; @TypeHint( types = { org.HdrHistogram.Histogram.class, org.HdrHistogram.ConcurrentHistogram.class })
-
Add springdocs native dependency:
<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-native</artifactId> <version>1.6.6</version> </dependency>
-
Liquibase is not supported yet, but you can make it work by adding files from this pull request to your
src/main/resources/META-INF/native-image/liquibase
directory. -
Add type hints for Liquibase and related classes.
@TypeHint( types = { ... liquibase.configuration.LiquibaseConfiguration.class, com.zaxxer.hikari.HikariDataSource.class, liquibase.change.core.LoadDataColumnConfig.class, tech.jhipster.domain.util.FixedPostgreSQL10Dialect.class, org.hibernate.type.TextType.class, })
-
If you’re using JPA, add a type hint for
java.util.HashSet.class
and turn off loading of SQL inapplication.yml
so Liquibase works:spring: ... sql: init: mode: never
Without this change, the following error happens:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSourceScriptDatabaseInitializer': Circular depends-on relationship between 'dataSourceScriptDatabaseInitializer' and 'liquibase'
-
If you’re using Spring WebFlux with R2DBC, you’ll need to add
@Component
to your Impl classes and addSimpleR2dbcRepository
to your type hints andcom.zaxxer.hikari.util.ConcurrentBag$IConcurrentBagEntry[]
as a typeName for Liquibase.@TypeHint(types = { ... org.springframework.data.r2dbc.repository.support.SimpleR2dbcRepository.class }, typeNames = "com.zaxxer.hikari.util.ConcurrentBag$IConcurrentBagEntry[]")
-
If Spring MVC, add names to any
@RequestParam
and@PathVariable
annotations.@RequestParam(name = "eagerload", required = false, defaultValue = "false") boolean eagerload @PathVariable("id") Long id
-
For logout to work, update
LogoutResource
to remove(expression = "idToken")
, inject theOidcUser
instead, and get the token from there:public ResponseEntity<?> logout(HttpServletRequest request, @AuthenticationPrincipal OidcUser oidcUser) { ... OidcIdToken idToken = oidcUser.getIdToken();
-
Caching is not supported yet. The only workaround I’ve found so far is to re-generate your app with
"cacheProvider": "no"
in your.yo-rc.json
. -
Build with
./mvnw package -Pnative,prod -DskipTests
-
-DskipTests
is needed for both Spring MVC and WebFlux. This seems to be caused by Mockito. -
Several of JHipster’s Administration features don’t work: metrics, logs, and configuration.
-
Metrics:
UnsupportedFeatureError: ThreadMXBean methods
-
Logs:
/management/loggers
returns HTML instead of JSON -
Configuration error:
org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class org.springframework.boot.actuate.context.properties.ConfigurationPropertiesReportEndpoint$ApplicationConfigurationProperties] with preset Content-Type 'null'
-
H2 doesn’t work if you build with the
dev
profile:java.lang.IllegalStateException: Failed to process lifecycle methods on bean definition with name 'h2TCPServer'
I tried adding the following to the
@TypeHint
in the main class, but it doesn’t work.typeNames = {"org.h2.tools.Server", "org.h2.server.web.WebServlet"}
This class is not on the classpath by default. Maybe that has something to do with it?