Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Fixes an issue where autoCommit is not restored properly in certain edge cases; ensures related proper Hibernate JTA settings are set by default #7741

Merged
merged 1 commit into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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