p2-inc / keycloak-events

Useful Keycloak event listener implementations and utilities.
https://phasetwo.io
Other
169 stars 35 forks source link

Issue when trying to use keycloak-admin-client within custom event listener #10

Closed escribano89 closed 1 year ago

escribano89 commented 1 year ago

Thanks for your contribution to Keycloak, it's really amazing. I'm trying to use the keycloak-admin-client within a custom event listener, but I'm getting the below error during the runtime (loading Events tab). Please note that I'm using the same pom structure as you are using the keycloak-events. The version of keycloak is 20.0.2:

ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-4) Uncaught server error: java.lang.NoClassDefFoundError: org/keycloak/admin/client/Keycloak
    at auth.CustomEventListenerProvider.<init>(CustomEventListenerProvider.java:29)
    at auth.CustomEventListenerProviderFactory.create(CustomEventListenerProviderFactory.java:12)
    at auth.CustomEventListenerProviderFactory.create(CustomEventListenerProviderFactory.java:9)
    at org.keycloak.services.DefaultKeycloakSession.getProvider(DefaultKeycloakSession.java:282)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy.invoke(ContextParameterInjector.java:166)
    at jdk.proxy2/jdk.proxy2.$Proxy56.getProvider(Unknown Source)
    at org.keycloak.services.resources.admin.AdminEventBuilder.lambda$addListeners$0(AdminEventBuilder.java:101)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.accept(ForEachOps.java:183)
    at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
    at java.base/java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1715)
    at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
    at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
    at java.base/java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:150)
    at java.base/java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:173)
    at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.base/java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:596)
    at org.keycloak.services.resources.admin.AdminEventBuilder.addListeners(AdminEventBuilder.java:100)
    at org.keycloak.services.resources.admin.AdminEventBuilder.<init>(AdminEventBuilder.java:57)
    at org.keycloak.services.resources.admin.RealmsAdminResource.getRealmAdmin(RealmsAdminResource.java:183)
    at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
    at java.base/java.lang.reflect.Method.invoke(Method.java:578)
    at org.jboss.resteasy.core.ResourceLocatorInvoker.constructLocator(ResourceLocatorInvoker.java:107)
    at org.jboss.resteasy.core.ResourceLocatorInvoker.resolveTargetFromLocator(ResourceLocatorInvoker.java:87)
    at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:148)
    at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:183)
    at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:141)
    at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:32)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:492)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:261)
    at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:161)
    at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:364)
    at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:164)
    at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:247)
    at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:73)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:151)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:82)
    at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:42)
    at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
    at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:84)
    at io.quarkus.vertx.http.runtime.StaticResourcesRecorder$2.handle(StaticResourcesRecorder.java:71)
    at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
    at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:430)
    at io.quarkus.vertx.http.runtime.VertxHttpRecorder$6.handle(VertxHttpRecorder.java:408)
    at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
    at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:173)
    at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:140)
    at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$0(QuarkusRequestFilter.java:82)
    at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:564)
    at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2449)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1478)
    at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
    at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.base/java.lang.Thread.run(Thread.java:1589)
Caused by: java.lang.ClassNotFoundException: org.keycloak.admin.client.Keycloak
    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:521)
    at io.quarkus.bootstrap.runner.RunnerClassLoader.loadClass(RunnerClassLoader.java:115)
    at io.quarkus.bootstrap.runner.RunnerClassLoader.loadClass(RunnerClassLoader.java:65)
    ... 60 more
xgp commented 1 year ago

The keycloak-admin-client and its dependencies are not included by default with Keycloak. In order to run extensions that require it, you have to include it. Check out this pom https://github.com/p2-inc/phasetwo-containers/blob/main/libs/pom.xml for an example of how to collect the appropriate libraries. I use this to build an ear file, but don't use it (Keycloak no longer supports deployment from ear files), but I just copy the jars from the unpacked target dir.

escribano89 commented 1 year ago

Thanks for your quick answer, I really appreciate it. I'm novel on keycloak and Java is not my main language. Yes, I'm using a pom, and I'm including the dependency. Then I generate the jar using mvn clean install and I move the jar to the providers folder. But I'm getting the same error. This is my pom file based on your provided example:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>test.auth</groupId>
  <artifactId>user-created-event-listener</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>jar</packaging>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-ear-plugin</artifactId>
    <version>3.3.0</version>
        <configuration>
          <includeLibInApplicationXml>true</includeLibInApplicationXml>
        </configuration>
      </plugin>
    </plugins>      
  </build>

  <properties>
  <maven.compiler.source>11</maven.compiler.source>
   <maven.compiler.target>11</maven.compiler.target>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <keycloak.version>20.0.2</keycloak.version>
    <resteasy.version>4.7.5.Final</resteasy.version>

    <keycloak-orgs.version>[0.5,)</keycloak-orgs.version>
    <keycloak-themes.version>[0.8,)</keycloak-themes.version>
  </properties>

  <dependencies>
     <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-server-spi</artifactId>
        <version>${keycloak.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-server-spi-private</artifactId>
        <version>${keycloak.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>org.keycloak</groupId>
        <artifactId>keycloak-services</artifactId>
        <version>${keycloak.version}</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.keycloak</groupId>
      <artifactId>keycloak-admin-client</artifactId>
      <version>${keycloak.version}</version>
      <exclusions>
        <exclusion>
          <groupId>*</groupId>
          <artifactId>*</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-client</artifactId>
      <version>${resteasy.version}</version>
      <exclusions>
        <exclusion>
          <groupId>*</groupId>
          <artifactId>*</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-client-api</artifactId>
      <version>${resteasy.version}</version>
      <exclusions>
        <exclusion>
          <groupId>*</groupId>
          <artifactId>*</artifactId>
        </exclusion>
      </exclusions>
    </dependency>
  </dependencies>
</project>
xgp commented 1 year ago

Almost there. Try the following.

  1. change your packaging from jar to ear. <packaging>ear</packaging>
  2. run mvn install
  3. copy the jar from target/user-created-event-listener-0.0.1-SNAPSHOT.jar to the providers dir
  4. copy the jars from target/user-created-event-listener-0.0.1-SNAPSHOT/*.jar to the providers dir
  5. start keycloak

Your target/ dir layout should look like this after running mvn install

target/
├── application.xml
├── maven-archiver
│   └── pom.properties
├── user-created-event-listener-0.0.1-SNAPSHOT
│   ├── META-INF
│   │   └── application.xml
│   ├── org.jboss.resteasy-resteasy-client-4.7.5.Final.jar
│   ├── org.jboss.resteasy-resteasy-client-api-4.7.5.Final.jar
│   └── org.keycloak-keycloak-admin-client-20.0.2.jar
├── user-created-event-listener-0.0.1-SNAPSHOT.ear
└── user-created-event-listener-0.0.1-SNAPSHOT.jar
escribano89 commented 1 year ago

It's working right now. Thank you very much. It has been crazy to try to find the solution out there. Very very helpful.

xgp commented 1 year ago

Great. Glad this worked for you.