From 970d1d9da849e2df2b1a54c6da40989c9579cf56 Mon Sep 17 00:00:00 2001
From: Pedro Igor
Date: Mon, 7 Mar 2022 08:49:10 -0300
Subject: [PATCH] Allow using an additional persistence unit and datasource
Closes #10579
---
.../quarkus/deployment/KeycloakProcessor.java | 61 ++++++++--
.../quarkus/runtime/KeycloakRecorder.java | 32 ++++-
.../jaxrs/QuarkusKeycloakApplication.java | 13 --
.../AbstractJpaConnectionProviderFactory.java | 115 ++++++++++++++++++
.../NamedJpaConnectionProviderFactory.java | 57 +++++++++
.../QuarkusJpaConnectionProviderFactory.java | 90 ++++----------
6 files changed, 278 insertions(+), 90 deletions(-)
create mode 100644 quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java
create mode 100644 quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/NamedJpaConnectionProviderFactory.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 a5cf596b2c59..f0f4a26c2f1f 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
@@ -48,6 +48,7 @@
import java.util.Properties;
import java.util.function.Consumer;
import java.util.function.Function;
+import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@@ -65,6 +66,7 @@
import io.quarkus.hibernate.orm.deployment.AdditionalJpaModelBuildItem;
import io.quarkus.hibernate.orm.deployment.HibernateOrmConfig;
import io.quarkus.hibernate.orm.deployment.PersistenceXmlDescriptorBuildItem;
+import io.quarkus.hibernate.orm.deployment.integration.HibernateOrmIntegrationRuntimeConfiguredBuildItem;
import io.quarkus.resteasy.server.common.deployment.ResteasyDeploymentCustomizerBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ProfileManager;
@@ -84,6 +86,8 @@
import org.jboss.resteasy.plugins.server.servlet.ResteasyContextParameters;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.keycloak.Config;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.connections.jpa.JpaConnectionSpi;
import org.keycloak.quarkus.runtime.QuarkusProfile;
import org.keycloak.quarkus.runtime.configuration.PersistedConfigSource;
import org.keycloak.quarkus.runtime.configuration.QuarkusPropertiesConfigSource;
@@ -121,6 +125,7 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.vertx.http.deployment.FilterBuildItem;
+import org.keycloak.quarkus.runtime.storage.database.jpa.NamedJpaConnectionProviderFactory;
import org.keycloak.representations.provider.ScriptProviderDescriptor;
import org.keycloak.representations.provider.ScriptProviderMetadata;
import org.keycloak.quarkus.runtime.integration.web.NotFoundHandler;
@@ -191,14 +196,27 @@ FeatureBuildItem getFeature() {
* @param descriptors
*/
@BuildStep
- void configureHibernate(HibernateOrmConfig config,
+ @Record(ExecutionTime.RUNTIME_INIT)
+ void configurePersistenceUnits(HibernateOrmConfig config,
List descriptors,
List jdbcDataSources,
BuildProducer additionalJpaModel,
- CombinedIndexBuildItem indexBuildItem) {
- ParsedPersistenceXmlDescriptor descriptor = descriptors.get(0).getDescriptor();
- configureJpaProperties(descriptor, config, jdbcDataSources);
- configureJpaModel(descriptor, indexBuildItem);
+ CombinedIndexBuildItem indexBuildItem,
+ BuildProducer runtimeConfigured,
+ KeycloakRecorder recorder) {
+ for (PersistenceXmlDescriptorBuildItem item : descriptors) {
+ ParsedPersistenceXmlDescriptor descriptor = item.getDescriptor();
+
+ if ("keycloak-default".equals(descriptor.getName())) {
+ configureJpaProperties(descriptor, config, jdbcDataSources);
+ configureJpaModel(descriptor, indexBuildItem);
+ } else {
+ Properties properties = descriptor.getProperties();
+ // register a listener for customizing the unit configuration at runtime
+ runtimeConfigured.produce(new HibernateOrmIntegrationRuntimeConfiguredBuildItem("keycloak", descriptor.getName())
+ .setInitListener(recorder.createUnitListener(properties.getProperty(AvailableSettings.DATASOURCE))));
+ }
+ }
}
private void configureJpaProperties(ParsedPersistenceXmlDescriptor descriptor, HibernateOrmConfig config,
@@ -245,7 +263,7 @@ private boolean isCustomJpaModel(String targetName) {
@Consume(RuntimeConfigSetupCompleteBuildItem.class)
@Record(ExecutionTime.RUNTIME_INIT)
@BuildStep
- KeycloakSessionFactoryPreInitBuildItem configureProviders(KeycloakRecorder recorder) {
+ KeycloakSessionFactoryPreInitBuildItem configureProviders(KeycloakRecorder recorder, List descriptors) {
Profile.setInstance(new QuarkusProfile());
Map, Map>>> factories = new HashMap<>();
Map, String> defaultProviders = new HashMap<>();
@@ -253,15 +271,21 @@ KeycloakSessionFactoryPreInitBuildItem configureProviders(KeycloakRecorder recor
for (Entry, Map>> entry : loadFactories(preConfiguredProviders)
.entrySet()) {
- checkProviders(entry.getKey(), entry.getValue(), defaultProviders);
+ Spi spi = entry.getKey();
+
+ checkProviders(spi, entry.getValue(), defaultProviders);
for (Entry, Map> value : entry.getValue().entrySet()) {
for (ProviderFactory factory : value.getValue().values()) {
- factories.computeIfAbsent(entry.getKey(),
+ factories.computeIfAbsent(spi,
key -> new HashMap<>())
- .computeIfAbsent(entry.getKey().getProviderClass(), aClass -> new HashMap<>()).put(factory.getId(),factory.getClass());
+ .computeIfAbsent(spi.getProviderClass(), aClass -> new HashMap<>()).put(factory.getId(),factory.getClass());
}
}
+
+ if (spi instanceof JpaConnectionSpi) {
+ configureUserDefinedPersistenceUnits(descriptors, factories, preConfiguredProviders, spi);
+ }
}
recorder.configSessionFactory(factories, defaultProviders, preConfiguredProviders, Environment.isRebuild());
@@ -269,6 +293,25 @@ KeycloakSessionFactoryPreInitBuildItem configureProviders(KeycloakRecorder recor
return new KeycloakSessionFactoryPreInitBuildItem();
}
+ private void configureUserDefinedPersistenceUnits(List descriptors,
+ Map, Map>>> factories,
+ Map preConfiguredProviders, Spi spi) {
+ descriptors.stream()
+ .map(PersistenceXmlDescriptorBuildItem::getDescriptor)
+ .map(ParsedPersistenceXmlDescriptor::getName)
+ .filter(Predicate.not("keycloak-default"::equals)).forEach(new Consumer() {
+ @Override
+ public void accept(String unitName) {
+ NamedJpaConnectionProviderFactory factory = new NamedJpaConnectionProviderFactory();
+
+ factory.setUnitName(unitName);
+
+ factories.get(spi).get(JpaConnectionProvider.class).put(unitName, NamedJpaConnectionProviderFactory.class);
+ preConfiguredProviders.put(unitName, factory);
+ }
+ });
+ }
+
/**
* Register the custom {@link org.eclipse.microprofile.config.spi.ConfigSource} implementations.
*
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 c9bc8dd017f9..fe20250f10cb 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
@@ -17,19 +17,24 @@
package org.keycloak.quarkus.runtime;
+import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Map;
+import java.util.function.BiConsumer;
+import io.agroal.api.AgroalDataSource;
+import io.quarkus.agroal.DataSource;
+import io.quarkus.arc.Arc;
+import io.quarkus.arc.InstanceHandle;
+import io.quarkus.hibernate.orm.runtime.integration.HibernateOrmIntegrationRuntimeInitListener;
import liquibase.Scope;
-import org.infinispan.configuration.parsing.ConfigurationBuilderHolder;
-import org.infinispan.configuration.parsing.ParserRegistry;
-import org.infinispan.jboss.marshalling.core.JBossUserMarshaller;
+
+import org.hibernate.cfg.AvailableSettings;
import org.infinispan.manager.DefaultCacheManager;
import io.quarkus.smallrye.metrics.runtime.SmallRyeMetricsHandler;
import io.vertx.core.Handler;
import io.vertx.ext.web.RoutingContext;
import org.keycloak.common.Profile;
-import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
import org.keycloak.quarkus.runtime.storage.database.liquibase.FastServiceLocator;
import org.keycloak.provider.Provider;
@@ -95,4 +100,23 @@ public Handler createMetricsHandler(String path) {
metricsHandler.setMetricsPath(path);
return metricsHandler;
}
+
+ public HibernateOrmIntegrationRuntimeInitListener createUnitListener(String name) {
+ return new HibernateOrmIntegrationRuntimeInitListener() {
+ @Override
+ public void contributeRuntimeProperties(BiConsumer propertyCollector) {
+ InstanceHandle instance = Arc.container().instance(
+ AgroalDataSource.class, new DataSource() {
+ @Override public Class extends Annotation> annotationType() {
+ return DataSource.class;
+ }
+
+ @Override public String value() {
+ return name;
+ }
+ });
+ propertyCollector.accept(AvailableSettings.DATASOURCE, instance.get());
+ }
+ };
+ }
}
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 2fe9e119c1b3..88fc48e1f12d 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
@@ -20,9 +20,6 @@
import java.util.Set;
import java.util.stream.Collectors;
-import javax.enterprise.inject.Instance;
-import javax.inject.Inject;
-import javax.persistence.EntityManagerFactory;
import javax.ws.rs.ApplicationPath;
import org.keycloak.Config;
@@ -47,12 +44,8 @@ private static boolean filterSingletons(Object o) {
return !WelcomeResource.class.isInstance(o);
}
- @Inject
- Instance entityManagerFactory;
-
@Override
protected void startup() {
- forceEntityManagerInitialization();
initializeKeycloakSessionFactory();
setupScheduledTasks(sessionFactory);
createAdminUser();
@@ -76,12 +69,6 @@ private void initializeKeycloakSessionFactory() {
sessionFactory.publish(new PostMigrationEvent());
}
- private void forceEntityManagerInitialization() {
- // also forces an initialization of the entity manager so that providers don't need to wait for any initialization logic
- // when first creating an entity manager
- entityManagerFactory.get().createEntityManager().close();
- }
-
private void createAdminUser() {
String adminUserName = System.getenv(KEYCLOAK_ADMIN_ENV_VAR);
String adminPassword = System.getenv(KEYCLOAK_ADMIN_PASSWORD_ENV_VAR);
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java
new file mode 100644
index 000000000000..56f71d9ddcac
--- /dev/null
+++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/AbstractJpaConnectionProviderFactory.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021 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.quarkus.runtime.storage.database.jpa;
+
+import java.lang.annotation.Annotation;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Optional;
+import javax.enterprise.inject.Instance;
+import javax.persistence.EntityManager;
+import javax.persistence.EntityManagerFactory;
+import javax.persistence.EntityTransaction;
+import javax.persistence.SynchronizationType;
+import org.hibernate.internal.SessionFactoryImpl;
+import org.keycloak.Config;
+import org.keycloak.connections.jpa.JpaConnectionProviderFactory;
+import org.keycloak.connections.jpa.JpaKeycloakTransaction;
+import org.keycloak.connections.jpa.PersistenceExceptionConverter;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.quarkus.runtime.configuration.Configuration;
+
+import io.quarkus.arc.Arc;
+import io.quarkus.hibernate.orm.PersistenceUnit;
+
+public abstract class AbstractJpaConnectionProviderFactory implements JpaConnectionProviderFactory {
+
+ protected Config.Scope config;
+ protected Boolean xaEnabled;
+ protected EntityManagerFactory entityManagerFactory;
+
+ @Override
+ public Connection getConnection() {
+ SessionFactoryImpl entityManagerFactory = this.entityManagerFactory.unwrap(SessionFactoryImpl.class);
+
+ try {
+ return entityManagerFactory.getJdbcServices().getBootstrapJdbcConnectionAccess().obtainConnection();
+ } catch (SQLException cause) {
+ throw new RuntimeException("Failed to obtain JDBC connection", cause);
+ }
+ }
+
+ @Override
+ public String getSchema() {
+ return Configuration.getRawValue("kc.db-schema");
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ this.config = config;
+ xaEnabled = "xa".equals(Configuration.getRawValue("kc.transaction-xa-enabled"));
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ entityManagerFactory = getEntityManagerFactory();
+ }
+
+ @Override
+ public void close() {
+ if (entityManagerFactory != null) {
+ entityManagerFactory.close();
+ }
+ }
+
+ protected abstract EntityManagerFactory getEntityManagerFactory();
+
+ protected Optional getEntityManagerFactory(String unitName) {
+ Instance instance = Arc.container().select(EntityManagerFactory.class, new PersistenceUnit() {
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return PersistenceUnit.class;
+ }
+
+ @Override
+ public String value() {
+ return unitName;
+ }
+ });
+
+ if (instance.isResolvable()) {
+ return Optional.of(instance.get());
+ }
+
+ return Optional.empty();
+ }
+
+ protected EntityManager createEntityManager(EntityManagerFactory emf, KeycloakSession session) {
+ EntityManager entityManager;
+
+ if (xaEnabled) {
+ entityManager = PersistenceExceptionConverter.create(session, emf.createEntityManager(SynchronizationType.SYNCHRONIZED));
+ } else {
+ entityManager = PersistenceExceptionConverter.create(session, emf.createEntityManager());
+ }
+
+ return entityManager;
+ }
+}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/NamedJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/NamedJpaConnectionProviderFactory.java
new file mode 100644
index 000000000000..ff8387e5d73a
--- /dev/null
+++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/NamedJpaConnectionProviderFactory.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 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.quarkus.runtime.storage.database.jpa;
+
+import java.util.function.Supplier;
+import javax.persistence.EntityManagerFactory;
+import org.keycloak.connections.jpa.DefaultJpaConnectionProvider;
+import org.keycloak.connections.jpa.JpaConnectionProvider;
+import org.keycloak.models.KeycloakSession;
+
+public final class NamedJpaConnectionProviderFactory extends AbstractJpaConnectionProviderFactory {
+
+ private String unitName;
+
+ @Override
+ public JpaConnectionProvider create(KeycloakSession session) {
+ return new DefaultJpaConnectionProvider(createEntityManager(entityManagerFactory, session));
+ }
+
+ @Override
+ protected EntityManagerFactory getEntityManagerFactory() {
+ return getEntityManagerFactory(unitName).orElseThrow(new Supplier() {
+ @Override
+ public IllegalStateException get() {
+ return new IllegalStateException("Could not resolve named EntityManagerFactory [" + unitName + "]");
+ }
+ });
+ }
+
+ public String getUnitName() {
+ return unitName;
+ }
+
+ public void setUnitName(String unitName) {
+ this.unitName = unitName;
+ }
+
+ @Override
+ public String getId() {
+ return unitName;
+ }
+}
diff --git a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java
index 32115025ced5..4b97f11c2bb1 100644
--- a/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java
+++ b/quarkus/runtime/src/main/java/org/keycloak/quarkus/runtime/storage/database/jpa/QuarkusJpaConnectionProviderFactory.java
@@ -35,25 +35,22 @@
import java.util.StringTokenizer;
import javax.enterprise.inject.Instance;
-import javax.enterprise.inject.spi.CDI;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
-import javax.persistence.SynchronizationType;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import com.fasterxml.jackson.core.type.TypeReference;
+
+import io.quarkus.arc.Arc;
import io.quarkus.runtime.Quarkus;
-import org.hibernate.internal.SessionFactoryImpl;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.ServerStartupError;
import org.keycloak.common.Version;
import org.keycloak.connections.jpa.DefaultJpaConnectionProvider;
import org.keycloak.connections.jpa.JpaConnectionProvider;
-import org.keycloak.connections.jpa.JpaConnectionProviderFactory;
-import org.keycloak.connections.jpa.PersistenceExceptionConverter;
import org.keycloak.connections.jpa.updater.JpaUpdaterProvider;
import org.keycloak.connections.jpa.util.JpaUtils;
import org.keycloak.exportimport.ExportImportManager;
@@ -65,7 +62,6 @@
import org.keycloak.models.dblock.DBLockProvider;
import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.ServerInfoAwareProviderFactory;
-import org.keycloak.quarkus.runtime.configuration.Configuration;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.ServicesLogger;
@@ -78,7 +74,7 @@
/**
* @author Stian Thorgersen
*/
-public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionProviderFactory, ServerInfoAwareProviderFactory {
+public class QuarkusJpaConnectionProviderFactory extends AbstractJpaConnectionProviderFactory implements ServerInfoAwareProviderFactory {
public static final String QUERY_PROPERTY_PREFIX = "kc.query.";
private static final Logger logger = Logger.getLogger(QuarkusJpaConnectionProviderFactory.class);
@@ -88,22 +84,13 @@ enum MigrationStrategy {
UPDATE, VALIDATE, MANUAL
}
- private EntityManagerFactory emf;
- private Config.Scope config;
private Map operationalInfo;
private KeycloakSessionFactory factory;
@Override
public JpaConnectionProvider create(KeycloakSession session) {
logger.trace("Create QuarkusJpaConnectionProvider");
- return new DefaultJpaConnectionProvider(createEntityManager(session));
- }
-
- @Override
- public void close() {
- if (emf != null) {
- emf.close();
- }
+ return new DefaultJpaConnectionProvider(createEntityManager(entityManagerFactory, session));
}
@Override
@@ -111,16 +98,11 @@ public String getId() {
return "quarkus";
}
- @Override
- public void init(Config.Scope config) {
- this.config = config;
- }
-
- private void addSpecificNamedQueries(KeycloakSession session, Connection connection) {
- EntityManager em = createEntityManager(session);
+ private void addSpecificNamedQueries(KeycloakSession session) {
+ EntityManager em = createEntityManager(entityManagerFactory, session);
try {
- Map unitProperties = emf.getProperties();
+ Map unitProperties = entityManagerFactory.getProperties();
for (Map.Entry entry : unitProperties.entrySet()) {
if (entry.getKey().startsWith(QUERY_PROPERTY_PREFIX)) {
@@ -134,21 +116,15 @@ private void addSpecificNamedQueries(KeycloakSession session, Connection connect
@Override
public void postInit(KeycloakSessionFactory factory) {
+ super.postInit(factory);
this.factory = factory;
- Instance instance = CDI.current().select(EntityManagerFactory.class);
-
- if (!instance.isResolvable()) {
- throw new RuntimeException("Failed to resolve " + EntityManagerFactory.class + " from Quarkus runtime");
- }
-
- emf = instance.get();
KeycloakSession session = factory.create();
boolean schemaChanged;
try (Connection connection = getConnection()) {
createOperationalInfo(connection);
- addSpecificNamedQueries(session, connection);
+ addSpecificNamedQueries(session);
schemaChanged = createOrUpdateSchema(getSchema(), connection, session);
} catch (SQLException cause) {
throw new RuntimeException("Failed to update database.", cause);
@@ -166,19 +142,14 @@ public void postInit(KeycloakSessionFactory factory) {
}
@Override
- public Connection getConnection() {
- SessionFactoryImpl entityManagerFactory = emf.unwrap(SessionFactoryImpl.class);
+ protected EntityManagerFactory getEntityManagerFactory() {
+ Instance instance = Arc.container().select(EntityManagerFactory.class);
- try {
- return entityManagerFactory.getJdbcServices().getBootstrapJdbcConnectionAccess().obtainConnection();
- } catch (SQLException cause) {
- throw new RuntimeException("Failed to obtain JDBC connection", cause);
+ if (instance.isResolvable()) {
+ return instance.get();
}
- }
- @Override
- public String getSchema() {
- return Configuration.getRawValue("kc.db-schema");
+ return getEntityManagerFactory("keycloak-default").orElseThrow(() -> new IllegalStateException("Failed to resolve the default entity manager factory"));
}
@Override
@@ -241,19 +212,19 @@ private ExportImportManager createMasterRealm(ExportImportManager exportImportMa
try {
session.getTransactionManager().begin();
- JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup) factory
- .getProviderFactory(JtaTransactionManagerLookup.class);
- if (lookup != null) {
- if (lookup.getTransactionManager() != null) {
- try {
- Transaction transaction = lookup.getTransactionManager().getTransaction();
- logger.debugv("bootstrap current transaction? {0}", transaction != null);
- if (transaction != null) {
- logger.debugv("bootstrap current transaction status? {0}", transaction.getStatus());
- }
- } catch (SystemException e) {
- throw new RuntimeException(e);
+
+ if (xaEnabled) {
+ JtaTransactionManagerLookup lookup = (JtaTransactionManagerLookup) factory
+ .getProviderFactory(JtaTransactionManagerLookup.class);
+
+ try {
+ Transaction transaction = lookup.getTransactionManager().getTransaction();
+ logger.debugv("bootstrap current transaction? {0}", transaction != null);
+ if (transaction != null) {
+ logger.debugv("bootstrap current transaction status? {0}", transaction.getStatus());
}
+ } catch (SystemException e) {
+ throw new RuntimeException(e);
}
}
@@ -514,13 +485,4 @@ private void export(Connection connection, String schema, File databaseUpdateFil
dbLock2.releaseLock();
}
}
-
- private EntityManager createEntityManager(KeycloakSession session) {
- // we need to auto join the transaction, hence the synchronized type
- // ideally, we should leverage how hibernate-orm creates the entity manager
- // but that breaks us, mainly due to flush which is always set to always
- // as per hibernate guys, we should consider how JTASessionOpener creates entity managers
- // but that brings lot of details that we need to investigate further
- return PersistenceExceptionConverter.create(session, emf.createEntityManager(SynchronizationType.SYNCHRONIZED));
- }
}