Quarkus: NullPointerException for REST client in native build

Created on 25 Jun 2020  路  12Comments  路  Source: quarkusio/quarkus

Describe the bug
In our application, we have a REST endpoint (defined in IntempusReportResource) that calls another REST endpoint with a REST client (defined in IntempusAPIService). However a NullPointerException is thrown when we call a method defined in the REST client when the application is built as a native build. It works when run in development mode (run using ./mvnw quarkus:dev).

IntempusReportResource:

@Path("/intempus")
public class IntempusReportResource {

    ...

    @Inject
    @RestClient
    IntempusAPIService intempusAPIService;

    ...

    @GET
    @Path("/createreport")
    public void run() {
        ...
        final IntempusResponse<Case> caseObjects = intempusAPIService.getCasesWithFilters(true);
        ...
    }
}

IntempusAPIService:

@Path("/v1")
@ApplicationScoped
@RegisterRestClient
@ClientHeaderParam(name = "Authorization", value = "{getAuthorizationHeader}")
public interface IntempusAPIService {

    ...

    @GET
    @Path("/case")
    @Produces("application/json")
    IntempusResponse<Case> getCasesWithFilters(@DefaultValue("true") @QueryParam("active") boolean active);

    ...
}

Expected behavior
Successful REST call with parse of response.

Actual behavior
NullPointerException thrown. Stack trace:

2020-06-25 09:14:45,337 ERROR [io.qua.ver.htt.run.QuarkusErrorHandler] (executor-thread-1) HTTP Request to /intempus/createreport failed, error id: 98f5141d-778b-4d2e-879a-0d58147b9e5a-1: org.jboss.resteasy.spi.UnhandledException: java.lang.NullPointerException
        at org.jboss.resteasy.core.ExceptionHandler.handleApplicationException(ExceptionHandler.java:106)
        at org.jboss.resteasy.core.ExceptionHandler.handleException(ExceptionHandler.java:372)
        at org.jboss.resteasy.core.SynchronousDispatcher.writeException(SynchronousDispatcher.java:216)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:515)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:259)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:160)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:163)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:245)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:132)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.access$000(VertxRequestHandler.java:37)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler$1.run(VertxRequestHandler.java:94)
        at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
        at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at java.lang.Thread.run(Thread.java:834)
        at org.jboss.threads.JBossThread.run(JBossThread.java:479)
        at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:517)
        at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:193)
Caused by: java.lang.NullPointerException
        at java.lang.reflect.Method.invoke(Method.java:559)
        at org.jboss.resteasy.microprofile.client.header.ComputedHeaderValueFiller.generateValues(ComputedHeaderValueFiller.java:121)
        at org.jboss.resteasy.microprofile.client.header.ClientHeaderValueGenerator.fillHeaders(ClientHeaderValueGenerator.java:59)
        at org.jboss.resteasy.microprofile.client.header.ClientHeaderProvider.lambda$addHeaders$3(ClientHeaderProvider.java:68)
        at java.util.HashMap$Values.forEach(HashMap.java:976)
        at org.jboss.resteasy.microprofile.client.header.ClientHeaderProvider.addHeaders(ClientHeaderProvider.java:68)
        at org.jboss.resteasy.microprofile.client.header.ClientHeadersRequestFilter.lambda$filter$0(ClientHeadersRequestFilter.java:40)
        at java.util.Optional.ifPresent(Optional.java:183)
        at org.jboss.resteasy.microprofile.client.header.ClientHeadersRequestFilter.filter(ClientHeadersRequestFilter.java:40)
        at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.filterRequest(ClientInvocation.java:679)
        at org.jboss.resteasy.microprofile.client.impl.MpClientInvocation.filterRequest(MpClientInvocation.java:45)
        at org.jboss.resteasy.client.jaxrs.internal.ClientInvocation.invoke(ClientInvocation.java:485)
        at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invokeSync(ClientInvoker.java:149)
        at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientInvoker.invoke(ClientInvoker.java:112)
        at org.jboss.resteasy.client.jaxrs.internal.proxy.ClientProxy.invoke(ClientProxy.java:76)
        at com.sun.proxy.$Proxy220.getCasesWithFilters(Unknown Source)
        at java.lang.reflect.Method.invoke(Method.java:566)
        at org.jboss.resteasy.microprofile.client.ProxyInvocationHandler.invoke(ProxyInvocationHandler.java:149)
        at com.sun.proxy.$Proxy221.getCasesWithFilters(Unknown Source)
        at io.aety.client.intempusclient.IntempusAPIService_a1dc6da61f1cf5406a908cfbeb337495603b6618_Synthetic_ClientProxy.getCasesWithFilters(IntempusAPIService_a1dc6da61f1cf5406a908cfbeb337495603b6618_Synthetic_ClientProxy.zig:160)
        at io.aety.client.intempusclient.IntempusReportResource.getStatusReportForCases(IntempusReportResource.java:87)
        at io.aety.client.intempusclient.IntempusReportResource.getStatusReportForAllCases(IntempusReportResource.java:72)
        at io.aety.client.intempusclient.IntempusReportResource.run(IntempusReportResource.java:50)
        at java.lang.reflect.Method.invoke(Method.java:566)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:167)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:130)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:621)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:487)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:437)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:362)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:439)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:400)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:374)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:67)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:488)
        ... 19 more

To Reproduce
Steps to reproduce the behaviour:

  1. Build project with native package type.
  2. Run native build.
  3. Make HTTP request to /intempus/createreport endpoint.

Configuration
N/A

Screenshots
N/A

Environment (please complete the following information):

  • Output of uname -a or ver:
Darwin MacBook-Pro.local 19.5.0 Darwin Kernel Version 19.5.0: Tue May 26 20:41:44 PDT 2020; root:xnu-6153.121.2~2/RELEASE_X86_64 x86_64
  • Output of java -version:
openjdk version "11.0.7" 2020-04-14
OpenJDK Runtime Environment GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02)
OpenJDK 64-Bit Server VM GraalVM CE 20.1.0 (build 11.0.7+10-jvmci-20.1-b02, mixed mode, sharing)
  • GraalVM version (if different from Java): N/A
  • Quarkus version or git rev: 1.5.2.Final
  • Build tool (ie. output of mvnw --version or gradlew --version):
Apache Maven 3.6.2 (40f52333136460af0dc0d7232c0dc0bcf0d9e117; 2019-08-27T17:06:16+02:00)
Maven home: /Users/gustavclausen/.m2/wrapper/dists/apache-maven-3.6.2-bin/795eh28tki48bv3l67maojf0ra/apache-maven-3.6.2
Java version: 11.0.7, vendor: GraalVM Community, runtime: /Library/Java/JavaVirtualMachines/graalvm-ce-java11-20.1.0/Contents/Home
Default locale: en_GB, platform encoding: UTF-8
OS name: "mac os x", version: "10.15.5", arch: "x86_64", family: "mac"

Additional context
N/A

arerest-client kinbug

Most helpful comment

:+1:

I'll reopen the issue, since this is an actual problem we need to solve

All 12 comments

Could you create a small Maven reproducer? That would help figuring out what's going on. Thanks!

Does IntempusAPIService by any chance have default methods?

Does IntempusAPIService by any chance have default methods?

Yes, we have the following method to retrieve an API key specified in application.properties:

default String getAuthorizationHeader(){
    final Config config = ConfigProvider.getConfig();
    String apikey = config.getValue("<x>.<x>.<x>.<x>.apikey", String.class);
    return apikey;
}

Is there a better way to do this?

cc @kenfinnigan @michalszynkiewicz I think we have discussed the problem with default methods in native before.

Is there a better way to do this?

Not at the moment unfortunately, not supporting default methods is a limitation of native mode for rest-client at the moment.

Thanks for the quick response 馃檪 The use of default methods was the cause of the issue.

:+1:

I'll reopen the issue, since this is an actual problem we need to solve

We have faced the same issue and used a utility class with a static method as a workaround for providing the authorization header.
Would be really cool if a fix for this came along so that we could use the default method instead :-)

Thanks for the update @michalszynkiewicz.

If I may propose adding some Javadoc / comments to your Quarkus commit as it's not very evident what is generated and how it works (I mean of course one can figure it out looking at the generated bytecode, but I always find it very useful to have an example of the generated bytecode in Javadoc when reading such code).

good point @geoand!
I still have some clean-up to do, will add some documentation then

Thanks @michalszynkiewicz!

Don't hesitate to ask for a review from me when things are in place :)

Was this page helpful?
0 / 5 - 0 ratings