dasniko / testcontainers-keycloak

A Testcontainer implementation for Keycloak IAM & SSO.
Apache License 2.0
328 stars 51 forks source link

NoSuchMethodError when upgrading to 2.1 #61

Closed sventorben closed 2 years ago

sventorben commented 2 years ago

I get the following exception after upgrading from version 2.0 to 2.1:

java.lang.NoSuchMethodError: 'org.jboss.resteasy.spi.ResteasyProviderFactory org.jboss.resteasy.plugins.providers.RegisterBuiltin.getClientInitializedResteasyProviderFactory(java.lang.ClassLoader)'

    at org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl.getProviderFactory(ResteasyClientBuilderImpl.java:377)
    at org.jboss.resteasy.client.jaxrs.internal.ResteasyClientBuilderImpl.register(ResteasyClientBuilderImpl.java:479)
    at dasniko.testcontainers.keycloak.KeycloakContainer.getKeycloakAdminClient(KeycloakContainer.java:328)
    at dasniko.testcontainers.keycloak.KeycloakContainer.containerIsStarted(KeycloakContainer.java:184)
    at org.testcontainers.containers.GenericContainer.containerIsStarted(GenericContainer.java:688)
    at org.testcontainers.containers.GenericContainer.tryStart(GenericContainer.java:504)
    at org.testcontainers.containers.GenericContainer.lambda$doStart$0(GenericContainer.java:331)
    at org.rnorth.ducttape.unreliables.Unreliables.retryUntilSuccess(Unreliables.java:81)
    at org.testcontainers.containers.GenericContainer.doStart(GenericContainer.java:329)
    at org.testcontainers.containers.GenericContainer.start(GenericContainer.java:317)
    at org.testcontainers.junit.jupiter.TestcontainersExtension$StoreAdapter.start(TestcontainersExtension.java:242)
    at org.testcontainers.junit.jupiter.TestcontainersExtension$StoreAdapter.access$200(TestcontainersExtension.java:229)
    at org.testcontainers.junit.jupiter.TestcontainersExtension.lambda$null$1(TestcontainersExtension.java:59)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.lambda$getOrComputeIfAbsent$4(ExtensionValuesStore.java:86)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.computeValue(ExtensionValuesStore.java:223)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$MemoizingSupplier.get(ExtensionValuesStore.java:211)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$StoredValue.evaluate(ExtensionValuesStore.java:191)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore$StoredValue.access$100(ExtensionValuesStore.java:171)
    at org.junit.jupiter.engine.execution.ExtensionValuesStore.getOrComputeIfAbsent(ExtensionValuesStore.java:89)
    at org.junit.jupiter.engine.execution.NamespaceAwareStore.getOrComputeIfAbsent(NamespaceAwareStore.java:53)
    at org.testcontainers.junit.jupiter.TestcontainersExtension.lambda$beforeAll$2(TestcontainersExtension.java:59)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.testcontainers.junit.jupiter.TestcontainersExtension.beforeAll(TestcontainersExtension.java:59)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeBeforeAllCallbacks$10(ClassBasedTestDescriptor.java:381)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeBeforeAllCallbacks(ClassBasedTestDescriptor.java:381)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:205)
    at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.before(ClassBasedTestDescriptor.java:80)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:148)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
    at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
    at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

Check this build or this PR for details.

I already tried to remove the explicit dependency to keycloak-admin-client, but that did not help either.

sventorben commented 2 years ago

Could it be possible that this is due to different resteasy versions on the classpath?

If I take a look at the dependencies in my above mentioned project (mvn dependency:tree -Dverbose), I see this:

[INFO] +- org.keycloak:keycloak-services:jar:17.0.0:provided
[...]
[INFO] |  +- org.jboss.resteasy:resteasy-multipart-provider:jar:3.15.1.Final:provided
[...]
[INFO] |  |  +- org.jboss.resteasy:resteasy-jaxrs:jar:3.15.1.Final:provided
[INFO] |  |  +- (org.jboss.resteasy:resteasy-client:jar:3.15.1.Final:provided - omitted for conflict with 4.7.5.Final)
[...]
[INFO] +- com.github.dasniko:testcontainers-keycloak:jar:2.1.0:test
[...]
[INFO] |  +- org.jboss.resteasy:resteasy-client:jar:4.7.5.Final:test
[INFO] |  |  +- org.jboss.resteasy:resteasy-client-api:jar:4.7.5.Final:test
[INFO] |  |  +- org.jboss.resteasy:resteasy-core:jar:4.7.5.Final:test

I think this may be related to 9b6567bfc8269e6a761a820c8b67e74d5e125fe8, but I don't really understand why you introduced a different resteasy version there. Should I use this in a different way or exclude the implicit resteasy dependencies from keycloak-services?

dasniko commented 2 years ago

That's the crap with different Resteasy versions in pom.xml and the Quarkus runtime (also legacy Wildfly, starting with KC 16+). Dependencies are still on 3.x and runtime is 4.x - and Resteasy API and package contents changed between 3 and 4. There are several issues on the KC repository, e.g. https://github.com/keycloak/keycloak/issues/9599 , https://github.com/keycloak/keycloak/issues/10555 and others.

As soon as I need some Restesay components and resources in my extensions, and I want to test it with this Testcontainer, I get version clashes, as I need to set the custom Resteasy version to the aligned runtime version 4.7.5.Final. But the default admin client, which is needed for importing a realm, as the auto-import feature is still missing, needs (wants!) 3.x. So this is clashing. Setting everything to 4.7.5.Final, like used in the current KC Quarkus runtime, helps and works. I know and see, that this isn't obvious at first sight and might cause some refactorings and good dependency management.

In my understanding, the transient dependency to 3.x in keycloak-services is wrong for usage in Quarkus, as there are no more 3.x libraries at the classpath in runtime environment.

I don't see a better solution at the moment. If you have an idea or approach, I'm happy to discuss this.

The move of Keycloak to Quarkus was a good step forward, but leads to some other problems on dependency usage in extensions. It really misses a good Developer Experience!

dasniko commented 2 years ago

Ok... seems that's indeed an implementation detail in my projects and I shouldn't have done it... blame on me! But, all this dependency hell is still crap, having different versions is not good, it does harm. Changing the platform and providing less possibilities than the product had before, is fk st. They just do what they want (see also the change in supported databases, no more support for JDBC_PING, lacking log-support, etc...) and don't care about existing customers/deployments and the developers.

I'll revert this dependency change asap and publish a version 2.1.1