Search code examples
quarkusgraalvmgraalvm-native-imagequarkus-rest-client

quarkus-rest-client can't be used with @ApplicationScoped beans for native images: No instances of sun.security.provider.NativePRNG allowed in heap


Sample project to reproduce this issue available on Github

I'm using quarkus-rest-client to invoke a REST service from my Quarkus application.

ExampleResource offers one endpoint http://localhost:8080/echo that calls https://postman-echo.com/get using a RestClient:

@Path("/echo")
public class ExampleResource {

    private final RestClient restClient;

    public ExampleResource(RestClient restClient){
        this.restClient = restClient;
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response get() {
        return restClient.get();
    }
}
@ApplicationScoped
public class RestClient {

    private final Client httpClient;

    public RestClient() {
        this.httpClient = ResteasyClientBuilder.newBuilder().build();
    }

    public Response get() {
        return httpClient.target("https://postman-echo.com/get").request().get();
    }
}

Although it works ok, when I try to generate a native image it fails:

./mvnw package -Pnative -Dquarkus.native.container-build=true 
Error: No instances of sun.security.provider.NativePRNG are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace:  object java.security.SecureRandom
        object sun.security.ssl.SSLContextImpl$TLSContext
        object sun.security.ssl.SSLSocketFactoryImpl
        object org.apache.http.conn.ssl.SSLConnectionSocketFactory
        object java.util.concurrent.ConcurrentHashMap$Node
        object java.util.concurrent.ConcurrentHashMap$Node[]
        object java.util.concurrent.ConcurrentHashMap
        object org.apache.http.config.Registry
        object org.apache.http.impl.conn.DefaultHttpClientConnectionOperator
        object org.apache.http.impl.conn.PoolingHttpClientConnectionManager
        object org.apache.http.impl.client.HttpClientBuilder$2
        object java.lang.Object[]
        object java.util.ArrayList
        object org.apache.http.impl.client.InternalHttpClient
        object org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine
        object org.jboss.resteasy.client.jaxrs.internal.ResteasyClientImpl
        object org.codependent.RestClient_ClientProxy
        object org.codependent.RestClient_Bean
        object java.lang.Object[]
        object java.util.ArrayList
        object io.quarkus.arc.impl.ArcContainerImpl
        object io.quarkus.arc.runtime.ArcRecorder$2
        field io.quarkus.resteasy.common.runtime.QuarkusInjectorFactory.CONTAINER

com.oracle.svm.core.util.UserError$UserException: No instances of sun.security.provider.NativePRNG are allowed in the image heap as this class should be initialized at image runtime. To see how this object got instantiated use -H:+TraceClassInitialization.
Detailed message:
Trace:  object java.security.SecureRandom
        object sun.security.ssl.SSLContextImpl$TLSContext
        object sun.security.ssl.SSLSocketFactoryImpl
        object org.apache.http.conn.ssl.SSLConnectionSocketFactory
        object java.util.concurrent.ConcurrentHashMap$Node
        object java.util.concurrent.ConcurrentHashMap$Node[]
        object java.util.concurrent.ConcurrentHashMap
        object org.apache.http.config.Registry
        object org.apache.http.impl.conn.DefaultHttpClientConnectionOperator
        object org.apache.http.impl.conn.PoolingHttpClientConnectionManager
        object org.apache.http.impl.client.HttpClientBuilder$2
        object java.lang.Object[]
        object java.util.ArrayList
        object org.apache.http.impl.client.InternalHttpClient
        object org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine
        object org.jboss.resteasy.client.jaxrs.internal.ResteasyClientImpl
        object org.codependent.RestClient_ClientProxy
        object org.codependent.RestClient_Bean
        object java.lang.Object[]
        object java.util.ArrayList
        object io.quarkus.arc.impl.ArcContainerImpl
        object io.quarkus.arc.runtime.ArcRecorder$2
        field io.quarkus.resteasy.common.runtime.QuarkusInjectorFactory.CONTAINER

If I follow the instructions from the Quarkus guide regarding this error, adding the additional build parameters in the pom it still fails:

<quarkus.native.additional-build-args>--initialize-at-run-time=org.codependent.RestClient,-H:+TraceClassInitialization</quarkus.native.additional-build-args>
./mvnw package -Pnative -Dquarkus.native.container-build=true 
com.oracle.svm.core.util.UserError$UserException: Classes that should be initialized at run time got initialized during image building:
 org.codependent.RestClient the class was requested to be initialized at build time (from the command line). io.quarkus.runner.ApplicationImpl caused initialization of this class with the following trace: 
        at org.codependent.RestClient.<clinit>(RestClient.java)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:348)
        at org.codependent.RestClient_Bean.<init>(RestClient_Bean.zig:135)
        at io.quarkus.arc.setup.Default_ComponentsProvider.addBeans1(Default_ComponentsProvider.zig:106)
        at io.quarkus.arc.setup.Default_ComponentsProvider.getComponents(Default_ComponentsProvider.zig:38)
        at io.quarkus.arc.impl.ArcContainerImpl.<init>(ArcContainerImpl.java:103)
        at io.quarkus.arc.Arc.initialize(Arc.java:20)
        at io.quarkus.arc.runtime.ArcRecorder.getContainer(ArcRecorder.java:35)
        at io.quarkus.deployment.steps.ArcProcessor$generateResources20.deploy_0(ArcProcessor$generateResources20.zig:72)
        at io.quarkus.deployment.steps.ArcProcessor$generateResources20.deploy(ArcProcessor$generateResources20.zig:36)
        at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:338)

org.codependent.RestClient_ClientProxy the class was requested to be initialized at build time (subtype of org.codependent.RestClient). io.quarkus.runner.ApplicationImpl caused initialization of this class with the following trace: 
        at org.codependent.RestClient_ClientProxy.<clinit>(RestClient_ClientProxy.zig)
        at org.codependent.RestClient_Bean.<init>(RestClient_Bean.zig:164)
        at io.quarkus.arc.setup.Default_ComponentsProvider.addBeans1(Default_ComponentsProvider.zig:106)
        at io.quarkus.arc.setup.Default_ComponentsProvider.getComponents(Default_ComponentsProvider.zig:38)
        at io.quarkus.arc.impl.ArcContainerImpl.<init>(ArcContainerImpl.java:103)
        at io.quarkus.arc.Arc.initialize(Arc.java:20)
        at io.quarkus.arc.runtime.ArcRecorder.getContainer(ArcRecorder.java:35)
        at io.quarkus.deployment.steps.ArcProcessor$generateResources20.deploy_0(ArcProcessor$generateResources20.zig:72)
        at io.quarkus.deployment.steps.ArcProcessor$generateResources20.deploy(ArcProcessor$generateResources20.zig:36)
        at io.quarkus.runner.ApplicationImpl.<clinit>(ApplicationImpl.zig:338)


        at com.oracle.svm.core.util.UserError.abort(UserError.java:65)
        at com.oracle.svm.hosted.classinitialization.ConfigurableClassInitialization.checkDelayedInitialization(ConfigurableClassInitialization.java:510)
        at com.oracle.svm.hosted.classinitialization.ClassInitializationFeature.duringAnalysis(ClassInitializationFeature.java:187)
        at com.oracle.svm.hosted.NativeImageGenerator.lambda$runPointsToAnalysis$8(NativeImageGenerator.java:710)
        at com.oracle.svm.hosted.FeatureHandler.forEachFeature(FeatureHandler.java:63)
        at com.oracle.svm.hosted.NativeImageGenerator.runPointsToAnalysis(NativeImageGenerator.java:710)
        at com.oracle.svm.hosted.NativeImageGenerator.doRun(NativeImageGenerator.java:530)
        at com.oracle.svm.hosted.NativeImageGenerator.lambda$run$0(NativeImageGenerator.java:445)
        at java.util.concurrent.ForkJoinTask$AdaptedRunnableAction.exec(ForkJoinTask.java:1386)
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
        at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

The problem happens when the RestClient is defined as an @ApplicationScoped component. If I change it so that it is instantiated the native image generation doesn't have any problem:

@Path("/echo")
public class ExampleResource {

    private final RestClient restClient;

    public ExampleResource(){
        this.restClient = new RestClient();
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public Response get() {
        return restClient.get();
    }
}
public class RestClient {

    private final Client httpClient;

    public RestClient() {
        this.httpClient = ResteasyClientBuilder.newBuilder().build();
    }

    public Response get() {
        return httpClient.target("https://postman-echo.com/get").request().get();
    }

}
./mvnw package -Pnative -Dquarkus.native.container-build=true 
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 72716ms
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  01:14 min
[INFO] Finished at: 2020-03-21T12:48:07+01:00
[INFO] -

The question is how can I define the RestClient as an @ApplicationScoped component and be able to generate a native image?


Solution

  • As answered here, you are not using the MicroProfile REST Client, but instead the JAX-RS Client.

    In any case, you can get your code working in native mode by changing the RestClient class to:

    @ApplicationScoped
    public class RestClient {
    
        private Client client;
    
        public Response get() {
            return getClient().target("https://postman-echo.com/get").request().get();
        }
    
        private Client getClient() {
            if (client == null) {
                client = ResteasyClientBuilder.newBuilder().build();
            }
            return client;
        }
    
    }
    

    and also adding:

    quarkus.native.enable-https-url-handler=true
    

    to application.properties