From c11a6e3ef0469739689921c1fe93ec51aaa69c44 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 a5e5c9a063..2464db67b1 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.Optional; 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.deployment.builditem.StaticInitConfigSourceProviderBuildItem; 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.logging.Logger; 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; @@ -122,6 +126,7 @@ import io.quarkus.deployment.annotations.Record; 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; @@ -193,14 +198,27 @@ class KeycloakProcessor { * @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, @@ -247,7 +265,7 @@ class KeycloakProcessor { @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<>(); @@ -255,15 +273,21 @@ class KeycloakProcessor { 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()); @@ -271,6 +295,25 @@ class KeycloakProcessor { 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 c9bc8dd017..fe20250f10 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 class KeycloakRecorder { 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 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 2fe9e119c1..88fc48e1f1 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 @@ package org.keycloak.quarkus.runtime.integration.jaxrs; 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 @@ public class QuarkusKeycloakApplication extends KeycloakApplication { return !WelcomeResource.class.isInstance(o); } - @Inject - Instance entityManagerFactory; - @Override protected void startup() { - forceEntityManagerInitialization(); initializeKeycloakSessionFactory(); setupScheduledTasks(sessionFactory); createAdminUser(); @@ -76,12 +69,6 @@ public class QuarkusKeycloakApplication extends KeycloakApplication { 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 0000000000..56f71d9ddc --- /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 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 0000000000..ff8387e5d7 --- /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 32115025ce..4b97f11c2b 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.Map; 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.DBLockManager; 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 @@ import org.keycloak.util.JsonSerialization; /** * @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 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP 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 final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP 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 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP @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 final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP } @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 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP 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 @@ public final class QuarkusJpaConnectionProviderFactory implements JpaConnectionP 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)); - } }