1

I'm currently building a microservice-based on Helidon Microprofile following guides and tutorials from Oracle themselves, but I've run into a problem related to the 'Automatic OpenAPI specification generator' when using Annotations.

My POM consists of an MP bundle and integrations to make it work with Hibernate-provided JPA.

Even after setting up all the annotations on my Resource, it doesn't generate an updated specification.

POM

 <dependencies>
    <dependency>
        <groupId>io.helidon.microprofile.bundles</groupId>
        <artifactId>helidon-microprofile</artifactId>
        <version>1.4.0</version>
    </dependency>

    <dependency>
        <groupId>org.jboss</groupId>
        <artifactId>jandex</artifactId>
        <version>2.1.1.Final</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>io.helidon.integrations.cdi</groupId>
        <artifactId>helidon-integrations-cdi-datasource-hikaricp</artifactId>
        <version>1.4.0</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>io.helidon.integrations.cdi</groupId>
        <artifactId>helidon-integrations-cdi-jta-weld</artifactId>
        <version>1.4.0</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>io.helidon.integrations.cdi</groupId>
        <artifactId>helidon-integrations-cdi-hibernate</artifactId>
        <version>1.4.0</version>
    </dependency>

    <dependency>
        <groupId>javax.transaction</groupId>
        <artifactId>javax.transaction-api</artifactId>
        <version>1.3</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>jakarta.persistence</groupId>
        <artifactId>jakarta.persistence-api</artifactId>
        <version>2.2.3</version>
        <scope>provided</scope>
    </dependency>

    <dependency>
        <groupId>org.mariadb.jdbc</groupId>
        <artifactId>mariadb-java-client</artifactId>
        <version>2.5.2</version>
    </dependency>

    <dependency>
        <groupId>com.googlecode.json-simple</groupId>
        <artifactId>json-simple</artifactId>
        <version>1.1.1</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>2.29.1</version>
        <scope>runtime</scope>
    </dependency>

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.8.3</version>
    </dependency>

    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>jwks-rsa</artifactId>
        <version>0.9.0</version>
    </dependency>

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-api</artifactId>
        <version>5.5.2</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.junit.jupiter</groupId>
        <artifactId>junit-jupiter-engine</artifactId>
        <version>5.5.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

I'm only using Annotations specified in the guides and @OpenAPIDefinition for defining things like Title and Licence.

RESOURCE

    @OpenAPIDefinition(
        info = @Info(
                title = "Newsletter Microservice",
                version = "1.0", description = "Microservice in charge of handling newsletter",
                license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0"),
                contact = @Contact(name = "Email", url = "mailto:email")
        ),
        tags = {
                @Tag(name = "public"), @Tag(name = "private")
        }
)

@Path("/newsletter")
@RequestScoped
public class NewsletterClientResource {

LOG

Connected to the target VM, address: '127.0.0.1:0', transport: 'socket'
Java HotSpot(TM) 64-Bit Server VM warning: Sharing is only supported for boot loader classes because bootstrap classpath has been appended
2019.12.02 20:01:19 INFO org.jboss.weld.Version Thread[main,5,main]: WELD-000900: 3.1.1 (Final)
2019.12.02 20:01:20 INFO org.jboss.weld.Bootstrap Thread[main,5,main]: WELD-ENV-000020: Using jandex for bean discovery
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.jboss.weld.util.bytecode.ClassFileUtils$1 (file:/C:/Users/Brenno%20Fagundes/.m2/repository/org/jboss/weld/weld-core-impl/3.1.1.Final/weld-core-impl-3.1.1.Final.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)
WARNING: Please consider reporting this to the maintainers of org.jboss.weld.util.bytecode.ClassFileUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
2019.12.02 20:01:21 INFO org.jboss.weld.Event Thread[main,5,main]: WELD-000411: Observer method [BackedAnnotatedMethod] public org.glassfish.jersey.ext.cdi1x.internal.ProcessAllAnnotatedTypes.processAnnotatedType(@Observes ProcessAnnotatedType<?>, BeanManager) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds.
2019.12.02 20:01:21 INFO org.jboss.weld.Event Thread[main,5,main]: WELD-000411: Observer method [BackedAnnotatedMethod] private io.helidon.microprofile.openapi.IndexBuilder.processAnnotatedType(@Observes ProcessAnnotatedType<X>) receives events for all annotated types. Consider restricting events using @WithAnnotations or a generic type with bounds.
2019.12.02 20:01:22 INFO org.jboss.weld.Bootstrap Thread[main,5,main]: WELD-ENV-002003: Weld SE container 404f642b-892f-4676-960e-8df848aee3a3 initialized
2019.12.02 20:01:22 INFO io.helidon.microprofile.security.SecurityMpService Thread[main,5,main]: Security extension for microprofile is enabled, yet security configuration is missing from config (requires providers configuration at key security.providers). Security will not have any valid provider.
2019.12.02 20:01:22 INFO io.smallrye.openapi.api.OpenApiDocument Thread[main,5,main]: OpenAPI document initialized: io.smallrye.openapi.api.models.OpenAPIImpl@7793ad58
2019.12.02 20:01:23 INFO io.helidon.webserver.NettyWebServer Thread[main,5,main]: Version: 1.4.0
2019.12.02 20:01:24 INFO io.helidon.webserver.NettyWebServer Thread[nioEventLoopGroup-2-1,10,main]: Channel '@default' started: [id: 0x4e1f119b, L:/0:0:0:0:0:0:0:0:7200]
2019.12.02 20:01:24 INFO io.helidon.microprofile.server.ServerImpl Thread[nioEventLoopGroup-2-1,10,main]: Server initialized on http://localhost:7200 (and all other host addresses) in 5254 milliseconds.

BUMP, also, the generation works using custom filters and models, a static definition in META-INF also works. Currently using JDK 13.


EDIT: this is my Application class after Tim Quinn's suggested modifications.

APPLICATION CLASS

@ApplicationScoped
@ApplicationPath("/")
@OpenAPIDefinition(
        info = @Info(
                title = "Newsletter Microservice",
                version = "1.0", description = "Microservice in charge of handling newsletter",
                license = @License(name = "Apache 2.0", url = "https://www.apache.org/licenses/LICENSE-2.0"),
                contact = @Contact(name = "Email", url = "mailto:john.doe@gmail.com")
        ),
        tags = {
                @Tag(name = "public"), @Tag(name = "private")
        }
)
public class NewsletterApplication extends Application {
    @Override
    public Set<Class<?>> getClasses(){
        HashSet<Class<?>> classes = new HashSet<Class<?>>();
        classes.add(NewsletterClientResource.class);
        return classes;
    }
}

GENERATED FILE

---
openapi: 3.0.1
info:
  title: Generated API
  version: "1.0"
paths: {}
Brenno Fagundes
  • 461
  • 2
  • 5
  • 11

2 Answers2

1

Brenno,

You didn't show your updated source code, but I'm assuming you added the @OpenAPIDefinition annotation to the quickstart GreetResource class, is that right?

That annotation's attributes describe the whole application, not a subset of its resources, so try moving the annotation to the GreetApplication class instead:

@ApplicationScoped
@ApplicationPath("/")
@OpenAPIDefinition(info = @Info(title = "QuickStart API", version = "1.1"))

public class GreetApplication extends Application {...}

That should work. I just tried this on the generated Helidon MP quickstart example source, rebuilt, and the server returned the expected results.

Here is the diff for the change I made:

diff --git a/examples/quickstarts/helidon-quickstart-mp/src/main/java/io/helidon/examples/quickstart/mp/GreetApplication.java b/examples/quickstarts/helidon-quickstart-mp/src/main/java/io/helidon/examples/quickstart/mp/GreetApplication.java
index fd140738..cca60da2 100644
--- a/examples/quickstarts/helidon-quickstart-mp/src/main/java/io/helidon/examples/quickstart/mp/GreetApplication.java
+++ b/examples/quickstarts/helidon-quickstart-mp/src/main/java/io/helidon/examples/quickstart/mp/GreetApplication.java
@@ -23,12 +23,15 @@ import javax.ws.rs.ApplicationPath;
 import javax.ws.rs.core.Application;

 import io.helidon.common.CollectionsHelper;
+import org.eclipse.microprofile.openapi.annotations.OpenAPIDefinition;
+import org.eclipse.microprofile.openapi.annotations.info.Info;

 /**
  * Simple Application that produces a greeting message.
  */
 @ApplicationScoped
 @ApplicationPath("/")
+@OpenAPIDefinition(info = @Info(title = "QuickStart API", version = "1.1"))
 public class GreetApplication extends Application {

     @Override

And here is the beginning of the updated OpenAPI document returned from the server:

---
openapi: 3.0.1
info:
  title: QuickStart API
  version: "1.1"
paths:

The title and version have changed, as expected.

Tim Quinn
  • 146
  • 4
  • I was defining the @OpenAPIDefinition in my Resource class instead of the Application class. I've changed the definition to the right place but still didn't work, also the log and my resource header were added to the question. – Brenno Fagundes Dec 02 '19 at 23:03
  • The code example you added to your post has the `@OpenAPIDefinition` on the resource class, not the application class where it should be. The annotation itself looks OK but if that is how your code is then it is still on the wrong class. As I mentioned, when I added the simple version of the annotation to the quickstart application class then it was processed correctly, so you should see the same results. – Tim Quinn Dec 04 '19 at 00:29
  • I've moved my annotation to the application class but no new results. – Brenno Fagundes Dec 14 '19 at 00:14
  • Can you share your source? It should work. I have updated my initial answer to include the diff for the change I made. Before the change, the OpenAPI output from `http://localhost:8080/openapi` is the default. After the change, the beginning of the output is as in the newly-added annotation. – Tim Quinn Dec 16 '19 at 16:27
  • Added the whole Application Class code to the original post + the generated file. – Brenno Fagundes Dec 16 '19 at 22:48
  • Thanks for adding the relevant part of the app class. I copied your `@OpenAPIDefinition` annotation into the Helidon MP quickstart application class and rebuilt and reran it, and the resulting OpenAPI document is as expected -- it has the information declared in the annotation, not the defaults. Given what you've posted it is hard to diagnose why it is not working in your application. Do you have more than one application class? – Tim Quinn Dec 17 '19 at 16:09
  • No, I have only one Application class. Maybe is related to my POM? – Brenno Fagundes Dec 18 '19 at 22:48
  • This is a puzzle. From the Helidon log message, the system is finding a Jandex index in your app (as opposed to building one on-demand when the OpenAPI subsystem starts). So I assume you are using the Jandex maven plug-in to create the index and include it in your app JAR. One suggestion would be to turn on DEBUG level logging for `io.smallrye.openapi.runtime.scanner.OpenApiAnnotationScanner`. (Helidon leverages some of the SmallRye implementation.) Among other things, that would display a message if and when the annotation scanning processes the `@OpenAPIDefinition`. – Tim Quinn Dec 19 '19 at 23:26
  • A second suggestion would be to comment out the Jandex plug-in usage in your `pom.xml` file. Helidon will automatically build a Jandex index in-memory at runtime if it cannot find a Jandex index in your app to load. – Tim Quinn Dec 19 '19 at 23:33
  • I'm debugging the application including OpenAPI's scanner. Somehow it's this.index.getAllKnownSubclasses isn't detecting my Application Class and keeps returning an empty collection. I've also removed the jandex plugin and its runtime dependency. – Brenno Fagundes Dec 20 '19 at 02:34
  • That being said, Helidon can find my Application class (by the power of beans.xml) but the scanner gets an empty FilteredIndexView and tries to find an Application.class – Brenno Fagundes Dec 20 '19 at 02:46
  • Given that you are using the debugger already, does the `delegate` index in `FilteredIndexView` contain an entry for your application class under `subclasses -> javax.ws.rs.core.Application -> value`? I assume not or the SmallRye code would be finding it. Do any of your classes appear anywhere in the index? – Tim Quinn Dec 20 '19 at 14:50
  • There wasn't any class `javax.ws.rs.core.application` on delegate's subclasses. Neither any of my other classes. – Brenno Fagundes Dec 20 '19 at 17:25
  • Actually, I didn't find any class related to `jax-rs` in the delegate. Even without the Jandex mvn plugin. – Brenno Fagundes Dec 20 '19 at 17:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/204624/discussion-between-brenno-fagundes-and-tim-quinn). – Brenno Fagundes Dec 20 '19 at 18:15
0

Case 1 : You are using jandex and your /openapi is not getting updated.

If you are using jandex, there is a high chance that your jandex.idx is not getting updated. You can do this by running mvn process-classes

Case 2 : You are not using jandex and when you hit /openapi you get somewhat blank response.

This seems to be an issue with Helidon. The dependencies helidon-integrations-cdi-jpa, helidon-integrations-cdi-jta, helidon-integrations-cdi-eclipselink etc... contains jandex.idx and Helidon now thinks that jandex is enabled and it will read only from those jandex files, skipping your resources. So for the time-being, you can include jandex plugin to solve the issue.

Sreehari B S
  • 48
  • 1
  • 7
  • 1
    Things are actually a bit more nuanced than this. A bug in the jandex plug-in prevents it from gathering information on classes from multiple JARs into the index. This issue explains in more detail: https://github.com/oracle/helidon/issues/2729. Not much Helidon can do about this. – Tim Quinn Apr 20 '21 at 15:58