mirror of
https://github.com/keycloak/keycloak.git
synced 2026-05-20 08:00:11 -05:00
KEYCLOAK-11785 - Support for deferred initialization
This commit is contained in:
committed by
Stian Thorgersen
parent
e3fdfeb040
commit
b68e8323ed
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user