From da83307278eeedf800f87999df50a60963867555 Mon Sep 17 00:00:00 2001 From: Steve Hawkins Date: Thu, 19 Mar 2026 18:40:59 -0400 Subject: [PATCH] fix: combining / removing static and initialization logic closes: #46917 Signed-off-by: Steve Hawkins --- .../quarkus/deployment/KeycloakProcessor.java | 10 ++- .../quarkus/runtime/KeycloakMain.java | 4 +- .../quarkus/runtime/KeycloakRecorder.java | 5 +- .../cli/command/AbstractNonServerCommand.java | 3 +- .../cli/command/BootstrapAdminService.java | 6 +- .../cli/command/BootstrapAdminUser.java | 6 +- .../QuarkusKeycloakSessionFactory.java | 28 --------- .../integration/cdi/KeycloakBeanProducer.java | 7 ++- .../jaxrs/QuarkusKeycloakApplication.java | 18 +++--- .../runtime/services/BootstrapFilter.java | 8 ++- .../health/BoostrapReadyHealthCheck.java | 10 ++- ...ycloakClusterReadyHealthCheckProducer.java | 8 ++- .../TransactionalSessionHandler.java | 12 ---- .../provider/ProviderManagerDeployer.java | 26 -------- .../provider/ProviderManagerRegistry.java | 62 ------------------- .../DefaultKeycloakSessionFactory.java | 50 ++++----------- .../resources/KeycloakApplication.java | 38 +++++------- .../resteasy/ResteasyKeycloakApplication.java | 10 +-- .../ResteasyKeycloakSessionFactory.java | 19 ++++++ .../testsuite/util/FeatureDeployerUtil.java | 22 ++++++- .../undertow/KeycloakOnUndertow.java | 2 +- .../keycloak/testsuite/KeycloakServer.java | 2 +- 22 files changed, 123 insertions(+), 233 deletions(-) delete mode 100644 services/src/main/java/org/keycloak/provider/ProviderManagerDeployer.java delete mode 100644 services/src/main/java/org/keycloak/provider/ProviderManagerRegistry.java 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 f9a27887ccf6..bb09efebc017 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.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.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.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 @@ private void configureDefaultPersistenceUnitEntities(ParsedPersistenceXmlDescrip @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 @@ void configureKeycloakSessionFactory(KeycloakRecorder recorder, 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 c12829701909..8e434ac33436 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.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 static void start(Picocli picocli, AbstractNonServerCommand command, Exec 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 c621347a253c..c30029cc0df5 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.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 void configureLiquibase(Map> services) { } } - 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 571798460aa0..f90c36018d24 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 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 boolean isHiddenCategory(OptionCategory category) { 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 c15d710e50bc..c6adc4f9fb6e 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 @@ 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 @@ private String getFromEnv(String envVar) { } @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 ac1aa4a73f31..36e1d6a6a5bb 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 @@ 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 @@ private String getFromEnv(String envVar) { } @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 01f8cd3256dc..08b47303440b 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.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 QuarkusKeycloakSessionFactory( } } - 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 c1e3a152ed7f..7b7fa7562e34 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 @@ 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 @@ @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 7a27d22dd51a..8805a3ceac19 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.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 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 @@ void onShutdownEvent(@Observes ShutdownEvent event) { } @Override - public QuarkusKeycloakSessionFactory createSessionFactory() { - return QuarkusKeycloakSessionFactory.getInstance(); - } - - @Override - protected void initKeycloakSessionFactory(QuarkusKeycloakSessionFactory quarkusKeycloakSessionFactory) { - quarkusKeycloakSessionFactory.init(); + public DefaultKeycloakSessionFactory createSessionFactory() { + return Arc.container().instance(QuarkusKeycloakSessionFactory.class).get(); } @Override @@ -116,11 +114,11 @@ protected boolean supportsAsyncInitialization() { .map(Boolean::parseBoolean) .orElse(Boolean.TRUE); // skip async bootstrap in dev and non-server mode - return !isDevMode() && !isNonServerMode() && asyncBootstrap; + return !isDevMode() && !isNonServerMode() && !isTestLaunchMode() && asyncBootstrap; } @Override - protected int getTransactionTimeout(QuarkusKeycloakSessionFactory sessionFactory) { + protected int getTransactionTimeout(DefaultKeycloakSessionFactory sessionFactory) { return ((QuarkusJpaConnectionProviderFactory) sessionFactory.getProviderFactory(JpaConnectionProvider.class)).getMigrationTransactionTimeout(); } diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/BootstrapFilter.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/BootstrapFilter.java index be75760a6fae..1ff961efca73 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/BootstrapFilter.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/BootstrapFilter.java @@ -1,12 +1,13 @@ package org.keycloak.quarkus.runtime.services; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; import jakarta.ws.rs.container.ContainerRequestContext; import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; -import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import org.jboss.resteasy.reactive.server.ServerRequestFilter; @@ -19,6 +20,9 @@ public class BootstrapFilter { private final long startup; private boolean ready; + @Inject + QuarkusKeycloakSessionFactory factory; + public BootstrapFilter() { startup = System.currentTimeMillis(); } @@ -29,7 +33,7 @@ public Response filter(ContainerRequestContext ignored) { // JVM branch prediction may optimize this code and saves on reading a static volatile field return null; } - if (KeycloakApplication.isBootstrapCompleted()) { + if (factory.isBootstrapCompleted()) { // Return null to continue the request chain normally ready = true; return null; diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/BoostrapReadyHealthCheck.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/BoostrapReadyHealthCheck.java index 3a36d6956cec..023099333dc0 100644 --- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/BoostrapReadyHealthCheck.java +++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/services/health/BoostrapReadyHealthCheck.java @@ -18,8 +18,9 @@ package org.keycloak.quarkus.runtime.services.health; import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; -import org.keycloak.services.resources.KeycloakApplication; +import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory; import io.smallrye.health.api.AsyncHealthCheck; import io.smallrye.mutiny.Uni; @@ -37,16 +38,19 @@ public class BoostrapReadyHealthCheck implements AsyncHealthCheck { private static final HealthCheckResponse UP = builder().up().build(); private boolean bootstrapCompleted; + @Inject + QuarkusKeycloakSessionFactory factory; + @Override public Uni 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 7a22d2e78b64..7de695fd54ad 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 @@ 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 AsyncHealthCheck createHealthCheck() { // 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 c7808b915fbb..7d5581432279 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 @@ */ 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 e8ed910befe5..000000000000 --- 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 fb8b759fdc23..000000000000 --- 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 6a86b9ba1586..22acd08b584e 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.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 void publish(ProviderEvent event) { } 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 @@ protected Map, Map> getFactor } - @Override public void deploy(ProviderManager pm) { registerNewSpis(pm); @@ -240,7 +211,6 @@ private void registerNewSpis(ProviderManager pm) { } } - @Override public void undeploy(ProviderManager pm) { logger.debug("undeploy"); // we make a copy to avoid concurrent access exceptions @@ -454,8 +424,6 @@ Class getProviderClass(String providerClassName) { @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 @@ protected void updateComponentFactoryProviderFactory() { 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 98e4c509f6af..a8044ccb7928 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.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 @@ * @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 @@ protected void initTmpDirectory() { 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,17 +97,17 @@ protected void startup() { return; } - runBootstrap(ksf); + runBootstrap(KeycloakApplication.sessionFactory); } protected boolean supportsAsyncInitialization() { return false; } - private void runBootstrap(KSF keycloakSessionFactory) { + private 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); @@ -130,20 +127,21 @@ private void runBootstrap(KSF keycloakSessionFactory) { } 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)); } protected void shutdown() { if (sessionFactory != null) { sessionFactory.close(); + sessionFactory = null; } } @@ -176,19 +174,13 @@ protected ExportImportManager bootstrap(KeycloakSession session) { 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); @@ -197,7 +189,7 @@ private void setTransactionTimeout(KSF keycloakSessionFactory) { } } - 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 836350ea5da8..af6c0da56c45 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.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.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 Set getSingletons() { } @Override - protected ResteasyKeycloakSessionFactory createSessionFactory() { + protected DefaultKeycloakSessionFactory createSessionFactory() { return new ResteasyKeycloakSessionFactory(); } - @Override - protected void initKeycloakSessionFactory(ResteasyKeycloakSessionFactory resteasyKeycloakSessionFactory) { - resteasyKeycloakSessionFactory.init(); - } - @Override protected void createTemporaryAdmin(KeycloakSession session) { // do nothing diff --git a/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakSessionFactory.java b/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakSessionFactory.java index 94984320a9bf..f6207f2c8744 100644 --- a/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakSessionFactory.java +++ b/services/src/test/java/org/keycloak/services/resteasy/ResteasyKeycloakSessionFactory.java @@ -17,11 +17,30 @@ package org.keycloak.services.resteasy; +import org.keycloak.Config; import org.keycloak.models.KeycloakSession; +import org.keycloak.provider.KeycloakDeploymentInfo; +import org.keycloak.provider.ProviderManager; +import org.keycloak.provider.Spi; import org.keycloak.services.DefaultKeycloakSessionFactory; public class ResteasyKeycloakSessionFactory extends DefaultKeycloakSessionFactory { + @Override + public void init() { + 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); + + checkProvider(); + super.init(); + } + @Override public KeycloakSession create() { return new ResteasyKeycloakSession(this); diff --git a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/FeatureDeployerUtil.java b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/FeatureDeployerUtil.java index 45badf044573..2fbaa4df712f 100644 --- a/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/FeatureDeployerUtil.java +++ b/testsuite/integration-arquillian/servers/auth-server/services/testsuite-providers/src/main/java/org/keycloak/testsuite/util/FeatureDeployerUtil.java @@ -33,9 +33,10 @@ import org.keycloak.provider.KeycloakDeploymentInfo; import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderManager; -import org.keycloak.provider.ProviderManagerRegistry; import org.keycloak.provider.Spi; import org.keycloak.services.DefaultKeycloakSession; +import org.keycloak.services.DefaultKeycloakSessionFactory; +import org.keycloak.services.resources.KeycloakApplication; import org.jboss.logging.Logger; @@ -77,7 +78,7 @@ public static void deployFactoriesAfterFeatureEnabled(Profile.Feature feature) { manager = new ProviderManager(di, FeatureDeployerUtil.class.getClassLoader(), Collections.singleton(new TestsuiteProviderLoader(di))); deployersCache.put(feature, manager); } - ProviderManagerRegistry.SINGLETON.deploy(manager); + deploy(manager); } public static void undeployFactoriesAfterFeatureDisabled(Profile.Feature feature) { @@ -95,7 +96,7 @@ public static void undeployFactoriesAfterFeatureDisabled(Profile.Feature feature loadFactories(manager); deployersCache.put(feature, manager); } - ProviderManagerRegistry.SINGLETON.undeploy(manager); + undeploy(manager); } private static Map getFactoriesDependentOnFeature(Map factoriesDisabled, Map factoriesEnabled) { @@ -153,4 +154,19 @@ private static void loadFactories(ProviderManager pm) { 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 9ea63ed8e426..8af598d5117e 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 void start() throws LifecycleException { DeploymentInfo di = createAuthServerDeploymentInfo(); undertow.deploy(di); - sessionFactory = (DefaultKeycloakSessionFactory) KeycloakApplication.getSessionFactory(); + sessionFactory = KeycloakApplication.getSessionFactory(); registerScriptProviders(sessionFactory); diff --git a/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java index 9ad573aad0eb..3a600cbcbcde 100755 --- a/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java +++ b/testsuite/utils/src/main/java/org/keycloak/testsuite/KeycloakServer.java @@ -457,7 +457,7 @@ public void release() { server.deploy(di); - sessionFactory = (DefaultKeycloakSessionFactory) KeycloakApplication.getSessionFactory(); + sessionFactory = KeycloakApplication.getSessionFactory(); registerScriptProviders(sessionFactory);