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:
/intempus/createreport endpoint.Configuration
N/A
Screenshots
N/A
Environment (please complete the following information):
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
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)
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
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
IntempusAPIServiceby 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 :-)
Currently waiting on https://github.com/resteasy/Resteasy/pull/2476
When it's merged and released, this code will get us what we need: https://github.com/michalszynkiewicz/quarkus/commit/89fa8e9d12823b5765bd2819706dd502002b090f
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 :)
Most helpful comment
:+1:
I'll reopen the issue, since this is an actual problem we need to solve