diff --git a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java index f9a27887ccf..bb09efebc01 100644 --- a/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java +++ b/quarkus/deployment/src/main/java/org/keycloak/quarkus/deployment/KeycloakProcessor.java @@ -45,6 +45,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.logging.Handler; +import jakarta.inject.Singleton; import jakarta.persistence.Entity; import jakarta.persistence.PersistenceUnitTransactionType; @@ -93,6 +94,7 @@ import org.keycloak.quarkus.runtime.configuration.PropertyMappingInterceptor; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMapper; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; import org.keycloak.quarkus.runtime.configuration.mappers.WildcardPropertyMapper; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakHandlerChainCustomizer; import org.keycloak.quarkus.runtime.integration.resteasy.KeycloakTracingCustomizer; import org.keycloak.quarkus.runtime.logging.ClearMappedDiagnosticContextFilter; @@ -127,6 +129,7 @@ import io.quarkus.agroal.spi.JdbcDataSourceBuildItem; import io.quarkus.agroal.spi.JdbcDriverBuildItem; import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem; import io.quarkus.arc.deployment.BuildTimeConditionBuildItem; +import io.quarkus.arc.deployment.SyntheticBeanBuildItem; import io.quarkus.bootstrap.logging.InitialConfigurator; import io.quarkus.datasource.deployment.spi.DevServicesDatasourceResultBuildItem; import io.quarkus.datasource.runtime.DataSourcesBuildTimeConfig; @@ -681,7 +684,7 @@ class KeycloakProcessor { @Consume(ConfigBuildItem.class) @Consume(CryptoProviderInitBuildItem.class) @Produce(KeycloakSessionFactoryPreInitBuildItem.class) - void configureKeycloakSessionFactory(KeycloakRecorder recorder, List descriptors) { + SyntheticBeanBuildItem configureKeycloakSessionFactory(KeycloakRecorder recorder, List descriptors) { Map, Map>>> factories = new HashMap<>(); Map, String> defaultProviders = new HashMap<>(); Map preConfiguredProviders = new HashMap<>(); @@ -709,7 +712,10 @@ class KeycloakProcessor { } } - recorder.configSessionFactory(factories, defaultProviders, preConfiguredProviders, loadThemesFromClassPath()); + return SyntheticBeanBuildItem.configure(QuarkusKeycloakSessionFactory.class).scope(Singleton.class) + .unremovable() + .runtimeValue(recorder.createSessionFactory(factories, defaultProviders, preConfiguredProviders, + loadThemesFromClassPath())).done(); } private List loadThemesFromClassPath() { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java index c1282970190..8e434ac3343 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakMain.java @@ -33,6 +33,7 @@ import org.keycloak.quarkus.runtime.cli.command.DryRunMixin; import org.keycloak.quarkus.runtime.configuration.Configuration; import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource; import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; import io.quarkus.arc.Arc; @@ -146,7 +147,8 @@ public class KeycloakMain implements QuarkusApplication { public int run(String... args) throws Exception { if (COMMAND != null) { QuarkusKeycloakApplication application = Arc.container().instance(QuarkusKeycloakApplication.class).get(); - COMMAND.onStart(application); + QuarkusKeycloakSessionFactory sessionFactory = Arc.container().instance(QuarkusKeycloakSessionFactory.class).get(); + COMMAND.onStart(application, sessionFactory); } if (isTestLaunchMode() || isNonServerMode()) { // in test mode we exit immediately diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java index c621347a253..c30029cc0df 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/KeycloakRecorder.java @@ -58,6 +58,7 @@ import io.quarkus.agroal.DataSource; import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener; +import io.quarkus.runtime.RuntimeValue; import io.quarkus.runtime.annotations.Recorder; import io.vertx.core.Handler; import io.vertx.ext.web.RoutingContext; @@ -156,12 +157,12 @@ public class KeycloakRecorder { } } - public void configSessionFactory( + public RuntimeValue createSessionFactory( Map, Map>>> factories, Map, String> defaultProviders, Map preConfiguredProviders, List themes) { - QuarkusKeycloakSessionFactory.setInstance(new QuarkusKeycloakSessionFactory(factories, defaultProviders, preConfiguredProviders, themes)); + return new RuntimeValue(new QuarkusKeycloakSessionFactory(factories, defaultProviders, preConfiguredProviders, themes)); } public void setDefaultUserProfileConfiguration(UPConfig configuration) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/AbstractNonServerCommand.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/AbstractNonServerCommand.java index 571798460aa..f90c36018d2 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/AbstractNonServerCommand.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/AbstractNonServerCommand.java @@ -21,6 +21,7 @@ import java.util.EnumSet; import org.keycloak.common.util.Environment; import org.keycloak.config.OptionCategory; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; import picocli.CommandLine; @@ -44,7 +45,7 @@ public abstract class AbstractNonServerCommand extends AbstractAutoBuildCommand return super.isHiddenCategory(category) || hidden.contains(category); } - public void onStart(QuarkusKeycloakApplication application) { + public void onStart(QuarkusKeycloakApplication application, QuarkusKeycloakSessionFactory sessionFactory) { } @Override diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminService.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminService.java index c15d710e50b..c6adc4f9fb6 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminService.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminService.java @@ -20,11 +20,10 @@ package org.keycloak.quarkus.runtime.cli.command; import org.keycloak.common.util.IoUtils; import org.keycloak.config.BootstrapAdminOptions; import org.keycloak.config.OptionCategory; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.quarkus.runtime.cli.PropertyException; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; -import org.keycloak.services.resources.KeycloakApplication; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; @@ -99,9 +98,8 @@ public class BootstrapAdminService extends AbstractNonServerCommand { } @Override - public void onStart(QuarkusKeycloakApplication application) { + public void onStart(QuarkusKeycloakApplication application, QuarkusKeycloakSessionFactory sessionFactory) { //BootstrapAdmin bootstrap = spec.commandLine().getParent().getCommand(); - KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> application .createTemporaryMasterRealmAdminService(clientId, clientSecret, /* bootstrap.expiration, */ session)); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminUser.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminUser.java index ac1aa4a73f3..36e1d6a6a5b 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminUser.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/cli/command/BootstrapAdminUser.java @@ -20,11 +20,10 @@ package org.keycloak.quarkus.runtime.cli.command; import org.keycloak.common.util.IoUtils; import org.keycloak.config.BootstrapAdminOptions; import org.keycloak.config.OptionCategory; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.quarkus.runtime.cli.PropertyException; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.integration.jaxrs.QuarkusKeycloakApplication; -import org.keycloak.services.resources.KeycloakApplication; import picocli.CommandLine.ArgGroup; import picocli.CommandLine.Command; @@ -99,9 +98,8 @@ public class BootstrapAdminUser extends AbstractNonServerCommand { } @Override - public void onStart(QuarkusKeycloakApplication application) { + public void onStart(QuarkusKeycloakApplication application, QuarkusKeycloakSessionFactory sessionFactory) { //BootstrapAdmin bootstrap = spec.commandLine().getParent().getCommand(); - KeycloakSessionFactory sessionFactory = KeycloakApplication.getSessionFactory(); KeycloakModelUtils.runJobInTransaction(sessionFactory, session -> application .createTemporaryMasterRealmAdminUser(username, password, /* bootstrap.expiration, */ session)); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/QuarkusKeycloakSessionFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/QuarkusKeycloakSessionFactory.java index 01f8cd3256d..08b47303440 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/QuarkusKeycloakSessionFactory.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/QuarkusKeycloakSessionFactory.java @@ -25,36 +25,19 @@ import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.ProviderManagerRegistry; import org.keycloak.provider.Spi; import org.keycloak.quarkus.runtime.themes.QuarkusJarThemeProviderFactory; import org.keycloak.services.DefaultKeycloakSessionFactory; -import org.keycloak.services.resources.admin.fgap.AdminPermissions; import org.keycloak.theme.ClasspathThemeProviderFactory; public final class QuarkusKeycloakSessionFactory extends DefaultKeycloakSessionFactory { - public static QuarkusKeycloakSessionFactory getInstance() { - if (INSTANCE == null) { - INSTANCE = new QuarkusKeycloakSessionFactory(); - } - - return INSTANCE; - } - - public static void setInstance(QuarkusKeycloakSessionFactory instance) { - INSTANCE = instance; - } - - private static QuarkusKeycloakSessionFactory INSTANCE; - public QuarkusKeycloakSessionFactory( Map, Map>>> factories, Map, String> defaultProviders, Map preConfiguredProviders, List themes) { this.provider = defaultProviders; - serverStartupTimestamp = System.currentTimeMillis(); spis = factories.keySet(); for (Spi spi : spis) { @@ -79,17 +62,6 @@ public final class QuarkusKeycloakSessionFactory extends DefaultKeycloakSessionF } } - private QuarkusKeycloakSessionFactory() { - } - - @Override - public void init() { - initProviderFactories(); - AdminPermissions.registerListener(this); - // make the session factory ready for hot deployment - ProviderManagerRegistry.SINGLETON.setDeployer(this); - } - private ProviderFactory lookupProviderFactory(Class factoryClazz) { ProviderFactory factory; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/cdi/KeycloakBeanProducer.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/cdi/KeycloakBeanProducer.java index c1e3a152ed7..7b7fa7562e3 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/cdi/KeycloakBeanProducer.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/cdi/KeycloakBeanProducer.java @@ -20,8 +20,10 @@ package org.keycloak.quarkus.runtime.integration.cdi; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.RequestScoped; import jakarta.enterprise.inject.Disposes; +import jakarta.inject.Inject; import org.keycloak.models.KeycloakSession; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler; import org.keycloak.utils.KeycloakSessionUtil; @@ -31,9 +33,12 @@ import io.quarkus.arc.Unremovable; @Unremovable public class KeycloakBeanProducer implements TransactionalSessionHandler { + @Inject + QuarkusKeycloakSessionFactory factory; + @RequestScoped public KeycloakSession getKeycloakSession() { - return create(); + return factory.create(); } void dispose(@Disposes KeycloakSession session) { diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java index 7a27d22dd51..8805a3ceac1 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/integration/jaxrs/QuarkusKeycloakApplication.java @@ -30,11 +30,13 @@ import org.keycloak.quarkus.runtime.configuration.MicroProfileConfigProvider; import org.keycloak.quarkus.runtime.configuration.PropertyMappingInterceptor; import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.keycloak.quarkus.runtime.storage.database.jpa.QuarkusJpaConnectionProviderFactory; +import org.keycloak.services.DefaultKeycloakSessionFactory; import org.keycloak.services.ServicesLogger; import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.utils.StringUtil; +import io.quarkus.arc.Arc; import io.quarkus.runtime.Quarkus; import io.quarkus.runtime.ShutdownEvent; import io.quarkus.runtime.StartupEvent; @@ -43,10 +45,11 @@ import org.jboss.logging.Logger; import static org.keycloak.common.util.Environment.isDevMode; import static org.keycloak.common.util.Environment.isNonServerMode; +import static org.keycloak.quarkus.runtime.Environment.isTestLaunchMode; @ApplicationPath("/") @Blocking -public class QuarkusKeycloakApplication extends KeycloakApplication { +public class QuarkusKeycloakApplication extends KeycloakApplication { private static final String KEYCLOAK_ADMIN_ENV_VAR = "KEYCLOAK_ADMIN"; private static final String KEYCLOAK_ADMIN_PASSWORD_ENV_VAR = "KEYCLOAK_ADMIN_PASSWORD"; @@ -75,13 +78,8 @@ public class QuarkusKeycloakApplication extends KeycloakApplication call() { // JVM branch prediction may optimize this code and saves on reading a static volatile field if (bootstrapCompleted) { return ready(); } - if (KeycloakApplication.isBootstrapCompleted()) { + if (factory.isBootstrapCompleted()) { bootstrapCompleted = true; return ready(); - } + } return Uni.createFrom().item(builder().down().build()); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakClusterReadyHealthCheckProducer.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakClusterReadyHealthCheckProducer.java index 7a22d2e78b6..7de695fd54a 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakClusterReadyHealthCheckProducer.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/KeycloakClusterReadyHealthCheckProducer.java @@ -19,10 +19,11 @@ package org.keycloak.quarkus.runtime.services.health; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.context.Dependent; import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; import org.keycloak.connections.infinispan.InfinispanConnectionProvider; import org.keycloak.connections.infinispan.InfinispanConnectionProviderFactory; -import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import io.smallrye.health.api.AsyncHealthCheck; import org.eclipse.microprofile.health.Readiness; @@ -32,6 +33,8 @@ public class KeycloakClusterReadyHealthCheckProducer { private AsyncHealthCheck instance; private boolean ready; + @Inject + QuarkusKeycloakSessionFactory sessionFactory; @Produces @Readiness @@ -41,14 +44,13 @@ public class KeycloakClusterReadyHealthCheckProducer { // JVM branch prediction may optimize this code and saves on reading a static volatile field return instance; } - if (!KeycloakApplication.isBootstrapCompleted()) { + if (!sessionFactory.isBootstrapCompleted()) { return null; } synchronized (this) { if (ready) { return instance; } - var sessionFactory = KeycloakApplication.getSessionFactory(); var factory = (InfinispanConnectionProviderFactory) sessionFactory.getProviderFactory(InfinispanConnectionProvider.class); if (factory.isClusterHealthSupported()) { instance = new KeycloakClusterReadyHealthCheck(factory); diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/transaction/TransactionalSessionHandler.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/transaction/TransactionalSessionHandler.java index c7808b915fb..7d558143227 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/transaction/TransactionalSessionHandler.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/transaction/TransactionalSessionHandler.java @@ -18,8 +18,6 @@ package org.keycloak.quarkus.runtime.transaction; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; -import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; /** *

A {@link TransactionalSessionHandler} is responsible for managing transaction sessions and its lifecycle. Its subtypes @@ -28,16 +26,6 @@ import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; */ public interface TransactionalSessionHandler { - /** - * Creates a {@link KeycloakSession}. - * - * @return a keycloak session - */ - default KeycloakSession create() { - KeycloakSessionFactory sessionFactory = QuarkusKeycloakSessionFactory.getInstance(); - return sessionFactory.create(); - } - /** * begin a transaction if possible * diff --git a/services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java b/services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java deleted file mode 100644 index e8ed910befe..00000000000 --- a/services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.provider; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public interface ProviderManagerDeployer { - void deploy(ProviderManager pm); - void undeploy(ProviderManager pm); -} diff --git a/services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java b/services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java deleted file mode 100644 index fb8b759fdc2..00000000000 --- a/services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2016 Red Hat, Inc. and/or its affiliates - * and other contributors as indicated by the @author tags. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.keycloak.provider; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -/** - * @author Bill Burke - * @version $Revision: 1 $ - */ -public class ProviderManagerRegistry { - public static final ProviderManagerRegistry SINGLETON = new ProviderManagerRegistry(); - protected List preBoot = Collections.synchronizedList(new LinkedList<>()); - protected AtomicReference deployerRef = new AtomicReference<>(); - - public synchronized void setDeployer(ProviderManagerDeployer deployer) { - this.deployerRef.set(deployer); - } - - public synchronized void deploy(ProviderManager pm) { - ProviderManagerDeployer deployer = getDeployer(); - if (deployer == null) { - preBoot.add(pm); - } else { - deployer.deploy(pm); - } - - } - - public synchronized void undeploy(ProviderManager pm) { - preBoot.remove(pm); - ProviderManagerDeployer deployer = getDeployer(); - if (deployer != null) { - deployer.undeploy(pm); - } - } - - private ProviderManagerDeployer getDeployer() { - return deployerRef.get(); - } - - public List getPreBoot() { - return preBoot; - } -} diff --git a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java index 6a86b9ba158..22acd08b584 100755 --- a/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultKeycloakSessionFactory.java @@ -44,21 +44,18 @@ import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.ThemeManager; import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.InvalidationHandler; -import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.Provider; import org.keycloak.provider.ProviderEvent; import org.keycloak.provider.ProviderEventListener; import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderManager; -import org.keycloak.provider.ProviderManagerDeployer; -import org.keycloak.provider.ProviderManagerRegistry; import org.keycloak.provider.Spi; import org.keycloak.services.resources.admin.fgap.AdminPermissions; import org.keycloak.theme.ThemeManagerFactory; import org.jboss.logging.Logger; -public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFactory, ProviderManagerDeployer { +public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFactory { private static final Logger logger = Logger.getLogger(DefaultKeycloakSessionFactory.class); @@ -68,10 +65,12 @@ public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFa protected CopyOnWriteArrayList listeners = new CopyOnWriteArrayList<>(); // TODO: Likely should be changed to int and use Time.currentTime() to be compatible with all our "time" reps - protected long serverStartupTimestamp; + protected long serverStartupTimestamp = System.currentTimeMillis(); protected ComponentFactoryProviderFactory componentFactoryPF; + private volatile boolean bootstrapCompleted; + @Override public void register(ProviderEventListener listener) { listeners.add(listener); @@ -90,34 +89,7 @@ public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFa } public void init() { - serverStartupTimestamp = System.currentTimeMillis(); - - ProviderManager pm = new ProviderManager(KeycloakDeploymentInfo.create().services(), getClass().getClassLoader(), Config.scope().getArray("providers")); - for (Spi spi : pm.loadSpis()) { - if (spi.isEnabled()) { - spis.add(spi); - } - } - - factoriesMap = loadFactories(pm); - - synchronized (ProviderManagerRegistry.SINGLETON) { - for (ProviderManager manager : ProviderManagerRegistry.SINGLETON.getPreBoot()) { - Map, Map> factoryMap = loadFactories(manager); - for (Map.Entry, Map> entry : factoryMap.entrySet()) { - Map factories = factoriesMap.get(entry.getKey()); - if (factories == null) { - factoriesMap.put(entry.getKey(), entry.getValue()); - } else { - factories.putAll(entry.getValue()); - } - } - } - checkProvider(); - initProviderFactories(); - // make the session factory ready for hot deployment - ProviderManagerRegistry.SINGLETON.setDeployer(this); - } + initProviderFactories(); AdminPermissions.registerListener(this); } @@ -180,7 +152,6 @@ public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFa } - @Override public void deploy(ProviderManager pm) { registerNewSpis(pm); @@ -240,7 +211,6 @@ public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFa } } - @Override public void undeploy(ProviderManager pm) { logger.debug("undeploy"); // we make a copy to avoid concurrent access exceptions @@ -454,8 +424,6 @@ public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFa @Override public void close() { - ProviderManagerRegistry.SINGLETON.setDeployer(null); - // Create a tree-structure to represent reverse relation of ProviderFactory#dependsOn to Providers Map, Node>> nodes = new HashMap<>(); for (Map.Entry, Map> f : factoriesMap.entrySet()) { @@ -520,4 +488,12 @@ public abstract class DefaultKeycloakSessionFactory implements KeycloakSessionFa this.componentFactoryPF = (ComponentFactoryProviderFactory) getProviderFactory(ComponentFactoryProvider.class); } + public void setBootstrapCompleted() { + this.bootstrapCompleted = true; + } + + public boolean isBootstrapCompleted() { + return this.bootstrapCompleted; + } + } 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 86f65ad2cd4..55491cfbc3b 100644 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -29,11 +29,11 @@ import org.keycloak.common.crypto.CryptoIntegration; import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.ExportImportManager; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.dblock.DBLockManager; import org.keycloak.models.dblock.DBLockProvider; import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.PostMigrationEvent; +import org.keycloak.services.DefaultKeycloakSessionFactory; import org.keycloak.services.managers.ApplianceBootstrap; import org.jboss.logging.Logger; @@ -43,15 +43,13 @@ import org.jboss.logging.Logger; * @version $Revision: 1 $ * */ -public abstract class KeycloakApplication extends Application { +public abstract class KeycloakApplication extends Application { private static final String KC_TMPDIR = "kc.io.tmpdir"; private static final Logger logger = Logger.getLogger(KeycloakApplication.class); - private static volatile KeycloakSessionFactory sessionFactory; - // Set to true when bootstrap is completed. It never changes back to false. - private static volatile boolean bootstrapCompleted = false; + private static volatile DefaultKeycloakSessionFactory sessionFactory; public KeycloakApplication() { try { @@ -86,12 +84,11 @@ public abstract class KeycloakApplication ex protected void startup() { Profile.getInstance().logUnsupportedFeatures(); CryptoIntegration.init(KeycloakApplication.class.getClassLoader()); - var ksf = createSessionFactory(); - sessionFactory = ksf; + KeycloakApplication.sessionFactory = createSessionFactory(); if (supportsAsyncInitialization()) { final var executor = Executors.newSingleThreadExecutor(); - CompletableFuture.runAsync(() -> runBootstrap(ksf), executor) + CompletableFuture.runAsync(() -> runBootstrap(KeycloakApplication.sessionFactory), executor) .exceptionally(throwable -> { exit(throwable); return null; @@ -100,18 +97,17 @@ public abstract class KeycloakApplication ex return; } - runBootstrap(ksf); + runBootstrap(KeycloakApplication.sessionFactory); } protected boolean supportsAsyncInitialization() { return false; } - // synchronized to prevent shutdown while running bootstrapping - private synchronized void runBootstrap(KSF keycloakSessionFactory) { + private synchronized void runBootstrap(DefaultKeycloakSessionFactory keycloakSessionFactory) { var startTime = System.nanoTime(); - initKeycloakSessionFactory(keycloakSessionFactory); + keycloakSessionFactory.init(); setTransactionTimeout(keycloakSessionFactory); var exportImportManager = KeycloakModelUtils.runJobInTransactionWithResult(keycloakSessionFactory, session -> { DBLockManager dbLockManager = new DBLockManager(session); @@ -131,14 +127,14 @@ public abstract class KeycloakApplication ex } resetTransactionTimeout(keycloakSessionFactory); - bootstrapCompleted = true; keycloakSessionFactory.publish(new PostMigrationEvent(keycloakSessionFactory)); + keycloakSessionFactory.setBootstrapCompleted(); var duration = Duration.ofNanos(System.nanoTime() - startTime); logger.infof("Bootstrap completed in %f seconds", (double) duration.toMillis() / 1000); } - protected int getTransactionTimeout(KSF sessionFactory) { + protected int getTransactionTimeout(DefaultKeycloakSessionFactory sessionFactory) { return Math.toIntExact(TimeUnit.MINUTES.toSeconds(5)); } @@ -146,6 +142,7 @@ public abstract class KeycloakApplication ex protected synchronized void shutdown() { if (sessionFactory != null) { sessionFactory.close(); + sessionFactory = null; } } @@ -178,19 +175,13 @@ public abstract class KeycloakApplication ex protected abstract void initAndStart(); - protected abstract KSF createSessionFactory(); + protected abstract DefaultKeycloakSessionFactory createSessionFactory(); - protected abstract void initKeycloakSessionFactory(KSF ksf); - - public static KeycloakSessionFactory getSessionFactory() { + public static DefaultKeycloakSessionFactory getSessionFactory() { return sessionFactory; } - public static boolean isBootstrapCompleted() { - return bootstrapCompleted; - } - - private void setTransactionTimeout(KSF keycloakSessionFactory) { + private void setTransactionTimeout(DefaultKeycloakSessionFactory keycloakSessionFactory) { try { var transactionTimeoutSeconds = getTransactionTimeout(keycloakSessionFactory); KeycloakModelUtils.setTransactionLimit(keycloakSessionFactory, transactionTimeoutSeconds); @@ -199,7 +190,7 @@ public abstract class KeycloakApplication ex } } - private void resetTransactionTimeout(KSF keycloakSessionFactory) { + private void resetTransactionTimeout(DefaultKeycloakSessionFactory keycloakSessionFactory) { try { KeycloakModelUtils.setTransactionLimit(keycloakSessionFactory, 0); } catch (Exception e) { diff --git a/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakApplication.java b/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakApplication.java index 836350ea5da..af6c0da56c4 100644 --- a/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakApplication.java +++ b/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakApplication.java @@ -27,6 +27,7 @@ import org.keycloak.common.util.MultiSiteUtils; import org.keycloak.exportimport.ExportImportManager; import org.keycloak.models.KeycloakSession; import org.keycloak.models.utils.KeycloakModelUtils; +import org.keycloak.services.DefaultKeycloakSessionFactory; import org.keycloak.services.error.KcUnrecognizedPropertyExceptionHandler; import org.keycloak.services.error.KeycloakErrorHandler; import org.keycloak.services.error.KeycloakMismatchedInputExceptionHandler; @@ -41,7 +42,7 @@ import org.keycloak.services.resources.WelcomeResource; import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.services.util.ObjectMapperResolver; -public class ResteasyKeycloakApplication extends KeycloakApplication { +public class ResteasyKeycloakApplication extends KeycloakApplication { protected Set singletons = new HashSet<>(); protected Set> classes = new HashSet<>(); @@ -90,15 +91,10 @@ public class ResteasyKeycloakApplication extends KeycloakApplication getFactoriesDependentOnFeature(Map factoriesDisabled, Map factoriesEnabled) { @@ -153,4 +154,19 @@ public class FeatureDeployerUtil { DefaultProviderLoader loader = new DefaultProviderLoader(di, classLoader); loader.loadSpis().forEach(pm::load); } + + static void deploy(ProviderManager pm) { + DefaultKeycloakSessionFactory deployer = KeycloakApplication.getSessionFactory(); + if (deployer == null) { + throw new IllegalStateException("No active KeycloakApplication"); + } + deployer.deploy(pm); + } + + static void undeploy(ProviderManager pm) { + DefaultKeycloakSessionFactory deployer = KeycloakApplication.getSessionFactory(); + if (deployer != null) { + deployer.undeploy(pm); + } + } } diff --git a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java index 9ea63ed8e42..8af598d5117 100644 --- a/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java +++ b/testsuite/integration-arquillian/servers/auth-server/undertow/src/main/java/org/keycloak/testsuite/arquillian/undertow/KeycloakOnUndertow.java @@ -213,7 +213,7 @@ public class KeycloakOnUndertow implements DeployableContainer