Skip to content

Commit

Permalink
Fixes an issue where autoCommit is not restored properly in certain e…
Browse files Browse the repository at this point in the history
…dge cases; ensures related proper Hibernate JTA settings are set by default (helidon-io#7741)

Signed-off-by: Laird Nelson <[email protected]>
  • Loading branch information
ljnelson authored Oct 6, 2023
1 parent e020211 commit f86aadc
Show file tree
Hide file tree
Showing 5 changed files with 203 additions and 101 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, 2021 Oracle and/or its affiliates.
* Copyright (c) 2019, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -15,15 +15,23 @@
*/
package io.helidon.integrations.cdi.hibernate;

import java.lang.System.Logger;
import java.util.Objects;
import java.util.Properties;

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.event.Observes;
import jakarta.inject.Inject;
import jakarta.persistence.spi.PersistenceUnitInfo;
import jakarta.transaction.TransactionManager;
import jakarta.transaction.UserTransaction;
import org.hibernate.engine.jndi.spi.JndiService;
import org.hibernate.engine.transaction.jta.platform.internal.AbstractJtaPlatform;

import static java.lang.System.Logger.Level.DEBUG;
import static org.hibernate.cfg.AvailableSettings.CONNECTION_HANDLING;
import static org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode.DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION;

/**
* An {@link AbstractJtaPlatform} that is an {@link ApplicationScoped}
* CDI managed bean that supplies {@link TransactionManager} and
Expand All @@ -35,74 +43,114 @@
*/
@ApplicationScoped
public class CDISEJtaPlatform extends AbstractJtaPlatform {
private final TransactionManager transactionManager;

private final UserTransaction userTransaction;
private static final Logger LOGGER = System.getLogger(CDISEJtaPlatform.class.getName());

private static final long serialVersionUID = 1L;

private transient TransactionManager transactionManager;

private transient UserTransaction userTransaction;

/**
* Creates a new {@link CDISEJtaPlatform}.
*
* @deprecated <a href="https://jakarta.ee/specifications/cdi/3.0/jakarta-cdi-spec-3.0#unproxyable">Required by the
* CDI specification</a> and not intended for end-user use.
*/
@Deprecated
CDISEJtaPlatform() {
super();
this.transactionManager = null;
this.userTransaction = null;
}

private static final long serialVersionUID = 1L;
/**
* Creates a new {@link CDISEJtaPlatform}.
*
* @param transactionManager the {@link TransactionManager} to use;
* must not be {@code null}
*
* @param userTransaction the {@link UserTransaction} to use; must
* not be {@code null}
*
* @exception NullPointerException if either {@code
* transactionManager} or {@code userTransaction} is {@code null}
*/
@Inject
public CDISEJtaPlatform(TransactionManager transactionManager,
UserTransaction userTransaction) {
super();
this.transactionManager = Objects.requireNonNull(transactionManager);
this.userTransaction = Objects.requireNonNull(userTransaction);
}

/**
* Creates a new {@link CDISEJtaPlatform}.
*
* @param transactionManager the {@link TransactionManager} to use;
* must not be {@code null}
*
* @param userTransaction the {@link UserTransaction} to use; must
* not be {@code null}
*
* @exception NullPointerException if either {@code
* transactionManager} or {@code userTransaction} is {@code null}
*/
@Inject
public CDISEJtaPlatform(final TransactionManager transactionManager,
final UserTransaction userTransaction) {
super();
this.transactionManager = Objects.requireNonNull(transactionManager);
this.userTransaction = Objects.requireNonNull(userTransaction);
}
/**
* Throws an {@link UnsupportedOperationException} when invoked.
*
* @return (not applicable)
*
* @exception UnsupportedOperationException when invoked
*/
@Override
protected JndiService jndiService() {
throw new UnsupportedOperationException();
}

/**
* Throws an {@link UnsupportedOperationException} when invoked.
*
* @return (not applicable)
*
* @exception UnsupportedOperationException when invoked
*/
@Override
protected JndiService jndiService() {
throw new UnsupportedOperationException();
}
/**
* Returns the {@link UserTransaction} instance supplied at
* {@linkplain #CDISEJtaPlatform(TransactionManager,
* UserTransaction) construction time}.
*
* <p>This method never returns {@code null}.</p>
*
* @return a non-{@code null} {@link UserTransaction}
*
* @see #CDISEJtaPlatform(TransactionManager, UserTransaction)
*/
@Override
protected UserTransaction locateUserTransaction() {
return this.userTransaction;
}

/**
* Returns the {@link UserTransaction} instance supplied at
* {@linkplain #CDISEJtaPlatform(TransactionManager,
* UserTransaction) construction time}.
*
* <p>This method never returns {@code null}.</p>
*
* @return a non-{@code null} {@link UserTransaction}
*
* @see #CDISEJtaPlatform(TransactionManager, UserTransaction)
*/
@Override
protected UserTransaction locateUserTransaction() {
return this.userTransaction;
}
/**
* Returns the {@link TransactionManager} instance supplied at
* {@linkplain #CDISEJtaPlatform(TransactionManager,
* UserTransaction) construction time}.
*
* <p>This method never returns {@code null}.</p>
*
* @return a non-{@code null} {@link TransactionManager}
*
* @see #CDISEJtaPlatform(TransactionManager, UserTransaction)
*/
@Override
protected TransactionManager locateTransactionManager() {
return this.transactionManager;
}

/**
* Returns the {@link TransactionManager} instance supplied at
* {@linkplain #CDISEJtaPlatform(TransactionManager,
* UserTransaction) construction time}.
*
* <p>This method never returns {@code null}.</p>
*
* @return a non-{@code null} {@link TransactionManager}
*
* @see #CDISEJtaPlatform(TransactionManager, UserTransaction)
*/
@Override
protected TransactionManager locateTransactionManager() {
return this.transactionManager;
}
/**
* Customizes the supplied {@link PersistenceUnitInfo}, when it is fired as a CDI event by, for example, the {@code
* io.helidon.integrations.cdi.jpa.PersistenceExtension} portable extension, by ensuring that certain important
* Hibernate properties are always set on the persistence unit.
*
* @param pui the {@link PersistenceUnitInfo} to customize; must not be {@code null}
*
* @exception NullPointerException if {@code pui} is {@code null}
*
* @see
* org.hibernate.resource.jdbc.spi.PhysicalConnectionHandlingMode#DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION
*/
private static void customizePersistenceUnitInfo(@Observes PersistenceUnitInfo pui) {
Properties p = pui.getProperties();
if (p != null && p.getProperty(CONNECTION_HANDLING) == null && p.get(CONNECTION_HANDLING) == null) {
if (LOGGER.isLoggable(DEBUG)) {
LOGGER.log(DEBUG, "Setting " + CONNECTION_HANDLING + " property to "
+ DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION
+ " on persistence unit " + pui.getPersistenceUnitName());
}
p.setProperty(CONNECTION_HANDLING, DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION.toString());
}
}

}
14 changes: 8 additions & 6 deletions integrations/cdi/hibernate-cdi/src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2021 Oracle and/or its affiliates.
* Copyright (c) 2020, 2023 Oracle and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -23,12 +23,14 @@
*
* @see io.helidon.integrations.cdi.hibernate.CDISEJtaPlatform
*/
@SuppressWarnings({"requires-automatic", "requires-transitive-automatic"})
module io.helidon.integrations.cdi.hibernate {
requires jakarta.transaction;
requires java.sql;
requires jakarta.inject;
requires jakarta.cdi;
requires org.hibernate.orm.core;

requires transitive jakarta.cdi;
requires transitive jakarta.inject;
requires jakarta.persistence;
requires transitive jakarta.transaction;
requires transitive org.hibernate.orm.core;

exports io.helidon.integrations.cdi.hibernate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import jakarta.annotation.Priority;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.context.spi.CreationalContext;
import jakarta.enterprise.event.Event;
import jakarta.enterprise.event.Observes;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.CreationException;
Expand Down Expand Up @@ -151,6 +152,9 @@ public final class PersistenceExtension implements Extension {

private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];

private static final TypeLiteral<Event<PersistenceUnitInfoBean>> EVENT_PERSISTENCEUNITINFOBEAN_TYPELITERAL =
new TypeLiteral<>() {};

private static final Logger LOGGER = Logger.getLogger(PersistenceExtension.class.getName());


Expand Down Expand Up @@ -997,7 +1001,7 @@ private void processPersistenceXmls(AfterBeanDiscovery event,
}
Supplier<? extends DataSourceProvider> dataSourceProviderSupplier =
() -> bm.createInstance().select(DataSourceProvider.class).get();
PersistenceUnitInfo solePui = null;
PersistenceUnitInfoBean solePui = null;
Supplier<? extends ClassLoader> tempClassLoaderSupplier =
classLoader instanceof URLClassLoader ucl ? () -> new URLClassLoader(ucl.getURLs()) : () -> classLoader;
for (int puCount = 0; persistenceXmlUrls.hasMoreElements();) {
Expand Down Expand Up @@ -1032,6 +1036,7 @@ private void processPersistenceXmls(AfterBeanDiscovery event,
if (unitName == null || unitName.isBlank()) {
unitName = DEFAULT_PERSISTENCE_UNIT_NAME;
}
Named qualifier = NamedLiteral.of(unitName);
// Provide support for, e.g.:
// @Inject
// @Named("test")
Expand All @@ -1040,8 +1045,8 @@ private void processPersistenceXmls(AfterBeanDiscovery event,
.beanClass(PersistenceUnitInfoBean.class)
.addTransitiveTypeClosure(PersistenceUnitInfoBean.class)
.scope(Singleton.class)
.qualifiers(NamedLiteral.of(unitName))
.createWith(cc -> pui);
.qualifiers(qualifier)
.produceWith(i -> producePersistenceUnitInfoBean(i, pui, qualifier));
addPersistenceProviderBeanIfAbsent(event, pui, providers);
if (puCount == 0) {
solePui = pui;
Expand All @@ -1056,17 +1061,18 @@ private void processPersistenceXmls(AfterBeanDiscovery event,
assert soleUnitName != null;
assert !soleUnitName.isBlank();
if (!soleUnitName.equals(DEFAULT_PERSISTENCE_UNIT_NAME)) {
PersistenceUnitInfo pui = solePui;
PersistenceUnitInfoBean pui = solePui;
// Provide support for, e.g.:
// @Inject
// @Named("__DEFAULT__"))
// private PersistenceUnitInfo persistenceUnitInfo;
Named qualifier = NamedLiteral.of(DEFAULT_PERSISTENCE_UNIT_NAME);
event.addBean()
.beanClass(PersistenceUnitInfoBean.class)
.addTransitiveTypeClosure(PersistenceUnitInfoBean.class)
.scope(Singleton.class)
.qualifiers(NamedLiteral.of(DEFAULT_PERSISTENCE_UNIT_NAME))
.createWith(cc -> pui);
.qualifiers(qualifier)
.produceWith(i -> producePersistenceUnitInfoBean(i, pui, qualifier));
}
}
}
Expand All @@ -1087,6 +1093,7 @@ private void processImplicitPersistenceUnits(AfterBeanDiscovery event, Iterable<
}
}
}
Named qualifier = NamedLiteral.of(unitName);
// Provide support for, e.g.:
// @Inject
// @Named("test")
Expand All @@ -1095,8 +1102,8 @@ private void processImplicitPersistenceUnits(AfterBeanDiscovery event, Iterable<
.beanClass(PersistenceUnitInfoBean.class)
.addTransitiveTypeClosure(PersistenceUnitInfoBean.class)
.scope(Singleton.class)
.qualifiers(NamedLiteral.of(unitName))
.createWith(cc -> pui);
.qualifiers(qualifier)
.produceWith(i -> producePersistenceUnitInfoBean(i, pui, qualifier));
addPersistenceProviderBeanIfAbsent(event, pui, providers);
if (puCount == 0) {
solePui = pui;
Expand All @@ -1112,6 +1119,7 @@ private void processImplicitPersistenceUnits(AfterBeanDiscovery event, Iterable<
assert !soleUnitName.isBlank();
if (!soleUnitName.equals(DEFAULT_PERSISTENCE_UNIT_NAME)) {
PersistenceUnitInfoBean pui = solePui;
Named qualifier = NamedLiteral.of(DEFAULT_PERSISTENCE_UNIT_NAME);
// Provide support for, e.g.:
// @Inject
// @Named("__DEFAULT__")
Expand All @@ -1120,8 +1128,8 @@ private void processImplicitPersistenceUnits(AfterBeanDiscovery event, Iterable<
.beanClass(PersistenceUnitInfoBean.class)
.addTransitiveTypeClosure(PersistenceUnitInfoBean.class)
.scope(Singleton.class)
.qualifiers(NamedLiteral.of(DEFAULT_PERSISTENCE_UNIT_NAME))
.createWith(cc -> pui);
.qualifiers(qualifier)
.produceWith(i -> producePersistenceUnitInfoBean(i, pui, qualifier));
}
}
}
Expand Down Expand Up @@ -1316,6 +1324,14 @@ private static void disposeJtaExtendedEntityManager(JtaExtendedEntityManager em,
containerManagedSelectionQualifiers);
}

private static PersistenceUnitInfoBean producePersistenceUnitInfoBean(Instance<Object> instance,
PersistenceUnitInfoBean pui,
Annotation... qualifiers) {
// Permit arbitrary customization of the PersistenceUnitInfoBean right before it is produced.
instance.select(EVENT_PERSISTENCEUNITINFOBEAN_TYPELITERAL, qualifiers).get().fire(pui);
return pui;
}

private static EntityManagerFactory produceEntityManagerFactory(Instance<Object> instance) {
BeanAttributes<EntityManagerFactory> ba = instance.select(BEAN_ENTITYMANAGERFACTORY_TYPELITERAL).get();
Set<Annotation> selectionQualifiers = new HashSet<>();
Expand Down
Loading

0 comments on commit f86aadc

Please sign in to comment.