Closed janek64 closed 9 months ago
/cc @DavideD (hibernate-reactive), @gavinking (hibernate-reactive), @geoand (kotlin)
I had a quick look and I think there might be something wrong in how the deployment dependencies are resolved in the Gradle plugin.
Because quarkus-agroal-deployment
is supposed to be excluded from quarkus-hibernate-reactive-deployment
-> quarkus-hibernate-orm-deployment
and it still comes.
I wonder if maybe exclusions are not properly taken into account as we don't do that very often.
The problem is that you don't have the runtime classes as the runtime classpath seems to be OK.
/cc @glefloch @aloubyansky @quarkusio/devtools
Also /cc @snazy
Not sure if this helps, but I tried excluding the problematic dependency via the following configuration:
configurations {
all*.exclude group: 'io.quarkus', module: 'quarkus-agroal-deployment'
}
This completely excludes quarkus-agroal-deployment
as it does not appear in ./gradlew dependencies
anymore. However, starting Quarkus will then produce the following error:
2024-02-06 12:14:41,679 ERROR [io.qua.run.boo.StartupActionImpl] (Quarkus Main Thread) Error running Quarkus: java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:118)
at java.base/java.lang.reflect.Method.invoke(Method.java:580)
at io.quarkus.runner.bootstrap.StartupActionImpl$1.run(StartupActionImpl.java:113)
at java.base/java.lang.Thread.run(Thread.java:1583)
Caused by: java.lang.ExceptionInInitializerError
at io.quarkus.runner.ApplicationImpl.<clinit>(Unknown Source)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized0(Native Method)
at java.base/jdk.internal.misc.Unsafe.ensureClassInitialized(Unsafe.java:1160)
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.ensureClassInitialized(MethodHandleAccessorFactory.java:300)
at java.base/jdk.internal.reflect.MethodHandleAccessorFactory.newConstructorAccessor(MethodHandleAccessorFactory.java:103)
at java.base/jdk.internal.reflect.ReflectionFactory.newConstructorAccessor(ReflectionFactory.java:200)
at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:549)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
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)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103)
... 3 more
Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig
at io.quarkus.runtime.configuration.AbstractConfigBuilder.withMapping(AbstractConfigBuilder.java:66)
at io.quarkus.runtime.generated.StaticInitConfigCustomizer.configBuilder(Unknown Source)
at io.smallrye.config.SmallRyeConfigBuilder.lambda$build$0(SmallRyeConfigBuilder.java:722)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:184)
at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
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.config.SmallRyeConfigBuilder.build(SmallRyeConfigBuilder.java:722)
at io.quarkus.runtime.generated.Config.<clinit>(Unknown Source)
... 17 more
Caused by: java.lang.ClassNotFoundException: io.quarkus.agroal.runtime.DataSourcesJdbcBuildTimeConfig
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:526)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:518)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:518)
at io.quarkus.bootstrap.classloading.QuarkusClassLoader.loadClass(QuarkusClassLoader.java:468)
at io.quarkus.runtime.configuration.AbstractConfigBuilder.withMapping(AbstractConfigBuilder.java:64)
... 29 more
Could it be related to #37938? /cc @tomas1885
Same problem here.
@janek64 yeah, that's probably not so simple, unfortunately.
@vemilyus could you have a look at this one? There's a good chance it's related to your changes. Thanks!
On it!
@vemilyus my comment here might be useful in understanding what's going on: https://github.com/quarkusio/quarkus/issues/38533#issuecomment-1921653601
It's a wild guess though :).
@gsmet
quarkus-hibernate-orm
pulls in quarkus-agroal
which leads to quarkus-agroal-deployment
being picked up by the Gradle plugin.
However, none of the reactive DB extensions specifies an exclusion for quarkus-agroal
in their quarkus-extension.properties
file.
As far as I can see excluded artifacts are handled in the ApplicationModelBuilder class.
So the only thing we would need to do would be to add an exclusion of quarkus-agroal
to quarkus-reactive-datasource
, right?
Please let me know if I'm right or completely wrong :)
Edit: I also checked quarkus-hibernate-reactive
and there are exclusion for multiple javax.persistence:...
artifacts, but not for quarkus-agroal
.
This extension is a bit specific (and hackyish) because we rely on parts of the Hibernate ORM extension. There's a plan to actually move the common parts to a separate extension but we never got to it.
At the moment, we rely on these (not pretty) exclusions:
As far as I understand those are exclusions in the Maven project model, not in the Quarkus extension model. Shouldn't artifacts excluded by an extension be specified under excluded-artifacts
in quarkus-extension.properties
?
quarkus-agroal
gets picked up by the Gradle plugin because it's an explicit extension dependency of quarkus-hibernate-orm
(in quarkus-extension.yaml
.
I must admit, I'm a bit confused here... How should the Gradle plugin determine based on a few excludes in a pom.xml
which deployment artifacts to add to the deployment classpath? What if the pom.xml
isn't part of the artifact?
Gradle itself can deal with exclusion
s in a pom. It's probably some code in the Quarkus Gradle plugin? Or, more likely, something that gets lost from Gradle to dev-mode.
The Gradle plugin doesn't know about the pom. It only reads the quarkus-extension.yaml
file and looks at previously configured dependencies (e.g. from a pom) when building the deployment classpath. So the pom might exclude an artifact, but due to the extension's presence in quarkus-extension.yaml
the plugin pulls it back in.
Also, doesn't an exclusion like this
<dependencies>
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-hibernate-orm-deployment</artifactId>
<exclusions>
<exclusion>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-narayana-jta-deployment</artifactId>
</exclusion>
<exclusion>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-narayana-jta</artifactId>
</exclusion>
<exclusion>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal</artifactId>
</exclusion>
<exclusion>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-agroal-deployment</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
only affect the classpath of the immediate module, and not any other modules? So someone else is perfectly able to pull in those excluded dependencies.
One of the main changes I made to the Gradle plugin is to recursively pull in all extensions' deployment modules to also get those where one extension or module may depend on another extension. To solve those transitive dependencies.
I added these exclusions to quarkus-reactive-datasource
:
<excludedArtifacts>
<excludedArtifact>io.quarkus:quarkus-agroal</excludedArtifact>
<excludedArtifact>io.quarkus:quarkus-agroal-deployment</excludedArtifact>
<excludedArtifact>io.quarkus:quarkus-narayana-jta</excludedArtifact>
<excludedArtifact>io.quarkus:quarkus-narayana-jta-deployment</excludedArtifact>
<excludedArtifact>io.smallrye:smallrye-context-propagation-jta</excludedArtifact>
</excludedArtifacts>
This made the reproducer start without issue.
However, this breaks all extensions relying on JDBC datasources (e.g. Flyway or Quartz), so that doesn't really help us either, unless we intend to break JDBC support when using reactive datasources.
Another solution would be to NOT treat being unable to find a JDBC driver when using a reactive datasource as a hard failure, just as a warning. In that case we simply wouldn't create an AgroalDataSource
in AgroalProcessor
. This would enable us to use a reactive driver without having to block us from using JDBC on the side. For any JDBC based extension we'd have to configure a non-reactive datasource anyway so we could use that to determine the appropriate response.
@gsmet What do you think?
@vemilyus all the dependency related config, such as exclusions, is still in the POM, which Gradle knows how to handle, generally.
quarkus-extension.properties
only points to the corresponding deployment artifact of the extension.
quarkus-extension.yaml
should not be read during the build process at all.
@aloubyansky I wrote to you on Zulip about some topics where I need to increase my understanding, which (I think) has no place in this issue.
I'm not exactly sure how things are resolved but basically, we need to be careful to handle the exclusions when considering the extensions. I'm not familiar enough with Gradle to help here.
After talking to @aloubyansky a bit I realized that's what was missing from the Gradle plugin. Will try to find out how we can best handle those exclusions.
@aloubyansky @gsmet @janek64 One bug fix coming up :)
The reproducer of this issue is a new integration test case for the Gradle plugin ;)
dependencies { implementation(enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")) implementation("io.quarkus:quarkus-grpc") implementation("io.quarkus:quarkus-jdbc-postgresql") implementation("io.quarkus:quarkus-reactive-pg-client") implementation("io.quarkus:quarkus-hibernate-reactive-panache-kotlin") implementation("io.quarkus:quarkus-resteasy-reactive-jackson") implementation("io.quarkus:quarkus-kotlin") implementation("io.quarkus:quarkus-smallrye-jwt") implementation("io.quarkus:quarkus-smallrye-graphql") implementation("io.quarkus:quarkus-smallrye-health") implementation("io.quarkus:quarkus-smallrye-stork") implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("io.quarkus:quarkus-resteasy-reactive") implementation("io.quarkus:quarkus-arc") implementation("io.quarkiverse.hibernatetypes:quarkus-hibernate-types:2.1.0") implementation("io.quarkiverse.config:quarkus-config-consul") implementation("io.smallrye.stork:stork-service-discovery-consul") implementation("io.smallrye.reactive:smallrye-mutiny-vertx-consul-client") testImplementation("io.quarkus:quarkus-junit5") testImplementation("io.rest-assured:rest-assured") }
this implementations worked for my project with quarkus 3.7.2
@shohruh-genius Yes, because you are pulling in io.quarkus:quarkus-jdbc-postgresql
. However, the point of this issue is that we must be able to run reactive Hibernate without having to pull in a JDBC driver.
@vemilyus the problem raises when I delete implementation("io.quarkiverse.hibernatetypes:quarkus-hibernate-types:2.1.0")
I am facing the same issue with 'mysql'. I am using mysql-reactive in my project. Is there any workaround for the time being?
For now I have manually added quarkus-agroal dependency in my gradle.
Describe the bug
When using
io.quarkus:quarkus-hibernate-reactive-panache
with version3.7.1
, the build fails with an error message regarding a missing JDBC driver even though JDBC is not used in the application. In version3.6.9
, the application successfully builds. I am not entirely sure, but it seems like the issue is only reproducible with Gradle.The application only uses reactive dependencies:
Furthermore, the configuration is as suggested in the docs:
Expected behavior
The application starts and accesses the database using Hibernate Reactive.
Actual behavior
The following exception is produced when starting the dev mode:
When setting the
quarkus.datasource.jdbc=false
property as suggested, the following error is produced:How to Reproduce?
Please find the attached reproducer for the issue:
jdb-driver-exception-reproducer.zip
Output of
uname -a
orver
No response
Output of
java -version
Quarkus version or git rev
3.7.1
Build tool (ie. output of
mvnw --version
orgradlew --version
)Additional information
No response