oracle / graal

GraalVM compiles Java applications into native executables that start instantly, scale fast, and use fewer compute resources 🚀
https://www.graalvm.org
Other
20.26k stars 1.62k forks source link

Failed to build native image, classes initialized at build time #7713

Closed KaeYan93 closed 9 months ago

KaeYan93 commented 10 months ago

Hi,

When i build spring native image with maven native plugin, i encountered the issue below. Can i get help to fix it? Thanks.

org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings was unintentionally initialized at build time. sun.nio.ch.UnixDomainSockets caused initialization of this class with the following trace:
        at org.bouncycastle.jcajce.provider.symmetric.Threefish$Mappings.<clinit>(Unknown Source)
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Unknown Source)
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
        at java.lang.reflect.ReflectAccess.newInstance(ReflectAccess.java:128)
        at jdk.internal.reflect.ReflectionFactory.newInstance(ReflectionFactory.java:347)
        at java.lang.Class.newInstance(Class.java:645)
        at org.bouncycastle.jce.provider.BouncyCastleProvider.loadServiceClass(BouncyCastleProvider.java:339)
        at org.bouncycastle.jce.provider.BouncyCastleProvider.loadAlgorithms(BouncyCastleProvider.java:319)
        at org.bouncycastle.jce.provider.BouncyCastleProvider.setup(BouncyCastleProvider.java:190)
        at org.bouncycastle.jce.provider.BouncyCastleProvider.access$000(BouncyCastleProvider.java:72)
        at org.bouncycastle.jce.provider.BouncyCastleProvider$1.run(BouncyCastleProvider.java:176)
        at java.security.AccessController.executePrivileged(AccessController.java:776)
        at java.security.AccessController.doPrivileged(AccessController.java:318)
        at org.bouncycastle.jce.provider.BouncyCastleProvider.<init>(BouncyCastleProvider.java:172)
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Unknown Source)
        at jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
        at jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
        at java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:480)
        at java.util.ServiceLoader$ProviderImpl.newInstance(ServiceLoader.java:789)
        at java.util.ServiceLoader$ProviderImpl.get(ServiceLoader.java:729)
        at java.util.ServiceLoader$3.next(ServiceLoader.java:1403)
        at sun.security.jca.ProviderConfig$ProviderLoader.load(ProviderConfig.java:347)
        at sun.security.jca.ProviderConfig$3.run(ProviderConfig.java:254)
        at sun.security.jca.ProviderConfig$3.run(ProviderConfig.java:248)
        at java.security.AccessController.executePrivileged(AccessController.java:776)
        at java.security.AccessController.doPrivileged(AccessController.java:318)
        at sun.security.jca.ProviderConfig.doLoadProvider(ProviderConfig.java:248)
        at sun.security.jca.ProviderConfig.getProvider(ProviderConfig.java:226)
        at sun.security.jca.ProviderList.getProvider(ProviderList.java:268)
        at sun.security.jca.ProviderList.getService(ProviderList.java:381)
        at sun.security.jca.GetInstance.getInstance(GetInstance.java:157)
        at java.security.SecureRandom.getInstance(SecureRandom.java:387)
        at sun.nio.ch.UnixDomainSockets.getRandom(UnixDomainSockets.java:118)
        at sun.nio.ch.UnixDomainSockets.<clinit>(UnixDomainSockets.java:124)

You can refer to this issue track for how i build and what error originally faced. https://github.com/spring-projects/spring-boot/issues/38163

selhagani commented 10 months ago

Hi @KaeYan93

Can you please share your OS information, the GraalVM version you're using and a concise reproducer please.

KaeYan93 commented 10 months ago

OS info: Windows 10

GraalVM version: 22.3.3

I do not have a concise reproducer yet. For this, can you help to provide some hints to replicate the issue in the reproducer? I tried to include the same dependencies in a simple project, the simple project is then able to build without issues.

KaeYan93 commented 10 months ago

Here is my dependencies in pom.xml

<properties>
        <java.version>17</java.version>
        <springdoc.version>1.6.14</springdoc.version>
        <jjwt.version>0.11.5</jjwt.version>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>${jjwt.version}</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>${jjwt.version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-gson</artifactId>
            <version>${jjwt.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

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

        <dependency>
            <groupId>com.google.code.gson</groupId>
            <artifactId>gson</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.httpcomponents.client5</groupId>
            <artifactId>httpclient5</artifactId>
            <version>5.2.1</version>
        </dependency>
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.springdoc</groupId>
            <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
            <version>2.0.0</version>
        </dependency>

        <dependency>
            <groupId>com.github.ulisesbocchio</groupId>
            <artifactId>jasypt-spring-boot-starter</artifactId>
            <version>3.0.5</version>
        </dependency>

        <dependency>
            <groupId>org.owasp.encoder</groupId>
            <artifactId>encoder</artifactId>
            <version>1.2.3</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.nimbusds/nimbus-jose-jwt -->
        <dependency>
            <groupId>com.nimbusds</groupId>
            <artifactId>nimbus-jose-jwt</artifactId>
            <version>9.24.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.ncs.secureconnect.iam</groupId>
            <artifactId>common-commons</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.ncs.secureconnect.iam</groupId>
            <artifactId>common-service</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.ncs.secureconnect.iam</groupId>
            <artifactId>common-dao</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.ncs.secureconnect.iam</groupId>
            <artifactId>common-saml</artifactId>
            <version>1.0.0</version>
        </dependency>
    </dependencies>
KaeYan93 commented 10 months ago

Hi @selhagani, I identified the issue. Whenever the project include bouncycastle dependency org.bouncycastle:bcprov-jdk18on:1.76, the bouncycastle classes will be initialized in the build time and therefore causes the above issue.

Can you help to check it? You can reproduce the issue by including the following:

<dependency> 
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk18on</artifactId>
    <version>1.76</version>
</dependency>
selhagani commented 10 months ago

Hi @KaeYan93, I'm afraid that's not enough for me to reproduce the issue on my end. Could you please create a concise project and send me the link so that I can test it on my end? Thank you.

chirontt commented 10 months ago

@KaeYan93 you should create a simple reproducer to demonstrate your problem, preferably a simple project not involving the Spring framework, to eliminate a (huge) layer of complexity, as well as solving Spring issue(s) are beyond the scope of this GraalVM project.

That said, I'm not sure how the BouncyCastle library is used in your project, but here's the compulsory info for any security provider (like BouncyCastle) to be used in native image: the security provider must be registered with the Java security framework via a Feature class, like this one from my project. As you can see, my Feature class forces all 'org.bouncycastle' classes to be initialized at build time, to fulfill the security registration requirement. I'm not sure whether the Spring framework does the same thing like that in your project.

Further more, some classes in the BouncyCastle library make use of the SecureRandom class to seed their random number generation during their class initialization, but with these classes being initialized at build time (by the above-mentioned Feature class), the native-image tool will complain about these classes using SecureRandom at build time and thus will fail the build (which is quite valid, because any random number generated during build time is like, hard-coded, into the image and thus no longer random at all.) The solution is to re-initialize these classes at run time so that they get the random numbers they need, randomly at runtime as intended. Here's the list of the BouncyCastle classes I need to re-initialize at run time in my project (your project's requirement may be different):

org.bouncycastle.crypto.CryptoServicesRegistrar
org.bouncycastle.jcajce.provider.drbg.DRBG
org.bouncycastle.jcajce.provider.drbg.DRBG$Default
org.bouncycastle.jcajce.provider.drbg.DRBG$NonceAndIV

and they are specified in the native-image command with the -H:ClassInitialization option, like:

-H:ClassInitialization=org.bouncycastle.crypto.CryptoServicesRegistrar:rerun,org.bouncycastle.jcajce.provider.drbg.DRBG:rerun,...

Again, I don't know what the Spring framework does with those BouncyCastle classes in your project, so I can't comment further. Again, as @selhagani requested, please make an effort to provide a simple reproducer so that we all can see what/where the problem lies.