[go: nahoru, domu]

Skip to content

mraible/spring-native-examples

Repository files navigation

Spring Native with JHipster

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

You should be able to create a JHipster monolith with OIDC, then make the following changes.

  1. 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.2</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.9</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>
  2. Delete src/main/resources/logback-spring.xml and tone down logging. Remove src/test/resources/logback.xml too.

    logging:
      level:
        root: ERROR
        io.netty: ERROR
        org.springframework: INFO
  3. 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
  4. If using Spring MVC, swap Undertow dependencies for Tomcat (in pom.xml) and modify WebConfigurer to comment out setLocationForStaticAssets(server).

  5. 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
        })
  6. Add springdocs native dependency:

    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-native</artifactId>
        <version>1.6.2</version>
    </dependency>

    Swagger doesn’t seem to work with WebFlux. In my browser’s console, it says:

    Failed to load resource: the server responded with a status of 404 :8080/management/jhiopenapigroups

    With Spring MVC, things don’t even start up when running ./mvnw:

    Parameter 0 of constructor in org.springdoc.nativex.core.SpringDocHints
    required a bean of type 'org.springdoc.core.SwaggerUiConfigProperties' that could not be found.
  7. 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.

  8. 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
        })
  9. If you’re using JPA, configure Hibernate build-time bytecode enhancement. Add the plugin right after the spring-aot-maven-plugin.

    In application.yml, turn off automatic datasource initialization so Liquibase works:

    spring:
      ...
      jpa:
        defer-datasource-initialization: true

    This almost works. Current error is:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase':
     Circular depends-on relationship between 'liquibase' and 'entityManagerFactory'
  10. Build with ./mvnw package -Pnative,prod -DskipTests

  11. If you’re using Spring WebFlux with R2DBC, we haven’t figured that one out yet.

Known Issues

  • Liquibase no longer works. See Error creating bean with name 'liquibase' error above for more information.

  • -DskipTests is needed for both Spring MVC and WebFlux. Might because by Mockito. Excluding it and deleting tests worked last time. Not with 0.11.2.

  • Several of JHipster’s Administration features don’t work: metrics, logs, API docs, and configuration.

  • Metrics: UnsupportedFeatureError: ThreadMXBean methods

  • Logs: /management/loggers returns HTML instead of JSON

  • Configuration:

    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

    java.lang.IllegalStateException: Failed to process lifecycle methods on bean definition with name 'h2TCPServer'

    It might be possible to fix with a @TypeHint for org.h2.tools.Server.class. However, this class is not in the classpath by default.