2

I use Spring Boot and Spring Security in combination with Keycloak. The build tool is gradle.

When I run ./gradlew bootRun the application works flawless. If I use the resulting fat jar (i.e. java -jar myapp.jar) the application will boot but I encounter an exception when the application tries to invoke some keyloak stuff:

java.lang.IllegalArgumentException: org.keycloak.admin.client.resource.RealmsResource referenced from a method is not visible from class loader
    at java.base/java.lang.reflect.Proxy$ProxyBuilder.ensureVisible(Proxy.java:851) ~[na:na]
    at java.base/java.lang.reflect.Proxy$ProxyBuilder.validateProxyInterfaces(Proxy.java:682) ~[na:na]
    at java.base/java.lang.reflect.Proxy$ProxyBuilder.<init>(Proxy.java:628) ~[na:na]
    at java.base/java.lang.reflect.Proxy.lambda$getProxyConstructor$1(Proxy.java:426) ~[na:na]
    at java.base/jdk.internal.loader.AbstractClassLoaderValue$Memoizer.get(AbstractClassLoaderValue.java:327) ~[na:na]
    at java.base/jdk.internal.loader.AbstractClassLoaderValue.computeIfAbsent(AbstractClassLoaderValue.java:203) ~[na:na]
    at java.base/java.lang.reflect.Proxy.getProxyConstructor(Proxy.java:424) ~[na:na]
    at java.base/java.lang.reflect.Proxy.newProxyInstance(Proxy.java:999) ~[na:na]
    at org.jboss.resteasy.client.jaxrs.ProxyBuilder.proxy(ProxyBuilder.java:79) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
    at org.jboss.resteasy.client.jaxrs.ProxyBuilder.build(ProxyBuilder.java:131) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
    at org.jboss.resteasy.client.jaxrs.internal.ClientWebTarget.proxy(ClientWebTarget.java:93) ~[resteasy-client-3.1.4.Final.jar!/:3.1.4.Final]
    at org.keycloak.admin.client.Keycloak.realms(Keycloak.java:114) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]
    at org.keycloak.admin.client.Keycloak.realm(Keycloak.java:118) ~[keycloak-admin-client-3.4.3.Final.jar!/:3.4.3.Final]

So I figured out that there must be something wrong how I start the application in the terminal. I found this official site, which explains how to run an Spring application from terminal. So I tried all of the solutions including:

$ unzip -q myapp.jar
$ java org.springframework.boot.loader.JarLauncher

but I get the same error.

After 2 days of searching and experimenting I'm out of any ideas.

So my question is basically: Does anyone have any idea how to solve this problem?

My knowledge regarding the classloader is also limited - so any pratical hints in this direction are very welcomed as well.

EDIT (added build.gradle): There were some Jackson problems that's why the Spring web dependencies are included directly (without the starter and therefor without Jackson). Here is the build.gradle:

buildscript {
   ext {
      kotlinVersion = '1.2.20'
      springBootVersion = '1.5.7.RELEASE'
      keycloakVersion = '3.4.3.Final'
      restEasyClientVersion = '3.1.4.Final'
      postgresServerVersion = '10.0'
      postgresJdbcDriverVersion = '42.1.4'
      spekVersion = '1.1.5'
      jacksonVersion = '2.8.10'
      javaxWsRsVersion = '2.1'

      logbackVersion = '1.2.3'
      slf4jVersion = '1.7.25'
   }
   repositories {
      mavenCentral()
      jcenter()
      //spring dev
      maven { url 'https://repo.spring.io/snapshot' }
      maven { url 'https://repo.spring.io/milestone' }
      //Kotlin dev
      maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
      //gradle plugins
      maven { url "https://plugins.gradle.org/m2/" }
   }
   dependencies {
      classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
      classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}")
      classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}")
      classpath('com.bmuschko:gradle-docker-plugin:3.2.0')

      //for tests
      classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.0'
   }
}

allprojects {

   repositories {
      mavenCentral()
      jcenter()
      //spring dev
      maven { url 'https://repo.spring.io/snapshot' }
      maven { url 'https://repo.spring.io/milestone' }
   }

}

subprojects {

   repositories {
      maven { url 'http://dl.bintray.com/kotlin/kotlin-eap-1.2' }
      // for tests
      maven { url "http://dl.bintray.com/jetbrains/spek" }
   }

   // for kotlin
   apply plugin: 'kotlin'
   // for tests
   apply plugin: 'org.junit.platform.gradle.plugin'

   apply plugin: 'kotlin-spring'
   apply plugin: 'org.springframework.boot'
   apply plugin: 'io.spring.dependency-management'

   // for tests
   junitPlatform {
      filters {
         engines {
            include 'spek'
         }
      }
   }

   sourceCompatibility = 1.9

   dependencies {
      // kotlin
      compile("org.jetbrains.kotlin:kotlin-stdlib:${kotlinVersion}")
      compile("org.jetbrains.kotlin:kotlin-test:${kotlinVersion}")
      compile("org.jetbrains.kotlin:kotlin-reflect:${kotlinVersion}")
      // jackson
      compile "com.fasterxml.jackson.core:jackson-core:${jacksonVersion}"
      compile "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"
      compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:${jacksonVersion}"
      compile "com.fasterxml.jackson.jaxrs:jackson-jaxrs-base:${jacksonVersion}"
      compile "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.2"

      // Java WS RS
      compile "javax.ws.rs:javax.ws.rs-api:${javaxWsRsVersion}"
      // for tests
      testCompile "org.jetbrains.spek:spek-api:${spekVersion}"
      testRuntime "org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}"
      testCompile ("org.jetbrains.spek:spek-api:${spekVersion}") {
         exclude group: 'org.jetbrains.kotlin'
      }
      testRuntime ("org.jetbrains.spek:spek-junit-platform-engine:${spekVersion}") {
         exclude group: 'org.junit.platform'
         exclude group: 'org.jetbrains.kotlin'
      }
      // spring security
      compile('org.springframework.boot:spring-boot-starter-security')
      testCompile('org.springframework.security:spring-security-test')
      // begin: spring web without jackson
      compile('org.springframework.boot:spring-boot-starter')
      compile('org.springframework.boot:spring-boot-starter-tomcat')
      compile('org.springframework:spring-web')
      compile('org.springframework:spring-webmvc')
      testCompile('org.springframework.boot:spring-boot-starter-test')
      // end: spring web without jackson
      // Keycloak
      compile("org.keycloak:keycloak-spring-security-adapter:${keycloakVersion}")
      compile("org.keycloak:keycloak-spring-boot-adapter:${keycloakVersion}")
      compile("org.keycloak:keycloak-tomcat8-adapter:${keycloakVersion}")
      compile("org.keycloak:keycloak-admin-client:${keycloakVersion}")
      compile("org.jboss.resteasy:resteasy-client:${restEasyClientVersion}")
      compile("org.jboss.resteasy:resteasy-jackson2-provider:${restEasyClientVersion}")

   }

   compileKotlin {
      kotlinOptions.jvmTarget = '1.8'
      kotlinOptions.allWarningsAsErrors = true
   }
   compileTestKotlin {
      kotlinOptions.jvmTarget = '1.8'
   }
}
Maurice Müller
  • 1,312
  • 3
  • 16
  • 32
  • I would bet you've got some conflict between two different resteasy versions. You could use `gradle dependencies` to list all the dependency tree and see what you've got there. https://stackoverflow.com/questions/21645071/using-gradle-to-find-dependency-tree – Aritz Jan 22 '18 at 21:35
  • Thanks for the suggestion. I took a look but all the dependencies regarding resteasy seem to be equal (i.e. 3.1.4.Final). So I checked the keycloak dependencies as well but they are equal, too. – Maurice Müller Jan 23 '18 at 11:51
  • could you post your gradle configuration? – Aritz Jan 23 '18 at 12:05
  • I added build.gradle. It's a multiproject setup, so to reduce complexity I removed a lot of stuff (the original file is >400 lines) - of course, all the dependencies are included! – Maurice Müller Jan 23 '18 at 12:32
  • You should include either the spring boot adapter + tomcat adapter or the spring security one, but not both together. See [the docs](http://www.keycloak.org/docs/3.0/securing_apps/topics/oidc/java/spring-boot-adapter.html). – Aritz Jan 23 '18 at 13:26
  • Ok, I tried all combinations with spring boot adapter / tomcat and spring security. Also, [as stated in the docs](http://www.keycloak.org/docs/3.3/securing_apps/topics/oidc/java/spring-security-adapter.html#springbootingeration) under **Spring Boot Integration**, I tried using *Keycloak Spring Boot Starter* as a dependency instead of the others mentioned above. Still, no luck :( – Maurice Müller Jan 23 '18 at 15:17

1 Answers1

1

The problem was the underlying class loader of ForkJoinPool.commonPool which is used by CompletableFuture.supplyAsync.

Because the problem and the solution is complex please refer to my other question for better understanding.

This question is only kept alive for this cross reference (and may hopefully lead others to the correct solution).

Maurice Müller
  • 1,312
  • 3
  • 16
  • 32