Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Build failure due to NullPointerException, related to @Parameter(ref = ...) processing #26848

Closed
cowtowncoder opened this issue Jul 20, 2022 · 9 comments · Fixed by #27206
Closed
Labels
area/openapi kind/bug Something isn't working
Milestone

Comments

@cowtowncoder
Copy link

cowtowncoder commented Jul 20, 2022

Describe the bug

When trying to build project with ./mvn clean verify (etc), with certain endpoint definitions I get following stack trace (first part, full is longer):

[ERROR] Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 3.53 s <<< FAILURE! - in io.stargate.web.GreetingResourceTest
[ERROR] io.stargate.web.GreetingResourceTest.testHelloEndpoint  Time elapsed: 0.006 s  <<< ERROR!
java.lang.RuntimeException: 
java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.smallrye.openapi.deployment.SmallRyeOpenApiProcessor#build threw an exception: java.lang.NullPointerException: Cannot invoke "org.eclipse.microprofile.openapi.models.parameters.Parameter$In.equals(Object)" because the return value of "org.eclipse.microprofile.openapi.models.parameters.Parameter.getIn()" is null
	at io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner.isPathParameter(AnnotationScanner.java:944)
	at io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner.lambda$getRequestBodyParameterClassType$7(AnnotationScanner.java:930)
	at java.base/java.util.stream.IntPipeline$10$1.accept(IntPipeline.java:392)
	at java.base/java.util.stream.IntPipeline$10$1.accept(IntPipeline.java:393)
	at java.base/java.util.stream.Streams$RangeIntSpliterator.tryAdvance(Streams.java:82)
	at java.base/java.util.stream.IntPipeline.forEachWithCancel(IntPipeline.java:163)
	at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:527)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:513)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.FindOps$FindOp.evaluateSequential(FindOps.java:150)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.findFirst(ReferencePipeline.java:647)
	at io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner.getRequestBodyParameterClassType(AnnotationScanner.java:936)
	at io.smallrye.openapi.runtime.scanner.spi.AnnotationScanner.processRequestBody(AnnotationScanner.java:875)
	at io.smallrye.openapi.jaxrs.JaxRsAnnotationScanner.processResourceMethod(JaxRsAnnotationScanner.java:437)
	at io.smallrye.openapi.jaxrs.JaxRsAnnotationScanner.lambda$processResourceMethods$0(JaxRsAnnotationScanner.java:279)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
	at java.base/java.util.Iterator.forEachRemaining(Iterator.java:133)
	at java.base/java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1845)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
	at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
	at io.smallrye.openapi.jaxrs.JaxRsAnnotationScanner.processResourceMethods(JaxRsAnnotationScanner.java:277)
	at io.smallrye.openapi.jaxrs.JaxRsAnnotationScanner.processResourceClass(JaxRsAnnotationScanner.java:247)
	at io.smallrye.openapi.jaxrs.JaxRsAnnotationScanner.processResourceClasses(JaxRsAnnotationScanner.java:222)
	at io.smallrye.openapi.jaxrs.JaxRsAnnotationScanner.scan(JaxRsAnnotationScanner.java:154)
	at io.smallrye.openapi.runtime.scanner.OpenApiAnnotationScanner.scan(OpenApiAnnotationScanner.java:127)
	at io.quarkus.smallrye.openapi.deployment.SmallRyeOpenApiProcessor.generateAnnotationModel(SmallRyeOpenApiProcessor.java:847)
	at io.quarkus.smallrye.openapi.deployment.SmallRyeOpenApiProcessor.build(SmallRyeOpenApiProcessor.java:706)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at io.quarkus.deployment.ExtensionLoader$3.execute(ExtensionLoader.java:944)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
	at org.jboss.threads.ContextHandler$1.runWith(ContextHandler.java:18)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
	at java.base/java.lang.Thread.run(Thread.java:833)

Issue seems to be triggered with endpoint definition like so:

@Operation(...)
@APIResponses(...)
public Response getRowWithWhere(
   // possibly some other parameters, but this is the problem one:
   @Parameter(name = "raw", ref = RestOpenApiConstants.Parameters.RAW)
   // Note: if this was added, problem avoided
   // @QueryParam("raw")
          final boolean raw

wherein parameter in question uses a (valid) reference but does not have matching @QueryParam (or similar).
In my particular case annotations are defined in an interface, implemented by actual sub-class, if that matters. Parameter definition reference is defined in "application" class which is found.

It also looks like failure only occurs when quarkus-resteasy-reactive is used as the dependency (earlier had trouble to reproduce with "classic").

Expected behavior

Build should either work or indicate the underlying problem (inability to figure out type of parameter for OpenAPI?); but would not fail with NPE.

Actual behavior

Build fails as a side effect of an internal NullPointerException being thrown.
See the stack trace in the description.

I will also try to isolate the failure case in a smaller project, if possible. The full project won't be possible to include due to its size (although theoretically maybe I could create a branch as this is for an OSS project, not closed).

How to Reproduce?

Here's a project for reproduction

https://github.com/tatu-at-datastax/quarkus-demo

where simple ./mvnw clean verify should show failure.

Output of uname -a or ver

Darwin Kernel Version 20.6.0

Output of java -version

openjdk version "17.0.2" 2022-01-18

GraalVM version (if different from Java)

No response

Quarkus version or git rev

2.10.3.Final

Build tool (ie. output of mvnw --version or gradlew --version)

Maven 3.8.4

Additional information

No response

@cowtowncoder cowtowncoder added the kind/bug Something isn't working label Jul 20, 2022
@cowtowncoder cowtowncoder changed the title Build failure due to NullPointerException, related to @Parameter processing Build failure due to NullPointerException, related to @Parameter(ref = ...) processing Jul 20, 2022
@quarkus-bot
Copy link

quarkus-bot bot commented Jul 20, 2022

@cowtowncoder
Copy link
Author

Have not been able to reproduce with the sample project, but unfortunately quite easy to get it to resurface accidentally on the bigger project, when replacing in-line @Parameter definitions with references and (accidentally) removing @QueryParam (due to oddities of code formatting, IDE editor etc).
But at least now I know what to look for when I get the build crash.

@cowtowncoder
Copy link
Author

Yay! Was able to make reproduction work: for some reason specific RESTeasy implementation is needed. Now sample project triggers the failure I see locally on the bigger project.

@phillip-kruger
Copy link
Member

phillip-kruger commented Jul 21, 2022

Ok, so firstly, we definitely need a null check and give a better message. That is code in MicroProfile, so not something we can fix and release, it needs to go through the process. I'll start that.

The issue here is that we do not know where to assume the parameter is. There is no in parameter in @Parameter and there is no JAX-RS or Quarkus annotation on the field that we can use to know where this is. So currently in your code the raw field is actually the RequestBody (from a JAX-RS point of view, because it does not have any **Param JAX-RS annotation). So you are annotating a JAX-RS RequestBody with an OpenAPI Parameter. (we should be able to provide a better error message here)

  1. If your intention is to make this a parameter, you need to either provide the in (OpenAPI @Parameter) as one of COOKIE, HEADER, PATH or QUERY or if you omit the in we will try and get this of the JAX-RS annotation (CookieParam, HeaderParam, PathParam, FormParam or QueryParam) that you will have to use.

OR

  1. If your intention is to make this a RequestBody you can use the @RequestBody(name = "raw", ref = Constants.Parameters.RAW) from org.eclipse.microprofile.openapi.annotations.parameters.RequestBody rather than @Parameter

Hope this helps. Thanks for the bug, I'll see if we can build a check into SmallRye OpenAPI to provide a better error message. I'll probably move this issue to SmallRye if you are OK with the above.

@cowtowncoder
Copy link
Author

@phillip-kruger I am not saying that the second annotation shouldn't be needed (if it could be avoided, nice, but I can see why this may not be practical). My only real concern is just the unhelpful exception failure; as long as that is avoided and user can be given something indicating the problem that's perfectly fine.

So this is caused by my missing one @PathParam, and then being really confused as to what is going on, due to lengthy stack traces from internal code that wasn't expecting missing information.
This was during conversion of a DropWizard application, not building something from scratch.

And yes, moving the issue wherever it should be is fine. I encountered it via Quarkus so I filed it here but if and when it belongs to SmallRye (OpenApi handling) definitely transfer/recreate it there.

Thank you for a very quick follow-up!

@phillip-kruger
Copy link
Member

Yes agree we should do better with the error message. We should be able to do this in SmallRye so I'll move this there. Closing here. Thanks again :)

@cowtowncoder
Copy link
Author

Oh. One minor other thing: I think in parameter is actually defined in referenced-to Parameter, although not directly in referencing @Parameter itself. Not sure if that is possible to resolve or not (I can see how following a chain might be tricky).

But I can check the situation again once the underlying root cause is resolved.

Thank you once again!

@phillip-kruger
Copy link
Member

I think in parameter is actually defined in referenced-to Parameter, although not directly in referencing @parameter itself

I do not follow ?

@cowtowncoder
Copy link
Author

Definition that was referenced (ref=....) from @Parameter did indeed contain:

@OpenAPIDefinition(
 ...
                // reusable parameters
                parameters = {
                        @Parameter(
                                in = ParameterIn.QUERY,
                                name = Constants.Parameters.RAW,
                                description = "Whether to 'unwrap' results object (omit wrapper)",
                                schema = @Schema(implementation = boolean.class)),
                }
)
public class GreetingApp { }

and reference to it looks like:

           @Parameter(name = "raw", ref = Constants.Parameters.RAW)
            final boolean raw,

so it seems to me that information is at least theoretically available.
But I do not know if this should be enough or not; if @QueryParam is needed or in property of direct @Parameter annotation.

I hope this helps -- the sample repo has the pertinent example.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/openapi kind/bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants