Spring Boot / Security classloader issues with Keycloak run from terminal - spring-boot

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'
}
}

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).

Related

Issue in upgrading Spring Boot App with Apache Camel Dependencies

Our application is currently running on spring boot 1.5.10.RELEASE version, and I'm trying to upgrade to spring boot 2.0.0.RELEASE But after upgrading to 2.0.0.RELEASE I get the following error
Error: Could not find or load main class com.app.MyApplication
Given Below is the build.gradle file
buildscript {
ext {
springBootVersion = '2.0.0.RELEASE'
camelVersion = '2.24.0'
}
repositories {
mavenCentral()
jcenter()
maven { url "https://plugins.gradle.org/m2/" }
maven { url "http://repo.spring.io/snapshot" }
maven { url "http://repo.spring.io/milestone" }
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
apply from: '../common.gradle'
apply from: '../test.gradle'
apply plugin: 'org.springframework.boot'
apply plugin: 'war'
apply plugin: 'io.spring.dependency-management'
dependencies {
providedRuntime "org.springframework.boot:spring-boot-starter-tomcat"
compile ("org.flywaydb:flyway-core:5.0.7")
compile ("org.springframework.retry:spring-retry:1.2.2.RELEASE")
compile("org.springframework:spring-jms:5.1.1.RELEASE")
compile("org.springframework.boot:spring-boot-starter-activemq:2.0.6.RELEASE")
compile("org.springframework:spring-jdbc:5.0.4.RELEASE")
compile("org.springframework.retry:spring-retry:1.2.2.RELEASE")
compile("com.amazonaws:aws-java-sdk-sqs:1.11.428")
compile("com.amazonaws:amazon-sqs-java-messaging-lib:1.0.4")
compile ("org.flywaydb:flyway-core:5.0.7")
runtime("org.apache.activemq:activemq-kahadb-store:5.15.6")
compile("org.apache.camel:camel-spring-boot-starter:${camelVersion}")
compile("org.apache.camel:camel-core:${camelVersion}")
compile("org.apache.camel:camel-http-starter:${camelVersion}")
compile("org.apache.camel:camel-http4:${camelVersion}")
compile("org.apache.camel:camel-jsonpath:${camelVersion}")
compile("org.apache.camel:camel-jackson:${camelVersion}")
compile("org.apache.camel:camel-base64:${camelVersion}")
compile("org.apache.camel:camel-groovy:${camelVersion}")
compile("org.apache.camel:camel-jolt:${camelVersion}")
compile("org.apache.camel:camel-jaxb:${camelVersion}")
compile("org.apache.camel:camel-ahc:${camelVersion}")
compile("org.codehaus.groovy:groovy-json:3.0.3")
}
configurations {
all*.exclude group: '', module: 'servlet-api'
}
springBoot {
mainClassName = 'com.app.MyApplication'
}
war {
archiveName = "application.war"
}
I'm wondering if this is due to the camel dependencies as when I tried to use Spring Boot version 1.5.22. It is working fine but as soon as I change this to 2.x.x it stop working.
This issue got resolved when I ran this application in Intellij instead of the eclipse.

Spring Boot plugin doesn't add lib folder in jar

I have the following build.gradle:
group 'as'
version '1.0'
buildscript {
ext {
springVersion = '5.0.4.RELEASE'
springBootVersion = '1.5.6.RELEASE'
springJPAVersion = '2.0.5.RELEASE'
javaxVersion = '1.0.2'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'org.springframework.boot'
jar {
baseName = 'reports'
version = '0.0.1'
manifest {
attributes 'Implementation-Title': baseName,
'Implementation-Version': version
}
from {
configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
}
}
repositories {
jcenter()
maven { url "https://repo.spring.io/snapshot" }
maven { url "https://repo.spring.io/milestone" }
}
dependencies {
implementation "org.springframework.boot:spring-boot-starter-web:${springBootVersion}"
implementation "org.springframework.boot:spring-boot-starter-data-jpa:${springBootVersion}"
implementation "org.springframework.data:spring-data-jpa:${springJPAVersion}"
implementation "javax.persistence:persistence-api:${javaxVersion}"
implementation "mysql:mysql-connector-java:5.1.43"
testImplementation group: 'junit', name: 'junit', version: '4.11'
}
And gradle creates jar with following structure:
BOOT-INF
classes
META-INF
org
But it seems that the structure must be the following:
BOOT-INF
classes
lib
META-INF
org
with lib folder, which contains dependencies, because now after launch I get error:
Exception ...
Caused by: java.lang.NoClassDefFoundError:
org/springframework/boot/SpringApplication at com.as.reports.Application.main(Application.java:12)
So I know this answer is probably too late to help you, but I stumbled across this question while trying to solve the exact same problem, and was reminded of this.
After half a day of trial and error, I found the solution, so hopefully I can save someone else the pain of finding this unanswered question.
The problem is with using the implementation keyword in your dependencies. The 1.x Spring Boot Gradle plugin doesn't appear recognize that keyword as notating required dependencies for repackaging a fat jar. If you change those back to the older compile keyword, you should get the necessary lib folder.

How to force a specific dependency version in a gradle buildscript

There's an issue for the gradle-docker-plugin and SpringBootVersion 2.0.0.M4
M4 uses a newer jersey client and using the docker-plugin ends in an Exception:
ERROR com.github.dockerjava.core.async.ResultCallbackTemplate - Error during callback
java.lang.IllegalStateException: InjectionManagerFactory not found.
at org.glassfish.jersey.internal.inject.Injections.lambda$lookupInjectionManagerFactory$0(Injections.java:98)
at java.util.Optional.orElseThrow(Optional.java:290)
at org.glassfish.jersey.internal.inject.Injections.lookupInjectionManagerFactory(Injections.java:98)
at org.glassfish.jersey.internal.inject.Injections.createInjectionManager(Injections.java:68)
at org.glassfish.jersey.client.ClientConfig$State.initRuntime(ClientConfig.java:432)
at org.glassfish.jersey.internal.util.collection.Values$LazyValueImpl.get(Values.java:341)
at org.glassfish.jersey.client.ClientConfig.getRuntime(ClientConfig.java:826)
at org.glassfish.jersey.client.ClientRequest.getConfiguration(ClientRequest.java:285)
at org.glassfish.jersey.client.JerseyInvocation.validateHttpMethodAndEntity(JerseyInvocation.java:143)
at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:112)
at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:108)
at org.glassfish.jersey.client.JerseyInvocation.<init>(JerseyInvocation.java:99)
at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:456)
at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:357)
at com.github.dockerjava.jaxrs.async.POSTCallbackNotifier.response(POSTCallbackNotifier.java:29)
at com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier.call(AbstractCallbackNotifier.java:50)
at com.github.dockerjava.jaxrs.async.AbstractCallbackNotifier.call(AbstractCallbackNotifier.java:24)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
My BuildScript in my main project:
buildscript {
ext {
springBootVersion = "2.0.0.M4"
}
repositories {
maven { url "https://repo.spring.io/plugins-snapshot" }
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
}
}
....
As you can see, we load the spring-boot-gradle-plugin version=2.0.0.M4 and all its dependencies.
My subproject build.gradle:
apply plugin: "org.springframework.boot"
apply from: "docker.gradle"
....
Most important the docker.gradle file in the same directory as the build.gradle of the subproject:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.bmuschko:gradle-docker-plugin:3.0.11'
}
}
apply plugin: com.bmuschko.gradle.docker.DockerRemoteApiPlugin
import com.bmuschko.gradle.docker.tasks.image.*
...
task buildImage(type: DockerBuildImage, dependsOn: copyDockerFiles) {
version.release = true
dockerFile = file("${projectDir}/build/docker/Dockerfile")
inputDir = file("${projectDir}/build/docker")
tags = ['...']
}
My Questions:
How do I know which Version of the jersey client loads SpringBoot 2.0.0.M4?
How do I force gradle in docker.gradle to use a specific version of the jersey client?
Adding to the classpath didnt work. I think gradle will just use the newest version, wich will be loaded by SpringBoot 2.0.0.M4
You have to add the following in your build.gradle dependencies as pointed in: This Link
dockerJava 'com.nirima:docker-java-shaded:0.16.2'
dockerJava 'org.slf4j:slf4j-simple:1.7.5'
dockerJava 'cglib:cglib:3.2.0'
After that you have to separate "buildImage" and "tagImage" tasks as suggested in This link
I have tested this with: spring boot 2.0.0.M6
I simply did:-
dependencies {
classpath "org.jfrog.buildinfo:build-info-extractor-gradle:4+"
classpath "org.mozilla:rhino:1.7.14"
classpath ( group: 'org.apache.commons', name: 'commons-text'){
version{
strictly '1.10.0'
}
}
}

excludeDevtools seems to have no effect under Spring Boot 1.5.1.RELEASE (but OK in 1.4.4.RELEASE)

I've created a fresh new Spring Boot 1.5.1.RELEASE project with a devtools dependency:
dependencies {
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-devtools')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
When running from inside Idea this works no problem. However, after performing a bootRepackage, the resulting fat jar has no devtools jar in its lib folder. I have the following entry in build.gradle:
bootRepackage {
mainClass = 'com.example.HotreloadApplication'
excludeDevtools = false
}
And upon a ./gradlew bootRepackage and a java -jar build/libs/hotreload-0.0.1-SNAPSHOT.jar, I can see that it fires up without devtools still. Upon opening the jar, I can also see that it is not included.
When moving springBootVersion back to '1.4.4.RELEASE' howeverm everything works absolutely as expected.
Any help would be greatly appreciated! I've included the entirety of my build.gradle following this message.
Many thanks,
Duncan
----- build.gradle ----
buildscript {
ext {
springBootVersion = '1.5.1.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
jar {
baseName = 'hotreload'
version = '0.0.1-SNAPSHOT'
}
sourceCompatibility = 1.8
repositories {
mavenCentral()
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-devtools')
testCompile('org.springframework.boot:spring-boot-starter-test')
}
bootRepackage {
mainClass = 'com.example.HotreloadApplication'
excludeDevtools = false
}
This question is superseded by this issue on the Spring Boot tracker that the author has cross-posted.
That's correct, thanks Stephane. A workaround is posted on the github ticket, by adding the following block to build.gradle:
springBoot {
excludeDevtools = false
}
With any luck, this bug should be fixed in 1.5.2.
Many thanks,
Duncan

GORM not bootstrapping from JAR

Replacing a persistence layer in legacy app with a JAR file using Spring, Hibernate and GORM. Methods like person.save() work fine when running agains project with Gradle etc. in project. However, after I build the fat jar and reference it with -cp my-big-fat-gorm.jar I get:
java.lang.IllegalStateException: Method on class [blah.Person] was
used outside of a Grails application. If running in the context of a
test using the mocking API or bootstrap Grails correctly.
Using Spring boot for Spring, Hibernate4 and GORM and build.gradle file show below...
apply plugin: 'java'
apply plugin: 'groovy'
apply plugin: 'application'
mainClassName = "blah.App"
jar {
baseName = 'blah-gorm'
version = '1.0-SNAPSHOT'
from {
configurations.compile.collect {
it.isDirectory() ? it : zipTree(it)
}
configurations.runtime.collect {
it.isDirectory() ? it : zipTree(it)
}
}
}
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
testCompile group: 'junit', name: 'junit', version: '4.8.2'
compile 'org.grails:gorm-hibernate4-spring-boot:1.1.0.RELEASE'
compile 'org.slf4j:slf4j-simple:1.7.7'
runtime 'com.h2database:h2:1.4.181'
}
Am I missing something in the JAR file creation that causes Spring boot to honor #Entity etc.?
Here is a GitHub project that illustrates this and should allow you to execute and see the same stuff I'm seeing.
https://github.com/twcrone/spring-gorm-jar
You don't have the Spring Boot Gradle plugin installed so you're not actually creating a fat JAR you need to add the following to your build.gradle file:
apply plugin: 'spring-boot'
buildscript {
ext {
springBootVersion = '1.1.0.M2'
groovyVersion = '2.3.2'
}
repositories {
mavenCentral()
maven {
url 'http://repo.spring.io/milestone'
}
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
With this in place doing gradle assemble and then java -jar ... results in bootstrapping GORM correctly

Resources