From 70325702de9d109f87e3eb28e91b53a36fcdb51e Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Wed, 4 Oct 2023 09:28:57 -0700 Subject: [PATCH 1/6] Switches default JPA CDI portable extension to PersistenceExtension from JpaExtension Signed-off-by: Laird Nelson --- .../integrations/cdi/jpa/JpaExtension.java | 16 +++++--- .../cdi/jpa/PersistenceExtension.java | 41 +++++++++---------- .../cdi/jpa/PersistenceUnitInfoBean.java | 14 ++++--- 3 files changed, 39 insertions(+), 32 deletions(-) diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaExtension.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaExtension.java index 86594a348ac..074e8c28321 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaExtension.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaExtension.java @@ -20,6 +20,7 @@ import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; @@ -114,7 +115,10 @@ * multiple threads.

* * @see PersistenceUnitInfoBean + * + * @deprecated Please use {@link PersistenceExtension} instead. */ +@Deprecated(since = "4.0") public class JpaExtension implements Extension { @@ -158,7 +162,9 @@ public class JpaExtension implements Extension { /** * Whether or not this extension will do anything. * - *

This field's value is {@code true} by default.

+ *

This field's value is {@code false} by default.

+ * + * @see PersistenceExtension */ private final boolean enabled; @@ -309,7 +315,7 @@ public JpaExtension() { if (LOGGER.isLoggable(Level.FINER)) { LOGGER.entering(cn, mn); } - this.enabled = Boolean.parseBoolean(System.getProperty(this.getClass().getName() + ".enabled", "true")); + this.enabled = Boolean.parseBoolean(System.getProperty(this.getClass().getName() + ".enabled", "false")); if (LOGGER.isLoggable(Level.FINE) && !this.enabled) { LOGGER.logp(Level.FINE, cn, mn, "jpaExtensionDisabled", this.getClass().getName()); } @@ -812,7 +818,7 @@ private void afterBeanDiscovery(@Observes @Priority(LIBRARY_AFTER) AfterBeanDiscovery event, BeanManager beanManager) - throws IOException, JAXBException, ReflectiveOperationException, XMLStreamException { + throws IOException, JAXBException, ReflectiveOperationException, URISyntaxException, XMLStreamException { String cn = JpaExtension.class.getName(); String mn = "afterBeanDiscovery"; if (LOGGER.isLoggable(Level.FINER)) { @@ -1357,7 +1363,7 @@ private void processPersistenceXmls(AfterBeanDiscovery event, Enumeration urls, Collection providers, boolean userSuppliedPersistenceUnitInfoBeans) - throws IOException, JAXBException, ReflectiveOperationException, XMLStreamException { + throws IOException, JAXBException, ReflectiveOperationException, URISyntaxException, XMLStreamException { String cn = JpaExtension.class.getName(); String mn = "processPersistenceXmls"; if (LOGGER.isLoggable(Level.FINER)) { @@ -1428,7 +1434,7 @@ private void processPersistenceXmls(AfterBeanDiscovery event, .add(PersistenceUnitInfoBean.fromPersistenceUnit(persistenceUnit, classLoader, tempClassLoaderSupplier, - new URL(url, ".."), // i.e. META-INF/.. + url.toURI().resolve("..").toURL(), unlistedManagedClassesByPersistenceUnitNames, dataSourceProviderSupplier)); } diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java index 31767003a1d..3b01e60bfad 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java @@ -21,6 +21,7 @@ import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.security.CodeSource; @@ -136,9 +137,10 @@ public final class PersistenceExtension implements Extension { * *

In such a case, the injection point will be effectively rewritten such that it will appear to the CDI * container as though there were a value specified for the {@link PersistenceContext#unitName() unitName} - * element—namely this field's value. Additionally, a bean identical to the existing solitary {@link - * PersistenceUnitInfo}-typed bean will be added with this field's value as the {@linkplain Named#value() value of - * its Named qualifier}, thus serving as a kind of alias for the "real" bean.

+ * element—namely this field's value ({@value #DEFAULT_PERSISTENCE_UNIT_NAME}). Additionally, a bean + * identical to the existing solitary {@link PersistenceUnitInfo}-typed bean will be added with this field's value + * as the {@linkplain Named#value() value of its Named qualifier}, thus serving as a kind of alias for + * the "real" bean.

* *

This is necessary because the empty string ({@code ""}) as the value of the {@link Named#value()} element can * have special @@ -228,7 +230,7 @@ public final class PersistenceExtension implements Extension { /** * Indicates if JTA transactions can be supported. * - * @see #addAllocatorBeanAndInstallTransactionSupport(AfterTypeDiscovery) + * @see #addTypes(AfterTypeDiscovery) */ private boolean transactionsSupported; @@ -260,7 +262,7 @@ public final class PersistenceExtension implements Extension { @Deprecated // For invocation by CDI only. public PersistenceExtension() { super(); - this.enabled = Boolean.parseBoolean(System.getProperty(this.getClass().getName() + ".enabled", "false")); + this.enabled = Boolean.parseBoolean(System.getProperty(this.getClass().getName() + ".enabled", "true")); this.containerManagedEntityManagerQualifiers = new HashSet<>(); this.containerManagedEntityManagerFactoryQualifiers = new HashSet<>(); this.entityManagerFactoryQualifiers = new HashSet<>(); @@ -452,8 +454,8 @@ private void discoverManagedClasses(@Observes * * @exception NullPointerException if {@code e} is {@code null} */ - @SuppressWarnings("checkstyle:LineLength") - private void saveContainerManagedEntityManagerFactoryQualifiers(@Observes ProcessInjectionPoint e) { + private void + saveContainerManagedEntityManagerFactoryQualifiers(@Observes ProcessInjectionPoint e) { if (!this.enabled) { return; } @@ -529,7 +531,8 @@ private void saveContainerManagedEntityManagerQualifie * * @see #addContainerManagedJpaBeans(AfterBeanDiscovery) */ - private void addSyntheticBeans(@Observes @Priority(LIBRARY_AFTER) AfterBeanDiscovery event, BeanManager bm) { + private void addSyntheticBeans(@Observes @Priority(LIBRARY_AFTER) AfterBeanDiscovery event, BeanManager bm) + throws URISyntaxException { if (!this.enabled) { return; } @@ -976,7 +979,8 @@ private void processPersistenceXmls(AfterBeanDiscovery event, ClassLoader classLoader, Enumeration persistenceXmlUrls, Iterable providers, - boolean userSuppliedPuiBeans) { + boolean userSuppliedPuiBeans) + throws URISyntaxException { if (!persistenceXmlUrls.hasMoreElements()) { return; } @@ -1021,7 +1025,7 @@ private void processPersistenceXmls(AfterBeanDiscovery event, PersistenceUnitInfoBean.fromPersistenceUnit(pu, classLoader, tempClassLoaderSupplier, - new URL(persistenceXmlUrl, ".."), // i.e. META-INF/.. + persistenceXmlUrl.toURI().resolve("..").toURL(), unlistedManagedClassesByUnitNames, dataSourceProviderSupplier); } catch (MalformedURLException e) { @@ -1505,17 +1509,12 @@ private static void sink(Object o1, Object o2) { private static final class SyntheticJpaQualifiers { - private static final Set INSTANCE; - - static { - Set q = new HashSet<>(); - q.add(ContainerManaged.Literal.INSTANCE); - q.add(Extended.Literal.INSTANCE); - q.add(JtaTransactionScoped.Literal.INSTANCE); - q.add(Synchronized.Literal.INSTANCE); - q.add(Unsynchronized.Literal.INSTANCE); - INSTANCE = Set.copyOf(q); - } + private static final Set INSTANCE = + Set.of(ContainerManaged.Literal.INSTANCE, + Extended.Literal.INSTANCE, + JtaTransactionScoped.Literal.INSTANCE, + Synchronized.Literal.INSTANCE, + Unsynchronized.Literal.INSTANCE); } diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java index 9ca7f2fcb21..7a84a4b39f6 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java @@ -16,6 +16,7 @@ package io.helidon.integrations.cdi.jpa; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.AbstractList; @@ -988,7 +989,11 @@ public String toString() { final List jarFileUrls = new ArrayList<>(); for (final String jarFile : jarFiles) { if (jarFile != null) { - jarFileUrls.add(createJarFileURL(rootUrl, jarFile)); + try { + jarFileUrls.add(createJarFileURL(rootUrl, jarFile)); + } catch (URISyntaxException e) { + throw (MalformedURLException) new MalformedURLException(e.getMessage()).initCause(e); + } } } @@ -1095,12 +1100,9 @@ public String toString() { } private static URL createJarFileURL(final URL persistenceUnitRootUrl, final String jarFileUrlString) - throws MalformedURLException { - Objects.requireNonNull(persistenceUnitRootUrl); - Objects.requireNonNull(jarFileUrlString); + throws MalformedURLException, URISyntaxException { // Revisit: probably won't work if persistenceUnitRootUrl is, say, a jar URL - final URL returnValue = new URL(persistenceUnitRootUrl, jarFileUrlString); - return returnValue; + return persistenceUnitRootUrl.toURI().resolve(jarFileUrlString).toURL(); } From 647f8a5c0e3ab7cf5b12ea289d2504a00f477101 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Wed, 4 Oct 2023 10:30:07 -0700 Subject: [PATCH 2/6] Squashable commit; adds more deprecation notices Signed-off-by: Laird Nelson --- .../BeanManagerBackedDataSourceProvider.java | 6 +++- .../cdi/jpa/CdiTransactionScoped.java | 5 ++- .../CdiTransactionScopedEntityManager.java | 6 +++- .../integrations/cdi/jpa/ClearingQuery.java | 3 +- .../cdi/jpa/ClearingStoredProcedureQuery.java | 3 +- .../cdi/jpa/ClearingTypedQuery.java | 3 +- .../cdi/jpa/EntityManagerFactories.java | 5 ++- .../integrations/cdi/jpa/EntityManagers.java | 5 ++- .../cdi/jpa/ExtendedEntityManager.java | 3 ++ .../cdi/jpa/JpaCdiQualifiers.java | 6 +++- .../cdi/jpa/JpaTransactionScoped.java | 5 ++- .../JpaTransactionScopedEntityManager.java | 3 ++ .../cdi/jpa/JtaDataSourceProvider.java | 3 +- .../cdi/jpa/JtaTransactionSupport.java | 5 ++- .../integrations/cdi/jpa/Messages.java | 3 +- .../cdi/jpa/NoTransactionSupport.java | 5 ++- .../cdi/jpa/NonTransactional.java | 5 ++- .../jpa/NonTransactionalEntityManager.java | 5 ++- .../cdi/jpa/PersistenceExtension.java | 34 +++++++++++++++++-- .../cdi/jpa/PersistenceUnitInfoBean.java | 4 +-- .../cdi/jpa/TransactionSupport.java | 5 ++- .../integrations/cdi/jpa/package-info.java | 6 ++-- .../jpa-cdi/src/main/java/module-info.java | 6 ++-- 23 files changed, 106 insertions(+), 28 deletions(-) diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/BeanManagerBackedDataSourceProvider.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/BeanManagerBackedDataSourceProvider.java index 5c1691cba7f..58b22f310d8 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/BeanManagerBackedDataSourceProvider.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/BeanManagerBackedDataSourceProvider.java @@ -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. @@ -34,8 +34,12 @@ * @see PersistenceUnitInfoBean.DataSourceProvider * * @see JtaDataSourceProvider + * + * @deprecated This is an internal class used by the now-deprecated {@link JpaExtension} class. Its replacement is an + * internal class, {@link JtaAbsentDataSourceProvider}, used by the {@link PersistenceExtension} class. */ @ApplicationScoped +@Deprecated(since = "4.0") class BeanManagerBackedDataSourceProvider implements PersistenceUnitInfoBean.DataSourceProvider { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScoped.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScoped.java index 4b0b692601c..406b9c32283 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScoped.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScoped.java @@ -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. @@ -28,7 +28,10 @@ * *

This qualifier must not be combined with {@link Extended}, * {@link JpaTransactionScoped} or {@link NonTransactional}.

+ * + * @deprecated No replacement. */ +@Deprecated(since = "4.0") @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({}) // can only be programmatically added diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScopedEntityManager.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScopedEntityManager.java index 818770b7c90..8615c05dc77 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScopedEntityManager.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/CdiTransactionScopedEntityManager.java @@ -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. @@ -50,7 +50,11 @@ * this class are not safe for concurrent use by multiple threads.

* * @see JpaExtension + * + * @deprecated This is an internal class used by the now-deprecated {@link JpaExtension} class. Its replacement is an + * internal detail of the {@link PersistenceExtension} class. */ +@Deprecated(since = "4.0") @Vetoed class CdiTransactionScopedEntityManager extends DelegatingEntityManager { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingQuery.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingQuery.java index 6ac39fc2578..27d054531c7 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingQuery.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingQuery.java @@ -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. @@ -21,6 +21,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.Query; +@Deprecated(since = "4.0") final class ClearingQuery extends DelegatingQuery { private final EntityManager entityManager; diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingStoredProcedureQuery.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingStoredProcedureQuery.java index 8077a080c7a..7f903b97140 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingStoredProcedureQuery.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingStoredProcedureQuery.java @@ -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. @@ -21,6 +21,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.StoredProcedureQuery; +@Deprecated(since = "4.0") final class ClearingStoredProcedureQuery extends DelegatingStoredProcedureQuery { private final EntityManager entityManager; diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingTypedQuery.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingTypedQuery.java index f3658fc8d06..7218e8d37a6 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingTypedQuery.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ClearingTypedQuery.java @@ -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. @@ -21,6 +21,7 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.TypedQuery; +@Deprecated(since = "4.0") final class ClearingTypedQuery extends DelegatingTypedQuery { private final EntityManager entityManager; diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagerFactories.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagerFactories.java index 1a2bd558b2b..bec60f163f5 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagerFactories.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagerFactories.java @@ -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. @@ -46,7 +46,10 @@ * * @see PersistenceProvider#createContainerEntityManagerFactory(PersistenceUnitInfo, * Map) + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") final class EntityManagerFactories { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagers.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagers.java index a0edc72daf0..d951a3a8a3d 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagers.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/EntityManagers.java @@ -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. @@ -38,7 +38,10 @@ * requirements of the JPA specification. * * @see #createContainerManagedEntityManager(Instance, Set) + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") final class EntityManagers { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ExtendedEntityManager.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ExtendedEntityManager.java index 7818fde33a1..4581aab3ded 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ExtendedEntityManager.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/ExtendedEntityManager.java @@ -30,7 +30,10 @@ /** * A {@link DelegatingEntityManager} created to support extended * persistence contexts. + * + * @deprecated This is an internal class used by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") class ExtendedEntityManager extends DelegatingEntityManager { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaCdiQualifiers.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaCdiQualifiers.java index 135cc71e8b1..9452109dde6 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaCdiQualifiers.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaCdiQualifiers.java @@ -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. @@ -25,7 +25,11 @@ * defined by this package. * * @see #JPA_CDI_QUALIFIERS + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension}, {@link EntityManagers} + * and {@link EntityManagerFactories} classes. */ +@Deprecated(since = "4.0") final class JpaCdiQualifiers { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScoped.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScoped.java index d6dfc6c65b4..0d135d1fe0a 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScoped.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScoped.java @@ -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. @@ -28,7 +28,10 @@ * *

This qualifier must not be combined with {@link Extended}, * {@link CdiTransactionScoped} or {@link NonTransactional}.

+ * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({}) // can only be programmatically added diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScopedEntityManager.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScopedEntityManager.java index e26f499fb81..4e52e9afb34 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScopedEntityManager.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JpaTransactionScopedEntityManager.java @@ -57,7 +57,10 @@ * *

As with all {@link EntityManager} implementations, instances of * this class are not safe for concurrent use by multiple threads.

+ * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") @Vetoed final class JpaTransactionScopedEntityManager extends DelegatingEntityManager { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaDataSourceProvider.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaDataSourceProvider.java index 7656815cc89..d2ea58cf26f 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaDataSourceProvider.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaDataSourceProvider.java @@ -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. @@ -46,6 +46,7 @@ import jakarta.transaction.TransactionSynchronizationRegistry; @ApplicationScoped +@Deprecated(since = "4.0") class JtaDataSourceProvider implements PersistenceUnitInfoBean.DataSourceProvider { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaTransactionSupport.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaTransactionSupport.java index 7ce0355214f..f0be504555c 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaTransactionSupport.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/JtaTransactionSupport.java @@ -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. @@ -41,8 +41,11 @@ * @see TransactionSupport * * @see NoTransactionSupport + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ @ApplicationScoped +@Deprecated(since = "4.0") class JtaTransactionSupport implements TransactionSupport { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/Messages.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/Messages.java index e047697eb0e..eb5ee4a36a0 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/Messages.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/Messages.java @@ -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. @@ -18,6 +18,7 @@ import java.text.MessageFormat; import java.util.ResourceBundle; +@Deprecated(since = "4.0") final class Messages { private static final ResourceBundle MESSAGES = ResourceBundle.getBundle(Messages.class.getName()); diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NoTransactionSupport.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NoTransactionSupport.java index e87ecfe729c..6bbbb863ff1 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NoTransactionSupport.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NoTransactionSupport.java @@ -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. @@ -31,8 +31,11 @@ * @see TransactionSupport * * @see JtaTransactionSupport + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ @ApplicationScoped +@Deprecated(since = "4.0") final class NoTransactionSupport implements TransactionSupport { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactional.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactional.java index 2c639124ebf..d47008c38b0 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactional.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactional.java @@ -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. @@ -29,7 +29,10 @@ *

This qualifier must not be combined with {@link * CdiTransactionScoped}, {@link Extended} or {@code * JpaTransactionScoped}.

+ * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") @Qualifier @Retention(RetentionPolicy.RUNTIME) @Target({}) // can only be programmatically added diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactionalEntityManager.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactionalEntityManager.java index 6516b8352ca..68e374cd0b8 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactionalEntityManager.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/NonTransactionalEntityManager.java @@ -44,7 +44,7 @@ * #acquireDelegate()} method and only under appropriate * circumstances.

* - *

This class is added as a synthetic bean by the {@link + *

This class is added as a synthetic bean by the (now-deprecated) {@link * JpaExtension} class.

* *

Implementation Notes

@@ -60,7 +60,10 @@ * this class are not safe for concurrent use by multiple threads.

* * @see JpaExtension + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") @Vetoed class NonTransactionalEntityManager extends DelegatingEntityManager { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java index 3b01e60bfad..6d8caf2d762 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java @@ -111,8 +111,8 @@ /** * An {@link Extension} that integrates container-mode
Jakarta Persistence - * 3.0 into CDI SE 3.0-based + * href="https://jakarta.ee/specifications/persistence/3.1/jakarta-persistence-spec-3.1.html">Jakarta Persistence + * 3.1 into CDI SE 4.0-based * applications. */ public final class PersistenceExtension implements Extension { @@ -143,7 +143,7 @@ public final class PersistenceExtension implements Extension { * the "real" bean.

* *

This is necessary because the empty string ({@code ""}) as the value of the {@link Named#value()} element can - * have special + * have special * semantics, so cannot be used to designate an unnamed or otherwise default persistence unit.

* *

The value of this field is subject to change without prior notice at any point. In general the mechanics @@ -313,6 +313,7 @@ private void makePersistencePropertyARepeatableQualifier(@Observes BeforeBea * * @see JtaAdaptingDataSourceProvider */ + @SuppressWarnings("deprecation") private void vetoDeprecatedJtaDataSourceProvider(@Observes ProcessBeanAttributes event) { if (!this.enabled) { return; @@ -328,6 +329,33 @@ private void vetoDeprecatedJtaDataSourceProvider(@Observes ProcessBeanAttributes event.veto(); } + /** + * {@linkplain ProcessBeanAttributes#veto() Vetoes} any bean whose bean types includes the deprecated {@link + * BeanManagerBackedDataSourceProvider} class, since it is replaced by {@link JtaAbsentDataSourceProvider}. + * + * @param event the {@link ProcessBeanAttributes} event in question; must not be {@code null} + * + * @exception NullPointerException if {@code event} is {@code null} + * + * @see JtaAbsentDataSourceProvider + */ + @SuppressWarnings("deprecation") + private void vetoDeprecatedBeanManagerBackedDataSourceProvider(@Observes + ProcessBeanAttributes e) { + if (!this.enabled) { + return; + } + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.logp(Level.FINE, this.getClass().getName(), "vetoDeprecatedBeanManagerBackedDataSourceProvider", + "Vetoing BeanAttributes {0} representing " + + BeanManagerBackedDataSourceProvider.class + + " because it is deprecated and " + + JtaAbsentDataSourceProvider.class + + " replaces it", e.getBeanAttributes()); + } + e.veto(); + } + private void rewriteJpaAnnotations(@Observes @WithAnnotations({PersistenceContext.class, PersistenceUnit.class}) ProcessAnnotatedType event) { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java index 7a84a4b39f6..c7d83634561 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceUnitInfoBean.java @@ -1016,7 +1016,7 @@ public String toString() { assert managedClasses != null; String name = persistenceUnit.getName(); if (name == null || name.isEmpty()) { - name = JpaExtension.DEFAULT_PERSISTENCE_UNIT_NAME; + name = PersistenceExtension.DEFAULT_PERSISTENCE_UNIT_NAME; } final Boolean excludeUnlistedClasses = persistenceUnit.isExcludeUnlistedClasses(); @@ -1032,7 +1032,7 @@ public String toString() { } } // Also add "default" ones - myUnlistedClasses = unlistedClasses.get(JpaExtension.DEFAULT_PERSISTENCE_UNIT_NAME); + myUnlistedClasses = unlistedClasses.get(PersistenceExtension.DEFAULT_PERSISTENCE_UNIT_NAME); if (myUnlistedClasses != null && !myUnlistedClasses.isEmpty()) { for (final Class unlistedClass : myUnlistedClasses) { if (unlistedClass != null) { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/TransactionSupport.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/TransactionSupport.java index 5a0bd636e08..a6d1573a483 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/TransactionSupport.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/TransactionSupport.java @@ -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. @@ -24,7 +24,10 @@ * @see NoTransactionSupport * * @see JtaTransactionSupport + * + * @deprecated This is an internal class used only by the now-deprecated {@link JpaExtension} class. */ +@Deprecated(since = "4.0") interface TransactionSupport { diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/package-info.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/package-info.java index c9bc9b439c7..0bf8da50999 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/package-info.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2022 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. @@ -17,10 +17,10 @@ /** * Provides classes and interfaces that integrate the * provider-independent parts of JPA into CDI. * - * @see io.helidon.integrations.cdi.jpa.JpaExtension + * @see io.helidon.integrations.cdi.jpa.PersistenceExtension * * @see io.helidon.integrations.cdi.jpa.PersistenceUnitInfoBean */ diff --git a/integrations/cdi/jpa-cdi/src/main/java/module-info.java b/integrations/cdi/jpa-cdi/src/main/java/module-info.java index 23d9e32a635..c92a5da4d35 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/module-info.java +++ b/integrations/cdi/jpa-cdi/src/main/java/module-info.java @@ -20,10 +20,10 @@ /** * Provides classes and interfaces that integrate the * provider-independent parts of JPA into CDI. * - * @see io.helidon.integrations.cdi.jpa.JpaExtension + * @see io.helidon.integrations.cdi.jpa.PersistenceExtension * * @see io.helidon.integrations.cdi.jpa.PersistenceUnitInfoBean */ @@ -32,7 +32,7 @@ in = HelidonFlavor.MP, path = "JPA" ) -@SuppressWarnings({ "requires-automatic"}) +@SuppressWarnings({ "deprecation", "requires-automatic"}) module io.helidon.integrations.cdi.jpa { requires jakarta.xml.bind; From 209a5fc0e94099e51727b803f453b19feccdd211 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Wed, 4 Oct 2023 14:45:29 -0700 Subject: [PATCH 3/6] Squashable commit; introduces ability to customize a PersistenceUnitInfoBean before it is placed into service; adjusts Hibernate integration to make use of this facility Signed-off-by: Laird Nelson --- .../cdi/hibernate/CDISEJtaPlatform.java | 172 +++++++++++------- .../src/main/java/module-info.java | 6 +- .../cdi/jpa/PersistenceExtension.java | 38 ++-- 3 files changed, 139 insertions(+), 77 deletions(-) diff --git a/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java index b7281067411..1de9cb57d2e 100644 --- a/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java +++ b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java @@ -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. @@ -16,14 +16,20 @@ package io.helidon.integrations.cdi.hibernate; 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 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 @@ -35,74 +41,112 @@ */ @ApplicationScoped public class CDISEJtaPlatform extends AbstractJtaPlatform { - private final TransactionManager transactionManager; - private final UserTransaction userTransaction; + private static final long serialVersionUID = 1L; + + private transient TransactionManager transactionManager; + + private transient UserTransaction userTransaction; + + /** + * Creates a new {@link CDISEJtaPlatform}. + * + * @deprecated Required by the + * CDI specification and not intended for end-user use. + */ + @Deprecated + CDISEJtaPlatform() { + super(); + this.transactionManager = null; + this.userTransaction = null; + } + + /** + * 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); + } - private static final long serialVersionUID = 1L; + @Override + protected boolean canCacheUserTransaction() { + return true; + } - /** - * 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}. + * + *

This method never returns {@code null}.

+ * + * @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}. - * - *

This method never returns {@code null}.

- * - * @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}. + * + *

This method never returns {@code null}.

+ * + * @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}. - * - *

This method never returns {@code null}.

- * - * @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) { + p.setProperty(CONNECTION_HANDLING, DELAYED_ACQUISITION_AND_RELEASE_AFTER_TRANSACTION.toString()); + } + } } diff --git a/integrations/cdi/hibernate-cdi/src/main/java/module-info.java b/integrations/cdi/hibernate-cdi/src/main/java/module-info.java index 796ad93e635..b93967fbb15 100644 --- a/integrations/cdi/hibernate-cdi/src/main/java/module-info.java +++ b/integrations/cdi/hibernate-cdi/src/main/java/module-info.java @@ -35,8 +35,10 @@ @Aot(description = "Experimental support, tested on limited use cases") @SuppressWarnings({ "requires-automatic", "requires-transitive-automatic" }) module io.helidon.integrations.cdi.hibernate { - requires jakarta.cdi; - requires jakarta.inject; + + requires transitive jakarta.cdi; + requires transitive jakarta.inject; + requires jakarta.persistence; requires java.sql; requires static io.helidon.common.features.api; diff --git a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java index 6d8caf2d762..a48a031c642 100644 --- a/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java +++ b/integrations/cdi/jpa-cdi/src/main/java/io/helidon/integrations/cdi/jpa/PersistenceExtension.java @@ -56,6 +56,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; @@ -153,6 +154,9 @@ public final class PersistenceExtension implements Extension { private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0]; + private static final TypeLiteral> EVENT_PERSISTENCEUNITINFOBEAN_TYPELITERAL = + new TypeLiteral<>() {}; + private static final Logger LOGGER = Logger.getLogger(PersistenceExtension.class.getName()); @@ -989,7 +993,7 @@ private void addPersistenceProviderBeanIfAbsent(AfterBeanDiscovery event, .addTransitiveTypeClosure(PersistenceProvider.class) .scope(Singleton.class) .qualifiers(NamedLiteral.of(unitName)) - .createWith(cc -> { + .produceWith(i -> { try { ClassLoader classLoader = pui.getClassLoader(); if (classLoader == null) { @@ -1029,7 +1033,7 @@ private void processPersistenceXmls(AfterBeanDiscovery event, } Supplier dataSourceProviderSupplier = () -> bm.createInstance().select(DataSourceProvider.class).get(); - PersistenceUnitInfo solePui = null; + PersistenceUnitInfoBean solePui = null; Supplier tempClassLoaderSupplier = classLoader instanceof URLClassLoader ucl ? () -> new URLClassLoader(ucl.getURLs()) : () -> classLoader; for (int puCount = 0; persistenceXmlUrls.hasMoreElements();) { @@ -1064,6 +1068,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") @@ -1072,8 +1077,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; @@ -1088,17 +1093,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)); } } } @@ -1119,6 +1125,7 @@ private void processImplicitPersistenceUnits(AfterBeanDiscovery event, Iterable< } } } + Named qualifier = NamedLiteral.of(unitName); // Provide support for, e.g.: // @Inject // @Named("test") @@ -1127,8 +1134,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; @@ -1144,6 +1151,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__") @@ -1152,8 +1160,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)); } } } @@ -1348,6 +1356,14 @@ private static void disposeJtaExtendedEntityManager(JtaExtendedEntityManager em, containerManagedSelectionQualifiers); } + private static PersistenceUnitInfoBean producePersistenceUnitInfoBean(Instance 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 instance) { BeanAttributes ba = instance.select(BEAN_ENTITYMANAGERFACTORY_TYPELITERAL).get(); Set selectionQualifiers = new HashSet<>(); From c7ad01aa03c45154417ba04d643feaa9cdaa9134 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Wed, 4 Oct 2023 15:25:31 -0700 Subject: [PATCH 4/6] Squashable commit; adds logging and cleans up module-info.java in hibernate-cdi Signed-off-by: Laird Nelson --- .../integrations/cdi/hibernate/CDISEJtaPlatform.java | 9 +++++++++ .../cdi/hibernate-cdi/src/main/java/module-info.java | 6 ++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java index 1de9cb57d2e..4bd600f7166 100644 --- a/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java +++ b/integrations/cdi/hibernate-cdi/src/main/java/io/helidon/integrations/cdi/hibernate/CDISEJtaPlatform.java @@ -15,6 +15,7 @@ */ package io.helidon.integrations.cdi.hibernate; +import java.lang.System.Logger; import java.util.Objects; import java.util.Properties; @@ -27,6 +28,7 @@ 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; @@ -42,6 +44,8 @@ @ApplicationScoped public class CDISEJtaPlatform extends AbstractJtaPlatform { + private static final Logger LOGGER = System.getLogger(CDISEJtaPlatform.class.getName()); + private static final long serialVersionUID = 1L; private transient TransactionManager transactionManager; @@ -145,6 +149,11 @@ protected TransactionManager locateTransactionManager() { 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()); } } diff --git a/integrations/cdi/hibernate-cdi/src/main/java/module-info.java b/integrations/cdi/hibernate-cdi/src/main/java/module-info.java index b93967fbb15..7ce24bf0496 100644 --- a/integrations/cdi/hibernate-cdi/src/main/java/module-info.java +++ b/integrations/cdi/hibernate-cdi/src/main/java/module-info.java @@ -39,13 +39,11 @@ requires transitive jakarta.cdi; requires transitive jakarta.inject; requires jakarta.persistence; - requires java.sql; - - requires static io.helidon.common.features.api; - requires transitive jakarta.transaction; requires transitive org.hibernate.orm.core; + requires static io.helidon.common.features.api; + exports io.helidon.integrations.cdi.hibernate; provides org.hibernate.engine.transaction.jta.platform.spi.JtaPlatformProvider From 932c25de47c52a247bbe31c5fc62b4828f146af1 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Wed, 4 Oct 2023 21:14:46 -0700 Subject: [PATCH 5/6] Squashable commit; fixes an arcane case where a connection leaving a JTA association would have its autoCommit status set to false in certain situations Signed-off-by: Laird Nelson --- .../integrations/jta/jdbc/JtaConnection.java | 74 +++++++++++++++---- .../jta/jdbc/LocalXAResource.java | 40 ++++++---- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/JtaConnection.java b/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/JtaConnection.java index 05407b89a00..775735e4ba4 100644 --- a/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/JtaConnection.java +++ b/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/JtaConnection.java @@ -47,6 +47,7 @@ import javax.transaction.xa.Xid; import io.helidon.integrations.jdbc.ConditionallyCloseableConnection; +import io.helidon.integrations.jdbc.DelegatingConnection; import io.helidon.integrations.jdbc.SQLSupplier; import io.helidon.integrations.jdbc.UncheckedSQLException; @@ -89,6 +90,7 @@ class JtaConnection extends ConditionallyCloseableConnection { // RollbackExceptions to SQLExceptions. private static final String TRANSACTION_ROLLBACK = "40000"; + // A VarHandle to atomically and efficiently manipulate the enlistment instance field (see below). private static final VarHandle ENLISTMENT; static { @@ -304,6 +306,10 @@ class JtaConnection extends ConditionallyCloseableConnection { ? () -> new LocalXAResource(this::connectionFunction, exceptionConverter) : xaResourceSupplier; this.xidConsumer = xidConsumer == null ? JtaConnection::sink : xidConsumer; + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.logp(Level.FINE, this.getClass().getName(), "", + "Creating {0} using delegate {1} on thread {2}", new Object[] {this, delegate, Thread.currentThread()}); + } if (immediateEnlistment) { this.enlist(); } @@ -317,10 +323,12 @@ class JtaConnection extends ConditionallyCloseableConnection { @Override // ConditionallyCloseableConnection public final void setCloseable(boolean closeable) { - super.setCloseable(closeable); if (LOGGER.isLoggable(Level.FINER)) { LOGGER.entering(this.getClass().getName(), "setCloseable", closeable); + super.setCloseable(closeable); LOGGER.exiting(this.getClass().getName(), "setCloseable"); + } else { + super.setCloseable(closeable); } } @@ -355,6 +363,10 @@ public final String nativeSQL(String sql) throws SQLException { @Override // ConditionallyCloseableConnection public final void setAutoCommit(boolean autoCommit) throws SQLException { this.failWhenClosed(); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.logp(Level.FINE, this.getClass().getName(), "setAutoCommit", + "Setting autoCommit on {0} to {1}", new Object[] {this, autoCommit}); + } this.enlist(); if (autoCommit && this.enlisted()) { // "SQLException...if...setAutoCommit(true) is called while participating in a distributed transaction" @@ -367,7 +379,12 @@ public final void setAutoCommit(boolean autoCommit) throws SQLException { public final boolean getAutoCommit() throws SQLException { this.failWhenClosed(); this.enlist(); - return super.getAutoCommit(); + boolean ac = super.getAutoCommit(); + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.logp(Level.FINE, this.getClass().getName(), "getAutoCommit", + "Getting autoCommit ({0}) on {1}", new Object[] {ac, this}); + } + return ac; } @Override // ConditionallyCloseableConnection @@ -788,10 +805,13 @@ public final void close() throws SQLException { } } super.close(); - if (LOGGER.isLoggable(Level.FINE) && !this.isClosePending()) { - // If a close is not pending then that means it actually happened. - LOGGER.logp(Level.FINE, this.getClass().getName(), "close", - "Closed {0} on thread {1}", new Object[] {this, Thread.currentThread()}); + if (LOGGER.isLoggable(Level.FINE)) { + if (!this.isClosePending()) { + // If a close is not pending then that means it actually happened. + LOGGER.logp(Level.FINE, this.getClass().getName(), "close", + "Closed {0} on thread {1}", new Object[] {this, Thread.currentThread()}); + assert this.delegate().isClosed(); + } } if (LOGGER.isLoggable(Level.FINER)) { LOGGER.exiting(this.getClass().getName(), "close"); @@ -970,7 +990,8 @@ private void enlist() throws SQLException { int currentThreadTransactionStatus = this.transactionStatus(); switch (currentThreadTransactionStatus) { case Status.STATUS_ACTIVE: - // There is a global transaction currently active on the current thread. That's good. Keep going. + // There is a global transaction currently active on the current thread. That's good. Keep going. (Every + // other case in this switch statement will result in a return or a throw.) break; case Status.STATUS_COMMITTED: case Status.STATUS_NO_TRANSACTION: @@ -996,10 +1017,11 @@ private void enlist() throws SQLException { if (!super.getAutoCommit()) { // There is, as far as we can tell, an active global transaction on the current thread, and - // super.getAutoCommit() (super. on purpose, not this.) returned false, and we aren't (yet) enlisted with - // the active global transaction, so autoCommit must have been disabled on purpose by the caller, not by the - // transaction enlistment machinery. In such a case, we don't want to permit enlistment, because a local - // transaction may be in progress and we don't want to have its effects mixed in. + // super.getAutoCommit() (super. on purpose, not this., to prevent a circular call to enlist()) returned + // false, and we aren't (yet) enlisted with the active global transaction, so autoCommit must have been + // disabled on purpose by the caller, not by the transaction enlistment machinery. In such a case, we don't + // want to permit enlistment, because a local transaction may be in progress and we don't want to have its + // effects mixed in. throw new SQLTransientException("autoCommit was false during active transaction enlistment", INVALID_TRANSACTION_STATE_NO_SUBCLASS); } @@ -1116,25 +1138,47 @@ private void enlist() throws SQLException { // Transaction#enlistResource(XAResource) in enlist() above.) private Connection connectionFunction(Xid xid) { this.xidConsumer.accept(xid); - return this.delegate(); + return new DelegatingConnection(this.delegate()) { + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.logp(Level.FINE, "", "setAutoCommit", + "Setting autoCommit on anonymous DelegatingConnection {0} to {1}", + new Object[] {this, autoCommit}); + } + super.setAutoCommit(autoCommit); + } + }; } // (Used only by reference in enlist() above. Remember, this callback may be called by the TransactionManager on // any thread at any time for any reason.) - private void transactionCompleted(int commitedOrRolledBack) { + private void transactionCompleted(int committedOrRolledBack) { this.enlistment = null; // volatile write try { - boolean closeWasPending = this.isClosePending(); - this.setCloseable(true); + boolean closeWasPending = this.isClosePending(); // volatile state read + + // This connection is now closeable "for real". + this.setCloseable(true); // volatile state write assert this.isCloseable(); + + // Becoming closeable "for real" resets the closePending state. assert !this.isClosePending(); + if (closeWasPending) { + // If a close was pending, then a real close did not ever happen. assert !this.isClosed(); assert !this.delegate().isClosed(); + + // This connection is now closeable, so this will actually close it. this.close(); assert this.isClosed(); assert this.delegate().isClosed(); + + // We are no longer closeable because we're actually closed. assert !this.isCloseable(); + + // There is currently no close pending, and there cannot ever be again, because we are actually closed. assert !this.isClosePending(); } } catch (SQLException e) { diff --git a/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java b/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java index 6c4c4570e70..458b8c86131 100644 --- a/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java +++ b/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java @@ -725,34 +725,40 @@ static record Association(BranchState branchState, // S5: Heuristically Completed Association(BranchState branchState, Xid xid, Connection connection) { - this(branchState, xid, false, connection); - } - - Association(BranchState branchState, Xid xid, boolean suspended, Connection connection) { - this(branchState, xid, suspended, connection, true /* JDBC default; will be set from connection anyway */); + this(branchState, xid, false, connection, autoCommit(connection)); } Association { Objects.requireNonNull(xid, "xid"); + boolean autoCommit = false; switch (branchState) { case IDLE: break; case ACTIVE: case HEURISTICALLY_COMPLETED: - case NON_EXISTENT_TRANSACTION: case PREPARED: case ROLLBACK_ONLY: if (suspended) { throw new IllegalArgumentException("suspended"); } break; + case NON_EXISTENT_TRANSACTION: + if (suspended) { + throw new IllegalArgumentException("suspended"); + } + autoCommit = priorAutoCommit; + break; default: throw new IllegalArgumentException("branchState: " + branchState); } try { - priorAutoCommit = connection.getAutoCommit(); - if (priorAutoCommit) { - connection.setAutoCommit(false); + if (connection.getAutoCommit() != autoCommit) { + if (LOGGER.isLoggable(Level.FINE)) { + LOGGER.logp(Level.FINE, Association.class.getName(), "", + "Setting autoCommit to {0} on connection {1}", + new Object[] {autoCommit, connection}); + } + connection.setAutoCommit(autoCommit); } } catch (SQLException sqlException) { throw new UncheckedSQLException(sqlException); @@ -984,14 +990,10 @@ private Association forgetAndReset() throws SQLException { } private Association reset() throws SQLException { - Connection connection = this.connection(); - connection.setAutoCommit(this.priorAutoCommit()); if (LOGGER.isLoggable(Level.FINE)) { LOGGER.logp(Level.FINE, this.getClass().getName(), "reset", - "Resetting; restored autoCommit to {0} on connection {1}", - new Object[] {this.priorAutoCommit(), connection}); - LOGGER.logp(Level.FINE, this.getClass().getName(), "reset", - "Transitioning Association {0} to NON_EXISTENT_TRANSACTION", this); + "Transitioning Association {0} from state {1} to state NON_EXISTENT_TRANSACTION", + new Object[] {this, this.branchState()}); } return new Association(BranchState.NON_EXISTENT_TRANSACTION, this.xid(), @@ -1000,6 +1002,14 @@ private Association reset() throws SQLException { this.priorAutoCommit()); } + private static boolean autoCommit(Connection c) { + try { + return c.getAutoCommit(); + } catch (SQLException e) { + throw new UncheckedSQLException(e); + } + } + // Transaction Branch States (XA specification, table 6-4): // S0: Non-existent Transaction // S1: Active From 8d5e9521b7206c38e38a1c1550c39102c6854d4c Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Thu, 5 Oct 2023 11:09:12 -0700 Subject: [PATCH 6/6] Squashable commit; fixes dormant typo Signed-off-by: Laird Nelson --- .../java/io/helidon/integrations/jta/jdbc/LocalXAResource.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java b/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java index 458b8c86131..185fafceb81 100644 --- a/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java +++ b/integrations/jta/jdbc/src/main/java/io/helidon/integrations/jta/jdbc/LocalXAResource.java @@ -644,7 +644,7 @@ private static Association prepare(Association a) { throw new UncheckedSQLException(e); } if (LOGGER.isLoggable(Level.FINER)) { - LOGGER.exiting(Association.class.getName(), "preapre", a); + LOGGER.exiting(Association.class.getName(), "prepare", a); } return a; }