spring-native-examples icon indicating copy to clipboard operation
spring-native-examples copied to clipboard

JHipster Works with Spring Native!

= Spring Native with JHipster

https://www.youtube.com/watch?v=f0yrxKRU-ME[Watch the presentation from the Garden State JUG]

For Josh's examples, see his https://github.com/spring-tips/spring-native-011x[Spring Native 0.11.x repo].

This repository contains five apps we (https://github.com/joshlong[@joshlong] and https://github.com/mraible[@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 https://github.com/jhipster/generator-jhipster-native[JHipster Native blueprint] to generate an app with all of these changes already included!

. Modify pom.xml to add Spring Native support: + [source,xml]

spring-releases Spring Releases https://repo.spring.io/release false spring-releases Spring Releases https://repo.spring.io/release false ...

<repackage.classifier/> <spring-native.version>0.11.3</spring-native.version> ...

org.springframework.experimental spring-native ${spring-native.version} org.springframework.boot spring-boot-maven-plugin ${repackage.classifier} paketobuildpacks/builder:tiny true org.springframework.experimental spring-aot-maven-plugin ${spring-native.version} test-generate test-generate generate generate native exec 0.9.10 org.junit.platform junit-platform-launcher test org.graalvm.buildtools native-maven-plugin ${native-buildtools.version} true test-native test test build-native package build ----

. Delete src/main/resources/logback-spring.xml and tone down logging. Remove src/test/resources/logback.xml too. + [source,yaml]

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 modify WebConfigurer to comment out setLocationForStaticAssets(server).

. Update main App.java to add hints for Micrometer: + [source,java]

import org.springframework.nativex.hint.TypeHint;

@TypeHint( types = { org.HdrHistogram.Histogram.class, org.HdrHistogram.ConcurrentHistogram.class })

. Add springdocs native dependency: + [source,xml]

org.springdoc springdoc-openapi-native 1.6.6 ----

. Liquibase is https://github.com/spring-projects-experimental/spring-native/issues/620[not supported yet], but you can make it work by adding files from https://github.com/liquibase/liquibase/pull/2005[this pull request] to your src/main/resources/META-INF/native-image/liquibase directory.

. Add type hints for Liquibase and related classes. + [source,java]

@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 in application.yml so Liquibase works: + [source,yaml]

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 add SimpleR2dbcRepository to your type hints and com.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. + [source,java]

@RequestParam(name = "eagerload", required = false, defaultValue = "false") boolean eagerload @PathVariable("id") Long id

. For logout to work, update LogoutResource to remove (expression = "idToken"), inject the OidcUser instead, and get the token from there: + [source,java]

public ResponseEntity<?> logout(HttpServletRequest request, @AuthenticationPrincipal OidcUser oidcUser) { ... OidcIdToken idToken = oidcUser.getIdToken();

. Caching is https://github.com/spring-projects-experimental/spring-native/issues/465[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

== Known Issues

  • -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. + [source,java]

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?