Open jeffawx opened 1 year ago
/cc @evanchooly (kotlin), @geoand (kotlin)
This doesn't work because an annotation processor needs to generate metadata for Config classes. Of course that annotation processor only works for Java source.
This is issue is very similar to #35110.
Just as a note, you could overcome this issue by using a @ConfigMapping
class for your configuration instead of a @ConfigRoot
.
Furthermore this also applies
In my example the annotation processor is already applied via kapt
plugin, so that there is no problem to pick up the build steps, only problem is can't find config class for QuarkusUnitTest
kapt("io.quarkus:quarkus-extension-processor:3.2.2.Final")
Just to clarify only QuarkusUnitTest
doesn't work, the actual application itself works fine, just can't test via QuarkusUnitTest
.
This doesn't work because an annotation processor needs to generate metadata for Config classes. Of course that annotation processor only works for Java source.
This is issue is very similar to #35110.
In that case, I propose using @ConfigMapping
instead
In that case, I propose using
@ConfigMapping
instead
I switched to use @ConfigMapping
, now a bit more Kotlin friendly (no more val/var limitation and @JvmField
), but I get same result as previously, i.e. extension still works properly in real application, but QuarkusUnitTest
can't find config which can be fixed by manually put in META-INF/quarkus-config-roots.list
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
@ConfigMapping(prefix = "quarkus.my.ext")
interface HelloConfig {
/**
* test
*/
@WithDefault("Hello World")
fun text(): String
}
btw: My config class is root level, has to put @ConfigRoot
or else doesn't work, I see the same pattern in Quarkus code base, e.g. https://github.com/quarkusio/quarkus/blob/main/extensions/resteasy-reactive/quarkus-resteasy-reactive/runtime/src/main/java/io/quarkus/resteasy/reactive/server/runtime/ResteasyReactiveServerRuntimeConfig.java
Can you update your sample with ConfigMapping?
Thanks
cc @radcortez
Can you update your sample with ConfigMapping?
Yep pushed the changes here: https://github.com/jeffawx/quarkus-ext-test
if you run ./gradlew test
it should pass test, but if you remove META-INF/quarkus-config-roots.list
will fail
Also confirmed this test extension works properly in real application.
For what I've seen, the processor is not being executed at all. It requires:
kapt {
keepJavacAnnotationProcessors = true
}
Now the processor executes as expected, but it does not generate the files in the standard folders. These are generated in build/tmp/kapt3/classes/
. Check https://youtrack.jetbrains.com/issue/KT-22263.
I've tried to add the sources on the runtime project, with
sourceSets {
main {
resources {
srcDir("build/tmp/kapt3/classes/main")
}
}
}
The expected config file is now in the JAR, but it seems it is not visible in the deployment project. It seems that additional source sets are not added in the module dependency. I'm not a big expert in Gradle, so maybe someone else can help here?
source sets are not added in the module dependency.
Thanks for the feedback!
Sorry previous example has some missing piece in build script causing it can't work in actual application.
Now I updated the example with the demo application that uses this extension, also added a README file for easy reproducing the issue: https://github.com/jeffawx/quarkus-ext-test
./gradlew test fails: No config found for interface demo.HelloConfig, not expected!!
But if publish the jar by ./gradlew publishToMavenLocal -x test, go to test-app folder, run
./gradlew quarkuDev
followed by curl localhost:8080/sayHello proves the actual application is working.
in deployment module, rename META-INF/quarkus-config-roots.list_test to META-INF/quarkus-config-roots.list, then ./gradlew test, works!
FYI: it seems to be some ClassLoader related problem for QuarkusUnitTest
(for kapt generated resources)
Because if I wrote a simple test method adding the lines below it can find and print my config file:
val file = ServiceUtil.classNamesNamedIn(
Thread.currentThread().contextClassLoader,
"META-INF/quarkus-config-roots.list"
)
println(file)
Under the covers Quarkus uses ServiceUtil.classNamesNamedIn
to find the resources so the only difference is classloader.
I have also played with setAllowTestClassOutsideDeployment
on QuarkusUnitTest
but no luck.
It works when running the test-app
because you are referencing the resulting jars of the extension, which contains the quarkus-config-root.list
.
The issue here is kapt
is generating the output in a different folder from build/classes
or build/resources
as you would expect. For a Gradle dependency project these are the expected directories to find each project module output. There should be a way to change the output path.
Another easier alternative is copy the generated file to the main build output. Adding this to the runtime
module, the test passes as expected:
tasks.register<Copy>("copyConfigRoots") {
from(layout.buildDirectory.file("tmp/kapt3/classes/main/META-INF/quarkus-config-roots.list"))
into(layout.buildDirectory.dir("resources/main/META-INF/"))
}
tasks.named("build") {
finalizedBy("copyConfigRoots")
}
Thanks @radcortez, I tried the snippet above it works!
I also found out the test code below can find config because kotlin plugin appended the directory into classpath.
In System.getProperty("java.class.path")
result I can see tmp/kapt3/classes/main
Question: is it possible in future to make QuarkusUnitTest
respect java.class.path
so that no need to do special configuration for kotlin?
@Test
fun test() {
println(System.getProperty("java.class.path"))
val names = ServiceUtil.classNamesNamedIn(
Thread.currentThread().contextClassLoader,
"META-INF/quarkus-config-roots.list"
)
println(names)
}
@aloubyansky I think this is needs to be handled in https://github.com/quarkusio/quarkus/blob/main/devtools/gradle/gradle-model/src/main/java/io/quarkus/gradle/tooling/GradleApplicationModelBuilder.java
Describe the bug
When I write a Quarkus extension using Kotlin, I could't get
QuarkusUnitTest
mechanism working properly if I have a config class defined inruntime
module and used in a build step indeployment
module.If I changed to Java, works fine.
Current workaround is manually creating a file
META-INF/quarkus-config-roots.list
and list the config class there, but as described in the following link this file is not supposed to be edited manually: https://quarkus.io/guides/extension-metadata#quarkus-config-rootsApart from this issue, defining config class using Kotlin isn't nice experience, e.g. can't use data class and Kotlin native types, has to put @JvmField on each field and make sure the field is declared using
var
instead ofval
, etc.Expected behavior
Should not throw error
Actual behavior
Can't find config class, throw exception:
How to Reproduce?
Example project demo this issue:
https://github.com/jeffawx/quarkus-ext-test
If this file is removed: https://github.com/jeffawx/quarkus-ext-test/blob/main/deployment/src/main/resources/META-INF/quarkus-config-roots.list
build will fail:
./gradlew clean build
Output of
uname -a
orver
No response
Output of
java -version
openjdk 17.0.7 2023-04-18 OpenJDK Runtime Environment Temurin-17.0.7+7 (build 17.0.7+7) OpenJDK 64-Bit Server VM Temurin-17.0.7+7 (build 17.0.7+7, mixed mode)
GraalVM version (if different from Java)
No response
Quarkus version or git rev
3.2.2.Final
Build tool (ie. output of
mvnw --version
orgradlew --version
)Gradle 8.0
Additional information
No response