micronaut-openapi
micronaut-openapi copied to clipboard
NPE when generating OpenAPI schema from Groovy classes
When trying to generate the OpenAPI schema for a Groovy based project the Gradle build stops due the following exception:
aused by: java.lang.NullPointerException
at io.micronaut.ast.groovy.visitor.GroovyClassElement.getBeanProperties(GroovyClassElement.java:259)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.populateSchemaProperties(AbstractOpenApiVisitor.java:1280)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.getSchemaDefinition(AbstractOpenApiVisitor.java:1103)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.resolveSchema(AbstractOpenApiVisitor.java:653)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.processPropertyElements(AbstractOpenApiVisitor.java:1301)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.populateSchemaProperties(AbstractOpenApiVisitor.java:1281)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.getSchemaDefinition(AbstractOpenApiVisitor.java:1103)
at io.micronaut.openapi.visitor.AbstractOpenApiVisitor.resolveSchema(AbstractOpenApiVisitor.java:662)
at io.micronaut.openapi.visitor.AbstractOpenApiEndpointVisitor.lambda$buildContent$15(AbstractOpenApiEndpointVisitor.java:878)
at io.micronaut.openapi.visitor.AbstractOpenApiEndpointVisitor.buildContent(AbstractOpenApiEndpointVisitor.java:876)
at io.micronaut.openapi.visitor.AbstractOpenApiEndpointVisitor.processBody(AbstractOpenApiEndpointVisitor.java:580)
at io.micronaut.openapi.visitor.AbstractOpenApiEndpointVisitor.processParameter(AbstractOpenApiEndpointVisitor.java:329)
at io.micronaut.openapi.visitor.AbstractOpenApiEndpointVisitor.processParameters(AbstractOpenApiEndpointVisitor.java:307)
at io.micronaut.openapi.visitor.AbstractOpenApiEndpointVisitor.visitMethod(AbstractOpenApiEndpointVisitor.java:283)
at io.micronaut.openapi.visitor.OpenApiControllerVisitor.visitMethod(OpenApiControllerVisitor.java:49)
at io.micronaut.ast.groovy.visitor.LoadedVisitor.visit(LoadedVisitor.groovy:148)
Checking the corresponding line it seems to be protected against null access
https://github.com/micronaut-projects/micronaut-core/blob/e9378a1adf1873bbb30889b74a5498c6cab89eca/inject-groovy/src/main/groovy/io/micronaut/ast/groovy/visitor/GroovyClassElement.java#L259
Environment Information
- Operating System: MacOS/Linux
- Micronaut Version: micronaut-core: 1.3.7, micronaut-openapi: 1.5.2
- JDK Version: Java 8
Example Application
Unfortunately, the issue is quite difficult to isolate since it happens quite large codebase.
I've been able to reproduce this issue. Apparently it happens in a Groovy multi-project when:
- A project defines the classes to be used as part of requests and responses (let's call them domain classes).
- Another project adds the former as a dependency and defines the controller classes to generate the corresponding API docs.
- One of the domain classes has a field annotated with a "nested" annotation (an annotation that takes other annotations in its values), for example:
@JsonSubTypes([@JsonSubTypes.Type(value = Cat, name = "cat")]).
This sample project reproduces the issue: https://github.com/tcrespog/mn-multi-openapi.
- The
animal-kingdomproject contains the domain classes and thepet-storeproject adds it as a dependency. - The
Petclass contains a@JsonSubtypesannotation with@JsonSubTypes.Typevalues. - The
PetControllerclass has an endpoint involvingPet. - The API docs generation fails when compiling
pet-store. It doesn't matter whether the values of@JsonSubTypes.Typeare classes ofanimal-kingdomor others such asObject, if the annotation@JsonSubTypes.Typeis present, then the NPE happens. - The error doesn't happen if the controller is written in Java, as seen in the branches
java(projects written in Java) andmixed(domain written in Groovy and app written in Java). In addition, it doesn't happen if domain and controller are located in the same project.
- Micronaut version: 2.1.4
- Micronaut OpenAPI version: 2.1.1
This seems like a bug in the Groovy AST processing. The problematic item is:
@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "kind")
@JsonSubTypes([
@JsonSubTypes.Type(value = Dog, name = "dog"),
@JsonSubTypes.Type(value = Cat, name = "cat")
])
Animal animal
When getting the annotations for the animal node, the second AnnotationNode looks like:

Note how the expressions are all null.
@paulk-asert is this enough information for you to understand where the issue could be?
I can replicate getting the NPE:
...
Caused by: java.lang.NullPointerException
at org.codehaus.groovy.ast.GroovyCodeVisitor.lambda$visitListOfExpressions$0(GroovyCodeVisitor.java:202)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.codehaus.groovy.ast.GroovyCodeVisitor.visitListOfExpressions(GroovyCodeVisitor.java:202)
...
Where is the breakpoint for above screenshot? There is a guard that the list is not null but the NPE would be expected if the list contained a null.
I think it was at GroovyCodeVisitor, don't remember exactly but can try again.
Notice also that AnnotationNode#toString() throws an exception too.
Second one could just be intellij trying to display the null
The breakpoint is at ClassCodeVisitorSupport
The NPE is actually thrown at GroovyCodeVisitor. The List<? extends Expression> list is [null, null].
Hope this helps.
@paulk-asert has this been reported in the Groovy Jira? I couldn't find anything.
I don't believe so. Please raise a Jira.
https://issues.apache.org/jira/browse/GROOVY-9871