diff --git a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/PersistenceUnitProperties.java b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/PersistenceUnitProperties.java index ce9d7cdeb23..4e92eff95cc 100644 --- a/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/PersistenceUnitProperties.java +++ b/foundation/org.eclipse.persistence.core/src/main/java/org/eclipse/persistence/config/PersistenceUnitProperties.java @@ -84,6 +84,19 @@ */ public final class PersistenceUnitProperties { + /** + * The {@code jakarta.persistence.provider} property specifies the name + * of a provider-specific implementation of {@link jakarta.persistence.spi.PersistenceProvider}. + * This property overrides the value specified in the persistence.xml. + *

+ * Allowed Values: + *

+ */ + public static final String PROVIDER = "jakarta.persistence.provider"; + /** * The {@code jakarta.persistence.transactionType} property specifies the * transaction type for the persistence unit. This property overrides the diff --git a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/jpa/PersistenceProvider.java b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/jpa/PersistenceProvider.java index ba56e7193d8..f7acc3b973e 100644 --- a/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/jpa/PersistenceProvider.java +++ b/jpa/org.eclipse.persistence.jpa/src/main/java/org/eclipse/persistence/jpa/PersistenceProvider.java @@ -192,6 +192,10 @@ public EntityManagerFactory createEntityManagerFactory(String emName, Map // Never call this method from this class because of stack frames removal. @Override public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration configuration) { + // Check whether persistence provider is set and matches EclipseLink known classes + if (!isProviderEclipseLink(configuration)) { + return null; + } JPAInitializer initializer = getInitializer(configuration.name(), configuration.properties()); // Root URL from method caller StackWalker stackWalker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); @@ -316,29 +320,59 @@ public JPAInitializer getInitializer(String emName, Map m){ return JavaSECMPInitializer.getJavaSECMPInitializer(classLoader); } + // Package visibility is required for jUnit /** - * Need to check that the provider property is null or set for EclipseLink + * The Persistence bootstrap class must locate all the persistence providers using the PersistenceProviderResolver + * mechanism described in Section 9.3 and call createEntityManagerFactory on them in turn until an appropriate backing + * provider returns an EntityManagerFactory instance. A provider may deem itself as appropriate for the persistence unit + * if any of the following are true: + *

* Its implementation class has been specified in the provider element for that persistence unit in the persistence.xml + * file and has not been overridden by a different jakarta.persistence.provider property value included in the Map passed + * to the createEntityManagerFactory method. + *

* The jakarta.persistence.provider property was included in the Map passed to createEntityManagerFactory and the value + * of the property is the provider’s implementation class. + *

* No provider was specified for the persistence unit in either the persistence.xml or the property map. + * + * @since Jakarta Persistence 3.2 */ - public boolean checkForProviderProperty(Map properties){ - Object provider = properties.get("jakarta.persistence.provider"); - if (provider != null){ - //user has specified a provider make sure it is us or abort. - if (provider instanceof Class){ - provider = ((Class)provider).getName(); - } - try{ - if (!(EntityManagerFactoryProvider.class.getName().equals(provider) || PersistenceProvider.class.getName().equals(provider))){ - return false; - //user has requested another provider so lets ignore this request. - } - }catch(ClassCastException e){ - return false; - // not a recognized provider property value so must be another provider. - } + static boolean isProviderEclipseLink(PersistenceConfiguration configuration) { + // Property jakarta.persistence.provider has higher priority, EclipseLink accepts it also as class. + if (configuration.properties().containsKey(PersistenceUnitProperties.PROVIDER)) { + return isProviderPropertyEclipseLink(configuration.properties().get(PersistenceUnitProperties.PROVIDER)); + } + // Persistence unit provider configuration option + if (configuration.provider() != null && !configuration.provider().isEmpty()) { + return checkEclipseLinkProviderClassName(configuration.provider()); } return true; + } + /** + * Need to check that the provider property is null or set for EclipseLink + */ + public static boolean checkForProviderProperty(Map properties) { + return !properties.containsKey(PersistenceUnitProperties.PROVIDER) + || isProviderPropertyEclipseLink(properties.get(PersistenceUnitProperties.PROVIDER)); + } + + // Check whether provided jakarta persistence provider property value matches any of known classes. + // Supported property value types are String and Class. + private static boolean isProviderPropertyEclipseLink(Object providerProperty) { + String providerClassName = (providerProperty instanceof String providerString) ? providerString : null; + if (providerProperty instanceof Class providerClass) { + providerClassName = providerClass.getName(); + } + return providerClassName != null && checkEclipseLinkProviderClassName(providerClassName); + } + + // Check whether provided jakarta persistence provider class name matches any of known classes: + // * org.eclipse.persistence.jpa.PersistenceProvider + // * org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider + private static boolean checkEclipseLinkProviderClassName(String providerClassName) { + return PersistenceProvider.class.getName().equals(providerClassName) + || EntityManagerFactoryProvider.class.getName().equals(providerClassName); } + /** * Called by the container when an EntityManagerFactory * is to be created. diff --git a/jpa/org.eclipse.persistence.jpa/src/test/java/org/eclipse/persistence/jpa/PersistenceProviderTest.java b/jpa/org.eclipse.persistence.jpa/src/test/java/org/eclipse/persistence/jpa/PersistenceProviderTest.java new file mode 100644 index 00000000000..c1d7e9f3de1 --- /dev/null +++ b/jpa/org.eclipse.persistence.jpa/src/test/java/org/eclipse/persistence/jpa/PersistenceProviderTest.java @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0, + * or the Eclipse Distribution License v. 1.0 which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause + */ +package org.eclipse.persistence.jpa; + +import java.util.Map; + +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.PersistenceConfiguration; +import jakarta.persistence.spi.PersistenceUnitInfo; +import jakarta.persistence.spi.ProviderUtil; +import org.eclipse.persistence.config.PersistenceUnitProperties; +import org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class PersistenceProviderTest { + + @Test + void testCheckForProviderPropertyWithoutProperty() { + Map properties = Map.of(); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertTrue(result); + } + + @Test + void testCheckForProviderPropertyWithProviderClass() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, PersistenceProvider.class + ); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertTrue(result); + } + + @Test + void testCheckForProviderPropertyWithProviderString() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, PersistenceProvider.class.getName() + ); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertTrue(result); + } + + @Test + void testCheckForProviderPropertyWithEMFProviderClass() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, EntityManagerFactoryProvider.class + ); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertTrue(result); + } + + @Test + void testCheckForProviderPropertyWithEMFProviderString() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, EntityManagerFactoryProvider.class.getName() + ); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertTrue(result); + } + + @Test + void testCheckForProviderPropertyWithUnsupportedClass() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, Unsupported.class + ); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertFalse(result); + } + + @Test + void testCheckForProviderPropertyWithUnsupportedString() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, Unsupported.class.getName() + ); + boolean result = PersistenceProvider.checkForProviderProperty(properties); + assertFalse(result); + } + + @Test + void testIsProviderEclipseLinkWithoutProvider() { + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithProvider() { + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.provider(PersistenceProvider.class.getName()); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithEMFProvider() { + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.provider(EntityManagerFactoryProvider.class.getName()); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithUnsupportedProvider() { + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.provider(Unsupported.class.getName()); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertFalse(result); + } + + @Test + void testIsProviderEclipseLinkWithProviderStringProperty() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, PersistenceProvider.class.getName() + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithEMFProviderStringProperty() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, EntityManagerFactoryProvider.class.getName() + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithUnsupportedProviderStringProperty() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, Unsupported.class.getName() + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertFalse(result); + } + + @Test + void testIsProviderEclipseLinkWithProviderClassProperty() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, PersistenceProvider.class + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithEMFProviderClassProperty() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, EntityManagerFactoryProvider.class + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithUnsupportedProviderClassProperty() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, Unsupported.class + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertFalse(result); + } + + + @Test + void testIsProviderEclipseLinkWithProviderStringPropertyOverride() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, PersistenceProvider.class.getName() + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + // Provider is set as unsupported, but supported property must override it + configuration.provider(Unsupported.class.getName()); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithEMFProviderStringPropertyOverride() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, EntityManagerFactoryProvider.class.getName() + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + // Provider is set as unsupported, but supported property must override it + configuration.provider(Unsupported.class.getName()); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertTrue(result); + } + + @Test + void testIsProviderEclipseLinkWithUnsupportedProviderStringPropertyOverride() { + Map properties = Map.of( + PersistenceUnitProperties.PROVIDER, Unsupported.class.getName() + ); + PersistenceConfiguration configuration = new PersistenceConfiguration("Test"); + // Provider is set as supported, but unsupported property must override it + configuration.provider(PersistenceProvider.class.getName()); + configuration.properties(properties); + boolean result = PersistenceProvider.isProviderEclipseLink(configuration); + assertFalse(result); + } + + // PersistenceProvider that shall fail the check + private static final class Unsupported implements jakarta.persistence.spi.PersistenceProvider { + + @Override + public EntityManagerFactory createEntityManagerFactory(String emName, Map map) { + return null; + } + + @Override + public EntityManagerFactory createEntityManagerFactory(PersistenceConfiguration configuration) { + return null; + } + + @Override + public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map map) { + return null; + } + + @Override + public void generateSchema(PersistenceUnitInfo info, Map map) { + + } + + @Override + public boolean generateSchema(String persistenceUnitName, Map map) { + return false; + } + + @Override + public ProviderUtil getProviderUtil() { + return null; + } + + } + +} diff --git a/pom.xml b/pom.xml index eeb3deb100f..558dda9c5f4 100644 --- a/pom.xml +++ b/pom.xml @@ -1287,7 +1287,7 @@ org.eclipse.persistence eclipselink-testbuild-plugin - 1.1.0 + 1.1.1 org.carlspring.maven