diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 8bd2965f327..be5d23bf6f3 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; -import org.jboss.resteasy.core.Dispatcher; import org.keycloak.Config; import org.keycloak.common.util.Resteasy; import org.keycloak.common.util.SystemEnvProperties; @@ -64,7 +63,6 @@ import javax.servlet.ServletContext; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.ws.rs.core.Application; -import javax.ws.rs.core.Context; import javax.ws.rs.core.UriInfo; import java.io.File; import java.io.FileInputStream; @@ -75,11 +73,14 @@ import java.net.URI; import java.net.URL; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; +import java.util.ServiceLoader; import java.util.Set; import java.util.StringTokenizer; +import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -106,8 +107,12 @@ public class KeycloakApplication extends Application { protected KeycloakSessionFactory sessionFactory; protected String contextPath; + private AtomicBoolean bootstrapAdminUser = new AtomicBoolean(false); + public KeycloakApplication() { + try { + logger.debugv("RestEasy provider: {0}", Resteasy.getProvider().getClass().getName()); ServletContext context = Resteasy.getContextData(ServletContext.class); @@ -115,7 +120,7 @@ public class KeycloakApplication extends Application { if ("true".equals(context.getInitParameter(KEYCLOAK_EMBEDDED))) { embedded = true; } - + loadConfig(context); this.contextPath = context.getContextPath(); @@ -135,53 +140,72 @@ public class KeycloakApplication extends Application { classes.add(KeycloakErrorHandler.class); singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false")))); + singletons.add(new WelcomeResource()); - ExportImportManager[] exportImportManager = new ExportImportManager[1]; + init(this::startup); - KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - - @Override - public void run(KeycloakSession lockSession) { - DBLockManager dbLockManager = new DBLockManager(lockSession); - dbLockManager.checkForcedUnlock(); - DBLockProvider dbLock = dbLockManager.getDBLock(); - dbLock.waitForLock(DBLockProvider.Namespace.KEYCLOAK_BOOT); - try { - exportImportManager[0] = migrateAndBootstrap(); - } finally { - dbLock.releaseLock(); - } - } - - }); - - - if (exportImportManager[0].isRunExport()) { - exportImportManager[0].runExport(); - } - - AtomicBoolean bootstrapAdminUser = new AtomicBoolean(false); - KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { - - @Override - public void run(KeycloakSession session) { - boolean shouldBootstrapAdmin = new ApplianceBootstrap(session).isNoMasterUser(); - bootstrapAdminUser.set(shouldBootstrapAdmin); - } - - }); - - sessionFactory.publish(new PostMigrationEvent()); - - singletons.add(new WelcomeResource(bootstrapAdminUser.get())); - - setupScheduledTasks(sessionFactory); } catch (Throwable t) { if (!embedded) { exit(1); } throw t; } + + } + + private void init(Runnable function) { + + ServiceLoader loader = ServiceLoader.load(Startup.class); + Iterator iterator = loader.iterator(); + + if (iterator.hasNext()) { + iterator.next().execute(function); + } else { + function.run(); + } + + } + + protected void startup() { + + ExportImportManager[] exportImportManager = new ExportImportManager[1]; + + KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { + + @Override + public void run(KeycloakSession lockSession) { + DBLockManager dbLockManager = new DBLockManager(lockSession); + dbLockManager.checkForcedUnlock(); + DBLockProvider dbLock = dbLockManager.getDBLock(); + dbLock.waitForLock(DBLockProvider.Namespace.KEYCLOAK_BOOT); + try { + exportImportManager[0] = migrateAndBootstrap(); + } finally { + dbLock.releaseLock(); + } + } + + }); + + + if (exportImportManager[0].isRunExport()) { + exportImportManager[0].runExport(); + } + + KeycloakModelUtils.runJobInTransaction(sessionFactory, new KeycloakSessionTask() { + + @Override + public void run(KeycloakSession session) { + boolean shouldBootstrapAdmin = new ApplianceBootstrap(session).isNoMasterUser(); + bootstrapAdminUser.set(shouldBootstrapAdmin); + } + + }); + + sessionFactory.publish(new PostMigrationEvent()); + + setupScheduledTasks(sessionFactory); + } // Migrate model, bootstrap master realm, import realms and create admin user. This is done with acquired dbLock @@ -314,14 +338,14 @@ public class KeycloakApplication extends Application { throw new RuntimeException("Failed to load config", e); } } - + private static String loadDmrConfig(ServletContext context) { String dmrConfig = context.getInitParameter(KEYCLOAK_CONFIG_PARAM_NAME); if (dmrConfig == null) return null; ModelNode dmrConfigNode = ModelNode.fromString(dmrConfig); if (dmrConfigNode.asPropertyList().isEmpty()) return null; - + // note that we need to resolve expressions BEFORE we convert to JSON return dmrConfigNode.resolve().toJSONString(true); } @@ -361,6 +385,10 @@ public class KeycloakApplication extends Application { return singletons; } + boolean isBootstrap() { + return bootstrapAdminUser.get(); + } + public void importRealms() { String files = System.getProperty("keycloak.import"); if (files != null) { @@ -490,4 +518,7 @@ public class KeycloakApplication extends Application { }.start(); } + public static interface Startup extends Executor { + } + } diff --git a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java index 6093879645e..c8387ab2372 100755 --- a/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java +++ b/services/src/main/java/org/keycloak/services/resources/WelcomeResource.java @@ -68,7 +68,7 @@ public class WelcomeResource { private static final String KEYCLOAK_STATE_CHECKER = "WELCOME_STATE_CHECKER"; - private boolean bootstrap; + private boolean bootstrap = true; @Context protected HttpHeaders headers; @@ -76,8 +76,10 @@ public class WelcomeResource { @Context private KeycloakSession session; - public WelcomeResource(boolean bootstrap) { - this.bootstrap = bootstrap; + @Context + private KeycloakApplication application; + + public WelcomeResource() { } /** @@ -228,7 +230,7 @@ public class WelcomeResource { } private void checkBootstrap() { - if (bootstrap) { + if (bootstrap && application.isBootstrap()) { bootstrap = new ApplianceBootstrap(session).isNoMasterUser(); } }