From 4ac3a1279a73d89a928e9f4084bcd9f2866f34ab Mon Sep 17 00:00:00 2001 From: Thomas Darimont Date: Thu, 8 Apr 2021 10:30:02 +0200 Subject: [PATCH] Avoid reflection when registering Spring-Boot as a Keycloak Platform Fixes #37 --- .../embedded/EmbeddedKeycloakConfig.java | 7 +- .../embedded/support/SpringBootPlatform.java | 69 ------------------- .../support/SpringBootPlatformProvider.java | 64 +++++++++++++++++ 3 files changed, 68 insertions(+), 72 deletions(-) delete mode 100644 embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatform.java create mode 100644 embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatformProvider.java diff --git a/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/EmbeddedKeycloakConfig.java b/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/EmbeddedKeycloakConfig.java index 78eb065..c781b29 100644 --- a/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/EmbeddedKeycloakConfig.java +++ b/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/EmbeddedKeycloakConfig.java @@ -3,13 +3,14 @@ import com.github.thomasdarimont.keycloak.embedded.support.DynamicJndiContextFactoryBuilder; import com.github.thomasdarimont.keycloak.embedded.support.KeycloakUndertowRequestFilter; import com.github.thomasdarimont.keycloak.embedded.support.SpringBootConfigProvider; -import com.github.thomasdarimont.keycloak.embedded.support.SpringBootPlatform; +import com.github.thomasdarimont.keycloak.embedded.support.SpringBootPlatformProvider; import lombok.extern.slf4j.Slf4j; import org.infinispan.configuration.parsing.ConfigurationBuilderHolder; import org.infinispan.configuration.parsing.ParserRegistry; import org.infinispan.manager.DefaultCacheManager; import org.jboss.resteasy.plugins.server.servlet.HttpServlet30Dispatcher; import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters; +import org.keycloak.platform.Platform; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.web.ServerProperties; @@ -39,8 +40,8 @@ protected EmbeddedKeycloakServer embeddedKeycloakServer(ServerProperties serverP @Bean @ConditionalOnMissingBean(name = "springBootPlatform") - protected SpringBootPlatform springBootPlatform() { - return new SpringBootPlatform(); + protected SpringBootPlatformProvider springBootPlatform() { + return (SpringBootPlatformProvider) Platform.getPlatform(); } @Bean diff --git a/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatform.java b/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatform.java deleted file mode 100644 index 8d1a1a5..0000000 --- a/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatform.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.github.thomasdarimont.keycloak.embedded.support; - -import com.google.auto.service.AutoService; -import org.keycloak.platform.Platform; -import org.keycloak.platform.PlatformProvider; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.ApplicationEvent; -import org.springframework.context.event.ContextStoppedEvent; -import org.springframework.context.event.SmartApplicationListener; -import org.springframework.util.ReflectionUtils; - -import java.lang.reflect.Field; -import java.util.Optional; - -public class SpringBootPlatform implements SmartApplicationListener { - - @Override - public boolean supportsEventType(Class eventType) { - return ApplicationReadyEvent.class.equals(eventType) || ContextStoppedEvent.class.equals(eventType); - } - - public void onApplicationEvent(ApplicationEvent event) { - - if (event instanceof ApplicationReadyEvent) { - runStartupHook(); - } else if (event instanceof ContextStoppedEvent) { - runShutdownHook(); - } - } - - protected void runShutdownHook() { - getPlatformField(Runnable.class, "shutdownHook").ifPresent(Runnable::run); - } - - protected void runStartupHook() { - getPlatformField(Runnable.class, "startupHook").ifPresent(Runnable::run); - } - - // TODO find better way to work around classloader issues - private Optional getPlatformField(Class type, String name) { - PlatformProvider p = Platform.getPlatform(); - Field field = ReflectionUtils.findField(p.getClass(), name); - ReflectionUtils.makeAccessible(field); - return Optional.ofNullable(type.cast(ReflectionUtils.getField(field, p))); - } - - @AutoService(PlatformProvider.class) - public static class Delegate implements PlatformProvider { - - private Runnable startupHook; - - private Runnable shutdownHook; - - @Override - public void onStartup(Runnable startupHook) { - this.startupHook = startupHook; - } - - @Override - public void onShutdown(Runnable shutdownHook) { - this.shutdownHook = shutdownHook; - } - - @Override - public void exit(Throwable cause) { - throw new RuntimeException(cause); - } - } -} diff --git a/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatformProvider.java b/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatformProvider.java new file mode 100644 index 0000000..5b9aa3f --- /dev/null +++ b/embedded-keycloak-server-spring-boot-support/src/main/java/com/github/thomasdarimont/keycloak/embedded/support/SpringBootPlatformProvider.java @@ -0,0 +1,64 @@ +package com.github.thomasdarimont.keycloak.embedded.support; + +import com.google.auto.service.AutoService; +import lombok.extern.slf4j.Slf4j; +import org.keycloak.platform.PlatformProvider; +import org.keycloak.services.ServicesLogger; +import org.springframework.boot.context.event.ApplicationReadyEvent; +import org.springframework.context.ApplicationEvent; +import org.springframework.context.event.ContextStoppedEvent; +import org.springframework.context.event.SmartApplicationListener; + +@Slf4j +@AutoService(PlatformProvider.class) +public class SpringBootPlatformProvider implements PlatformProvider, SmartApplicationListener { + + Runnable onStartup; + + Runnable onShutdown; + + @Override + public void onApplicationEvent(ApplicationEvent event) { + + if (event instanceof ApplicationReadyEvent) { + startup(); + } else if (event instanceof ContextStoppedEvent) { + shutdown(); + } + } + + @Override + public boolean supportsEventType(Class eventType) { + return ApplicationReadyEvent.class.equals(eventType) || ContextStoppedEvent.class.equals(eventType); + } + + @Override + public String getListenerId() { + return this.getClass().getName(); + } + + @Override + public void onStartup(@SuppressWarnings("hiding") Runnable onStartup) { + this.onStartup = onStartup; + } + + @Override + public void onShutdown(@SuppressWarnings("hiding") Runnable onShutdown) { + this.onShutdown = onShutdown; + } + + @Override + public void exit(Throwable cause) { + log.error("exit", cause); + ServicesLogger.LOGGER.fatal(cause); + throw new RuntimeException(cause); + } + + private void shutdown() { + this.onShutdown.run(); + } + + private void startup() { + this.onStartup.run(); + } +} \ No newline at end of file