quarkus: Unable to extract REST API into shared gradle module

Describe the bug

We have a Quarkus service that exposes a rest api and one that consumes the same rest api. Both are submodules of a multi-module gradle project and we use Kotlin.

Since we don’t want to write the DTOs and the rest api interface twice, we added another gradle module where we extracted the rest client interface and the dtos to. Both Quarkus services now have this library module as a dependency. In the service that provides the API, I have now implemented this interface to provide the implementation for the api.

The library code looks like this (also checkout the reproducer below):

@Path("/books")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
interface BookApi {
    @GET
    fun getBooks(): List<BookDto>
}

data class BookDto(val name: String)

and the server code looks like this

class BookResource : BooksApi {
    override fun getBooks(): List<BookDto> {
        return listOf(BookDto("Abc"))
    }
}

Unfortunately, when you start the server, no resource is loaded and no endpoint is available.

We also tried adding @ApplicationScoped to the BookResource. Then we get warnings:

2023-01-30 18:01:41,134 WARN  [io.qua.arc.pro.BeanArchives] (build-39) Failed to index com.arconsis.books.BooksApi: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: DEV@68567e20
2023-01-30 18:01:43,021 WARN  [io.qua.arc.ComponentsProvider] (Quarkus Main Thread) Unable to load removed bean type [com.arconsis.planconfig.resource.BookResource]: java.lang.NoClassDefFoundError: com/arconsis/books/BooksApi

So it seems the interface is somehow not included in the code. Also the endpoint is not available.

When we add the @Path("/books") annotation to the BooksResource, we get an error like this:

2023-01-30 18:10:12,035 INFO  [io.qua.oid.dep.dev.OidcDevConsoleProcessor] (build-17) OIDC Dev Console: discovering the provider metadata at http://localhost:13501/realms/master/.well-known/openid-configuration
2023-01-30 18:10:12,240 WARN  [io.qua.arc.pro.BeanArchives] (build-31) Failed to index com.arconsis.books.BooksApi: Class does not exist in ClassLoader QuarkusClassLoader:Deployment Class Loader: DEV@17a7f733
2023-01-30 18:10:12,272 INFO  [io.qua.arc.pro.IndexClassLookupUtils] (build-31) Class for name: com.arconsis.books.BooksApi was not found in Jandex index. Please ensure the class is part of the index.
2023-01-30 18:10:14,061 ERROR [io.qua.run.boo.StartupActionImpl] (Quarkus Main Thread) Error running Quarkus: java.lang.reflect.InvocationTargetException
    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.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:104)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.lang.ExceptionInInitializerError
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
    at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:70)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:44)
    at io.quarkus.runtime.Quarkus.run(Quarkus.java:124)
    at io.quarkus.runner.GeneratedMain.main(Unknown Source)
    ... 6 more
Caused by: java.lang.RuntimeException: Failed to start quarkus
    at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source)
    ... 15 more
Caused by: java.lang.NoClassDefFoundError: com/arconsis/books/BooksApi
    at java.base/java.lang.ClassLoader.defineClass1(Native Method)
    at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1012)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:497)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:457)
    at java.base/java.lang.Class.forName0(Native Method)
    at java.base/java.lang.Class.forName(Class.java:467)
    at com.arconsis.books.BookResource_Bean.<init>(Unknown Source)
    at io.quarkus.arc.setup.Default_ComponentsProvider.addBeans3(Unknown Source)
    at io.quarkus.arc.setup.Default_ComponentsProvider.getComponents(Unknown Source)
    at io.quarkus.arc.impl.ArcContainerImpl.<init>(ArcContainerImpl.java:117)
    at io.quarkus.arc.Arc.initialize(Arc.java:31)
    at io.quarkus.arc.runtime.ArcRecorder.initContainer(ArcRecorder.java:43)
    at io.quarkus.deployment.steps.ArcProcessor$generateResources844392269.deploy_0(Unknown Source)
    at io.quarkus.deployment.steps.ArcProcessor$generateResources844392269.deploy(Unknown Source)
    ... 16 more
Caused by: java.lang.ClassNotFoundException: com.arconsis.books.BooksApi
    at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
    at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
    at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:507)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:457)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:507)
    at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:457)
    ... 30 more

2023-01-30 18:10:14,064 INFO  [io.qua.dep.dev.IsolatedDevModeMain] (main) Attempting to start live reload endpoint to recover from previous Quarkus startup failure

Furthermore, if we create a client like the following:

@RegisterRestClient(configKey = "books-api")
interface BooksClient : BooksApi

it is also not loaded when the application starts.

Expected behavior

We should be able to extract the DTOs and API definiton to a different module to reuse it in resources and clients in multiple projects in order to not duplicate the code.

Actual behavior

The interface of the API seems to get lost and the setup is not working.

How to Reproduce?

Reproducer: 2023-01-31-gradle-multimodule.zip

  1. Download & Unpack the reproducer
  2. Start the server by running ./gradlew :server:quarkusDev
  3. The should be an endpoint http://localhost:8080/books. But it is not there. You can test and see the behavior described above.
  4. Start the client by running ./gradlew :client:quarkusDev
  5. You can test and see the behavior described above.

Output of uname -a or ver

No response

Output of java -version

kotlin 1.7.22

GraalVM version (if different from Java)

No response

Quarkus version or git rev

Tested 2.16.0 and 2.15.3

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

gradle

Additional information

This ticket is the result of this Zulip chat here: https://quarkusio.zulipchat.com/#narrow/stream/187030-users/topic/How.20to.20extract.20REST.20API.20into.20shared.20gradle.20module

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Comments: 16 (12 by maintainers)

Most upvoted comments

I will do my best and try to look into it quickly.

Not really any progress on my side. I definitely miss time for now. I will do my best!

Ah, I meant it properly locates the compileKotlin task in the client project, the dev mode still fails due to the issue in the library project.