Include third party library in wildfly jar (keycloak SPI)

Tags: , , , ,



I am creating plugins (providers) for keycloak using the Service Provider Interface. I have been able to build a couple. Now I need to add the smallrye-graphql-client library to query a graphql server. However, the library is not found in the classpath when I deploy the pluging.

Questions

  1. Is it possible to still create a jar which includes the dependency library?
  2. If 1 is not possible, can it be done with a war?
  3. How can I add the library to the classpath. Preferably, that those are added together with the plugin instead of statically to Wildfly. I am using gradle. More details below.

Background Info

I succeeded to create a class and integration test for it. However, when I deploy the plugin to keycloak I get a the following error:

16:38:38,127 ERROR [org.keycloak.services.error.KeycloakErrorHandler] (default task-1) 
             Uncaught server error: java.util.ServiceConfigurationError: no 
             io.smallrye.graphql.client.typesafe.api.GraphQlClientBuilder in classpath

I have configured gradle to include the dependency that caused the problem and also add it to the classpath. I suspect that I should add an entry to the jboss-deployment-structure.xml but I don’t know what I should write there.

The gradle configuration

plugins {
    id 'war'
    id 'java-library'
    id 'maven-publish'
}
repositories {
    mavenLocal()
    mavenCentral()
    jcenter()
}
configurations {
    dependenciesToInclude
}
dependencies {
    dependenciesToInclude "io.smallrye:smallrye-graphql-client:1.0.20"


    providedCompile group: 'javax.enterprise', name: 'cdi-api', version: '2.0'
    providedCompile "org.keycloak:keycloak-server-spi:${keycloakVersion}"
    providedCompile "org.keycloak:keycloak-server-spi-private:${keycloakVersion}"
    providedCompile("org.keycloak:keycloak-services:${keycloakVersion}") {
        exclude group: 'org.slf4j', module: 'slf4j-api'
        exclude group: 'org.slf4j', module: 'slf4j-log4j12'
    }
    providedCompile group: 'org.keycloak', name: 'keycloak-model-api', version: '1.8.1.Final'
    providedCompile "org.jboss.resteasy:resteasy-jaxrs"

    providedCompile group: 'org.eclipse.microprofile.graphql', name: 'microprofile-graphql-api', version: '1.0.3'
    compile group: 'org.apache.geronimo.config', name: 'geronimo-config-impl', version: '1.2.2'
    configurations.compile.extendsFrom(configurations.dependenciesToInclude)
}


jar {
    manifest {
        attributes(
                "Class-Path": configurations.dependenciesToInclude.collect { it.getName() }.join(' '))
    }
    from {
        configurations.dependenciesToInclude.collect { it.isDirectory() ? it : zipTree(it) }
    }
}
❯ cat META-INF/MANIFEST.MF                                                                                                                                                                                                     ─╯
Manifest-Version: 1.0
Class-Path: smallrye-graphql-client-1.0.20.jar geronimo-config-impl-1.2.
 2.jar smallrye-graphql-client-api-1.0.20.jar microprofile-graphql-api-1
 .0.3.jar microprofile-config-api-1.3.jar org.osgi.annotation.versioning
 -1.0.0.jar

Bellow is the jboss-deployment-structure.xml. There you can see my attempt to include the graphql library (commented out)

<jboss-deployment-structure>
    <deployment>
        <dependencies>
            <module name="org.keycloak.keycloak-services"/>
            <module name="org.keycloak.keycloak-saml-core-public"/>
            <module name="org.apache.commons.codec"/>
            <module name="org.apache.commons.lang"/>
            <module name="org.jboss.logging"/>
            <!--            <module name="io.smallrye.smallrye-graphql-client"/>-->
        </dependencies>
    </deployment>
</jboss-deployment-structure>

I am using Keycloak 11.0.2 (WildFly Core 12.0.3.Final)

Answer

I am not sure if I understand correctly the piece with adding the library statically to jboss. I will answer from what I understand your issue is. There are multiple solutions to your issue.

Jboss dependencies

You should first understand how JBoss resolves its classes and dependencies. I will explain very simply. JBoss is divided to modules and deployments.

Modules are placed in home folder of your JBoss. Modules are the libraries that you will not be changing during runtime. Each module has descriptor file called module.xml in which you define you artifacts, dependencies and name of the module. File module.xml is basically something like your jboss-deployment-structure.xml in the module world.

Deployments are placed into deployments folder and can be redeployed while the JBoss server is running. In case a deployment needs a dependency on another module/deployment, it needs to be included in the jboss-deployment-structure of the given deployment. Note that modules cannot be dependent on deployments but it works the other way around

You can find much more information all over the internet, but this is the basic info you could use.

Solution 1 – JBoss Modules

In case you want to acquire a dependency to a module in JBoss you need to find its name in module.xml and write it to your jboss-deployment-structure.xml (assuming that you library is a deployment)

So for example if you want a dependency to a jackson-dataformat-cbor-2.10.5.jar in keycloak modules path:

modules/system/layers/keycloak/com/fasterxml/jackson/dataformat/jackson-dataformat-cbor/main/jackson-dataformat-cbor-2.10.5.jar

In the same folder is a module.xml with content:

<?xml version="1.0" encoding="UTF-8"?>
<!--
  ~ Copyright 2019 Red Hat, Inc. and/or its affiliates
  ~ and other contributors as indicated by the @author tags.
  ~
  ~ Licensed under the Apache License, Version 2.0 (the "License");
  ~ you may not use this file except in compliance with the License.
  ~ You may obtain a copy of the License at
  ~
  ~ http://www.apache.org/licenses/LICENSE-2.0
  ~
  ~ Unless required by applicable law or agreed to in writing, software
  ~ distributed under the License is distributed on an "AS IS" BASIS,
  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ~ See the License for the specific language governing permissions and
  ~ limitations under the License.
  -->
<module name="com.fasterxml.jackson.dataformat.jackson-dataformat-cbor" xmlns="urn:jboss:module:1.3">
    <resources>
        <resource-root path="jackson-dataformat-cbor-2.10.5.jar"/>
    </resources>

    <dependencies>
        <module name="com.fasterxml.jackson.core.jackson-core"/>
        <module name="com.fasterxml.jackson.core.jackson-databind"/>
        <module name="com.fasterxml.jackson.core.jackson-annotations"/>
    </dependencies>
</module>

that means that the name of your module is com.fasterxml.jackson.dataformat.jackson-dataformat-cbor and that is what you put in your jboss-deployment-structure.xml as a dependency.

Your case

In your case you need a dependency to smallrye-graphql-client that is not included in JBoss modules. Which means you will have to add it there. Create a JBoss layer by creating a folder in layers: modules/system/layers/[name of your layer] After doing so you should also include your layer in modules/layers.conf. You include it by writting comma after keycloak layer like so:

layers=keycloak,[your layer]

After you created that, you can add to your layer your plugins for example like so: modules/system/layers/[name of your layer]/org/my/plugins/main

Inside of the folder will be:

  • Your jar
  • module.xml (create it according to the other modules)

Then the only thing you will do is to include this module in your jboss-deployment-structure.xml as a dependency. (You always include the name of the module)

Note that you might have to add more libraries as dependencies to your modules. You can include all of it in the modules

In case you don´t like to create this stuff manually you can create jboss-modules-builder such as this one. It will create your modules xml using maven:

https://github.com/marcel-ouska/jboss-modules-builder

Solution 2 – Fat JAR/WAR

Basically If you want a simple solution you can add the library into your JAR/WAR directly using maven/Gradle plugins.

I do not recommend to use Fat JAR/WAR solutions as with adding more plugins you could get lost in dependencies and getting errors such as cases of duplicated dependency classes.



Source: stackoverflow