KEYCLOAK-11785 - Support for deferred initialization

This commit is contained in:
Dmitry Telegin
2019-10-17 15:20:11 +03:00
committed by Stian Thorgersen
parent e3fdfeb040
commit b68e8323ed
2 changed files with 81 additions and 48 deletions
@@ -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<Startup> loader = ServiceLoader.load(Startup.class);
Iterator<Startup> 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 {
}
}
@@ -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();
}
}