3

I am following spring-native getting started guide.

I can create a spring boot native image which works fine. But as soon as I add DataSource configuration and a few extra dependencies (spring-boot-starter-data-jpa, ojdbc8), even though it works fine from IDE, the native executable does not work.

I tried creating a native docker image as well as a native executable, both are giving the same error as below. I am sure I might be overlooking something.

Error:

vishal@LP-xxxxxx:/mnt/c/vishal/tempws/gs-rest-service/complete$ docker run --rm -p 8080:8080 rest-service-complete:0.0.1-SNAPSHOT
2022-08-30 22:05:49.835  INFO 1 --- [           main] o.s.nativex.NativeListener               : AOT mode enabled

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.7.1)

2022-08-30 22:05:49.837  INFO 1 --- [           main] c.e.restservice.RestServiceApplication   : Starting RestServiceApplication using Java 11.0.16.1 on aae020a2c84e with PID 1 (/workspace/com.example.restservice.RestServiceApplication started by cnb in /workspace)
2022-08-30 22:05:49.837  INFO 1 --- [           main] c.e.restservice.RestServiceApplication   : No active profile set, falling back to 1 default profile: "default"
2022-08-30 22:05:49.857  INFO 1 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2022-08-30 22:05:49.858  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2022-08-30 22:05:49.858  INFO 1 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.64]
2022-08-30 22:05:49.861  INFO 1 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
2022-08-30 22:05:49.861  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 24 ms
2022-08-30 22:05:49.867  WARN 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'dataSourceScriptDatabaseInitializer': Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Unexpected exception during bean creation; nested exception is java.lang.RuntimeException: Failed to instantiate class oracle.jdbc.driver.OracleDriver
2022-08-30 22:05:49.867  INFO 1 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2022-08-30 22:05:49.869 ERROR 1 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   :

***************************
APPLICATION FAILED TO START
***************************

Description:

Native reflection configuration for oracle.jdbc.driver.OracleDriver.<init>() is missing.

Action:

Native configuration for a method accessed reflectively is likely missing.
You can try to configure native hints in order to specify it explicitly.
See https://docs.spring.io/spring-native/docs/current/reference/htmlsingle/#native-hints for more details.

I am using sample code same as per documentation: https://github.com/spring-guides/gs-rest-service The only change I have made is in pom.xml and application.yaml. Which are provided as below.

As I mentioned, the application works fine from IntelliJ IDE without any issues. I can build native executable as well as native docker image. But executing them gives me error.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>rest-service-complete</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rest-service-complete</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
        <spring-native.version>0.12.1</spring-native.version>
        <binary-name>rest-service-complete</binary-name>
        <native-buildtools.version>0.9.13</native-buildtools.version>
        <native-image-extra-flags></native-image-extra-flags>
        <skip-native-build>true</skip-native-build>
        <repackage.classifier/>
    </properties>

    <profiles>
        <profile>
            <id>native</id>
            <properties>
                <repackage.classifier>exec</repackage.classifier>
                <skip-native-build>false</skip-native-build>
            </properties>
        </profile>
    </profiles>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.experimental</groupId>
            <artifactId>spring-native</artifactId>
            <version>${spring-native.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>oracle.jdbc</groupId>
            <artifactId>ojdbc8</artifactId>
            <version>12.2.0.1</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <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>
            <plugin>
                <groupId>org.graalvm.buildtools</groupId>
                <artifactId>native-maven-plugin</artifactId>
                <version>${native-buildtools.version}</version>
                <executions>
                    <execution>
                        <id>build-native</id>
                        <phase>package</phase>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <imageName>${binary-name}</imageName>
                    <skip>${skip-native-build}</skip>
                    <buildArgs>
                        <buildArg>-H:+ReportExceptionStackTraces ${native-image-extra-flags}</buildArg>
                    </buildArgs>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <repositories>
        <repository>
            <id>spring-release</id>
            <name>Spring release</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-release</id>
            <name>Spring release</name>
            <url>https://repo.spring.io/release</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </pluginRepository>
    </pluginRepositories>
</project>

application.yaml

spring:
  datasource:
    url: "jdbc:oracle:thin:@xx.xx.xx.xxx:1621:xxxx"
    driver-class-name: "oracle.jdbc.driver.OracleDriver"
    username: "xxxx"
    password: "xxxx"

Additional detail if it matters: I am on windows WSL2 Ubuntu

I tried both approaches on the documentation

(1) native image

> mvn spring-boot:build-image
> docker run --rm -p 8080:8080 rest-service-complete:0.0.1-SNAPSHOT

(2) native executable

> mvn -Pnative -DskipTests package
> ./target/rest-service-complete

In both cases, I got the same error.

Updates

To make sure it is not WSL2 issue, I tired later on on linux machine but got same error. I also tried connecting SQLserver with mssql-jdbc driver, same issue, works fine with java -jar but fails when running native-executable.

Vishal
  • 774
  • 12
  • 27

1 Answers1

0

To your problem

I think you're running into a native reflection issue. Native GraalVM cannot understand private reflections. A good intro to native images ca be found here (Note sections limitations point 2: Reflection):

https://www.baeldung.com/spring-native-intro

To understand and fix your particular problem you find more precise infors in the GraalVM docs:

https://docs.spring.io/spring-boot/docs/current/reference/html/native-image.html#native-image.introducing-graalvm-native-images.understanding-aot-processing

You'll need Reflection hints configured in (reflect-config.json) as decribed in the doc. It also might be good enough to disable aot (ahead of time compilation)

$ java -Dspring.aot.enabled=true -jar myapplication.jar

Why does IntelliJ work?

I'm pretty sure it runs the applications in the JVM rather than on the native image (depends on your config, but it seems rather logical). So the reflection will work as the lmitation do not apply.

How to debug

Try starting the application/image with the --verbose option and see where classes are loaded from. You can cross check libraray loading in IntelliJ run configuration and also add --verbose there, so it shows where jars are loaded from classpath.

Note some external libs come with drivers included (some shade, some don't). The first class loaded wins. So in case of a clash of 2 Java classes only one is loaded (the first). If that one has different methods it might lead to unexpected behaviours. I guess this is not the case here, but if someone else stumbles across a similar issue.

Better ways to go for

Why go native images? They are optimized but harder to start with.

Instead of $ mvn -Pnative native:compile use $ mvn compile, so you will create a normal jar file and not an optimized native image.
This should be located in target folder and will need the JVM to run it (native images are using GraalVM). there run in terminal java -jar rest-service-0.0.1-SNAPSHOT.jar (maybe adjust name, but I think this step was clear anyway).

UPDATED - Check POM dependency

Just a guess. Try changing the Oracle dependency to this:

    <dependency>
      <groupId>com.oracle.database.jdbc</groupId>
      <artifactId>ojdbc8</artifactId>
    </dependency>
supernova
  • 1,762
  • 1
  • 14
  • 31