| /* |
| * Copyright (c) 1998, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 1998, 2021 IBM Corporation. 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 |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| // tware - 1.0RC1 - OSGI refactor |
| // 12/23/2008-1.1M5 Michael O'Brien |
| // - 253701: set persistenceInitializationHelper so EntityManagerSetupImpl.undeploy() can clear the JavaSECMPInitializer |
| // 12/24/2012-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support |
| // 01/08/2013-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support |
| // 01/11/2013-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support |
| // 02/04/2013-2.5 Guy Pelletier |
| // - 389090: JPA 2.1 DDL Generation Support |
| // 02/02/2015-2.6 Dalia Abo Sheasha |
| // - 458462: generateSchema throws a ClassCastException within a container |
| // 02/17/2015-2.6 Rick Curtis |
| // - 460138: Change method visibility. |
| package org.eclipse.persistence.jpa; |
| |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.Map; |
| |
| import jakarta.persistence.EntityManagerFactory; |
| import jakarta.persistence.PersistenceException; |
| import jakarta.persistence.spi.ClassTransformer; |
| import jakarta.persistence.spi.LoadState; |
| import jakarta.persistence.spi.PersistenceUnitInfo; |
| import jakarta.persistence.spi.ProviderUtil; |
| |
| import org.eclipse.persistence.config.PersistenceUnitProperties; |
| import org.eclipse.persistence.config.SystemProperties; |
| import org.eclipse.persistence.exceptions.PersistenceUnitLoadingException; |
| import org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl; |
| import org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider; |
| import org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl; |
| import org.eclipse.persistence.internal.jpa.deployment.JPAInitializer; |
| import org.eclipse.persistence.internal.jpa.deployment.JavaSECMPInitializer; |
| import org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor; |
| import org.eclipse.persistence.internal.jpa.deployment.SEPersistenceUnitInfo; |
| import org.eclipse.persistence.internal.weaving.PersistenceWeaved; |
| |
| /** |
| * This is the EclipseLink EJB 3.0 provider |
| * |
| * This provider should be used by JavaEE and JavaSE users. |
| */ |
| public class PersistenceProvider implements jakarta.persistence.spi.PersistenceProvider, ProviderUtil { |
| |
| public PersistenceProvider(){ |
| } |
| |
| /** |
| * Internal method to return the entity manager factory. |
| */ |
| protected EntityManagerFactoryImpl createEntityManagerFactoryImpl(PersistenceUnitInfo puInfo, Map properties, boolean requiresConnection){ |
| if (puInfo != null) { |
| boolean isNew = false; |
| String uniqueName = null; // the name the uniquely defines the pu |
| String sessionName = null; |
| EntityManagerSetupImpl emSetupImpl = null; |
| String puName = puInfo.getPersistenceUnitName(); |
| JPAInitializer initializer = getInitializer(puInfo.getPersistenceUnitName(), properties); |
| |
| try { |
| if (EntityManagerSetupImpl.mustBeCompositeMember(puInfo)) { |
| // Persistence unit cannot be used standalone (only as a composite member). |
| // Still the factory will be created but attempt to createEntityManager would cause an exception. |
| emSetupImpl = new EntityManagerSetupImpl(puName, puName); |
| // Predeploy assigns puInfo and does not do anything else. |
| // The session is not created, no need to add emSetupImpl to the global map. |
| emSetupImpl.predeploy(puInfo, properties); |
| isNew = true; |
| } else { |
| if (initializer.isPersistenceUnitUniquelyDefinedByName()) { |
| uniqueName = puName; |
| } else { |
| uniqueName = initializer.createUniquePersistenceUnitName(puInfo); |
| } |
| |
| sessionName = EntityManagerSetupImpl.getOrBuildSessionName(properties, puInfo, uniqueName); |
| synchronized (EntityManagerFactoryProvider.emSetupImpls) { |
| emSetupImpl = EntityManagerFactoryProvider.getEntityManagerSetupImpl(sessionName); |
| |
| if (emSetupImpl == null) { |
| // there may be initial emSetupImpl cached in Initializer - remove it and use. |
| emSetupImpl = initializer.extractInitialEmSetupImpl(puName); |
| |
| if (emSetupImpl != null) { |
| // change the name |
| emSetupImpl.changeSessionName(sessionName); |
| } else { |
| // create and predeploy a new emSetupImpl |
| emSetupImpl = initializer.callPredeploy((SEPersistenceUnitInfo) puInfo, properties, uniqueName, sessionName); |
| } |
| |
| // emSetupImpl has been already predeployed, predeploy will just increment factoryCount. |
| emSetupImpl.predeploy(emSetupImpl.getPersistenceUnitInfo(), properties); |
| EntityManagerFactoryProvider.addEntityManagerSetupImpl(sessionName, emSetupImpl); |
| isNew = true; |
| } |
| } |
| } |
| } catch (Exception e) { |
| throw PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(initializer.getInitializationClassLoader(), e); |
| } |
| |
| if (! isNew) { |
| if (! uniqueName.equals(emSetupImpl.getPersistenceUnitUniqueName())) { |
| throw PersistenceUnitLoadingException.sessionNameAlreadyInUse(sessionName, uniqueName, emSetupImpl.getPersistenceUnitUniqueName()); |
| } |
| |
| // synchronized to prevent undeploying by other threads. |
| boolean undeployed = false; |
| synchronized(emSetupImpl) { |
| if (emSetupImpl.isUndeployed()) { |
| undeployed = true; |
| } else { |
| // emSetupImpl has been already predeployed, predeploy will just increment factoryCount. |
| emSetupImpl.predeploy(emSetupImpl.getPersistenceUnitInfo(), properties); |
| } |
| } |
| |
| if (undeployed) { |
| // after the emSetupImpl has been obtained from emSetupImpls |
| // it has been undeployed by factory.close() in another thread - start all over again. |
| return (EntityManagerFactoryImpl) createEntityManagerFactory(puName, properties); |
| } |
| } |
| |
| EntityManagerFactoryImpl factory = null; |
| try { |
| factory = new EntityManagerFactoryImpl(emSetupImpl, properties); |
| emSetupImpl.setRequiresConnection(requiresConnection); |
| |
| emSetupImpl.preInitializeCanonicalMetamodel(factory); |
| // This code has been added to allow validation to occur without actually calling createEntityManager |
| |
| if (emSetupImpl.shouldGetSessionOnCreateFactory(properties)) { |
| factory.getDatabaseSession(); |
| } |
| |
| return factory; |
| } catch (RuntimeException ex) { |
| if (factory != null) { |
| factory.close(); |
| } else { |
| emSetupImpl.undeploy(); |
| } |
| |
| throw ex; |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Called by Persistence class when an EntityManagerFactory |
| * is to be created. |
| * |
| * @param emName The name of the persistence unit |
| * @param properties A Map of properties for use by the |
| * persistence provider. These properties may be used to |
| * override the values of the corresponding elements in |
| * the persistence.xml file or specify values for |
| * properties not specified in the persistence.xml. |
| * @return EntityManagerFactory for the persistence unit, |
| * or null if the provider is not the right provider |
| */ |
| @Override |
| public EntityManagerFactory createEntityManagerFactory(String emName, Map properties){ |
| Map nonNullProperties = (properties == null) ? new HashMap<>() : properties; |
| |
| if (checkForProviderProperty(nonNullProperties)){ |
| String name = (emName == null) ? "" : emName; |
| JPAInitializer initializer = getInitializer(name, nonNullProperties); |
| return createEntityManagerFactoryImpl(initializer.findPersistenceUnitInfo(name, nonNullProperties), nonNullProperties, true); |
| } |
| |
| // Not EclipseLink so return null; |
| return null; |
| } |
| |
| /** |
| * Create database schemas and/or tables and/or create DDL |
| * scripts as determined by the supplied properties. |
| * <p> |
| * Called by the Persistence class when schema generation is to occur as a |
| * separate phase from creation of the entity manager factory. |
| * |
| * @param info the name of the persistence unit |
| * @param properties properties for schema generation; these may also |
| * contain provider-specific properties. The value of these |
| * properties override any values that may have been configured |
| * elsewhere. |
| * @throws PersistenceException if insufficient or inconsistent |
| * configuration information is provided of if schema generation |
| * otherwise fails |
| * |
| * @since Java Persistence 2.1 |
| */ |
| @Override |
| public void generateSchema(PersistenceUnitInfo info, Map properties) { |
| if (checkForProviderProperty(properties)) { |
| // Bug 458462 - Generate the DDL and then close. This method is |
| // called when running within a container. |
| createContainerEntityManagerFactoryImpl(info, properties, false).close(); |
| } |
| } |
| |
| /** |
| * Create database schemas and/or tables and/or create DDL scripts as |
| * determined by the supplied properties. |
| * <p> |
| * Called by the Persistence class when schema generation is to occur as a |
| * separate phase from creation of the entity manager factory. |
| * |
| * @param persistenceUnitName the name of the persistence unit |
| * @param properties properties for schema generation; these may also |
| * contain provider-specific properties. The value of these |
| * properties override any values that may have been configured |
| * elsewhere. |
| * @throws PersistenceException if insufficient or inconsistent |
| * configuration information is provided of if schema generation |
| * otherwise fails |
| * |
| * @since Java Persistence 2.1 |
| */ |
| @Override |
| public boolean generateSchema(String persistenceUnitName, Map properties) { |
| String puName = (persistenceUnitName == null) ? "" : persistenceUnitName; |
| Map nonNullProperties = (properties == null) ? new HashMap<>() : properties; |
| |
| // If not EclipseLink, do nothing. |
| if (checkForProviderProperty(nonNullProperties)) { |
| JPAInitializer initializer = getInitializer(puName, nonNullProperties); |
| SEPersistenceUnitInfo puInfo = initializer.findPersistenceUnitInfo(puName, nonNullProperties); |
| |
| if (puInfo != null && checkForProviderProperty(properties)) { |
| // Will cause a login if necessary, generate the DDL and then close. |
| // The false indicates that we do not require a connection if |
| // generating only to script. Since the user may have connected with |
| // specific database credentials for DDL generation or even provided |
| // a specific connection, close the emf once we're done. |
| createEntityManagerFactoryImpl(puInfo, properties, false).close(); |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Return JPAInitializer corresponding to the passed classLoader. |
| * Note: This is written as an instance method rather than a static to allow |
| * it to be overridden |
| * @param emName |
| * @param m |
| * @return |
| */ |
| public JPAInitializer getInitializer(String emName, Map m){ |
| ClassLoader classLoader = getClassLoader(emName, m); |
| return JavaSECMPInitializer.getJavaSECMPInitializer(classLoader); |
| } |
| |
| |
| /** |
| * Need to check that the provider property is null or set for EclipseLink |
| */ |
| 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. |
| } |
| } |
| return true; |
| |
| } |
| /** |
| * Called by the container when an EntityManagerFactory |
| * is to be created. |
| * |
| * @param info Metadata for use by the persistence provider |
| * @return EntityManagerFactory for the persistence unit |
| * specified by the metadata |
| * @param properties A Map of integration-level properties for use |
| * by the persistence provider. |
| */ |
| @Override |
| public EntityManagerFactory createContainerEntityManagerFactory(PersistenceUnitInfo info, Map properties) { |
| return createContainerEntityManagerFactoryImpl(info, properties, true); |
| } |
| |
| protected EntityManagerFactory createContainerEntityManagerFactoryImpl(PersistenceUnitInfo info, Map properties, boolean requiresConnection) { |
| // Record that we are inside a JEE container to allow weaving for non managed persistence units. |
| JavaSECMPInitializer.setIsInContainer(true); |
| |
| Map nonNullProperties = (properties == null) ? new HashMap<>() : properties; |
| |
| String forceTargetServer = EntityManagerFactoryProvider.getConfigPropertyAsString(SystemProperties.ENFORCE_TARGET_SERVER, null); |
| if ("true".equalsIgnoreCase(forceTargetServer)) { |
| nonNullProperties.remove(PersistenceUnitProperties.TARGET_SERVER); |
| } |
| |
| EntityManagerSetupImpl emSetupImpl = null; |
| if (EntityManagerSetupImpl.mustBeCompositeMember(info)) { |
| // persistence unit cannot be used standalone (only as a composite member). |
| // still the factory will be created but attempt to createEntityManager would cause an exception. |
| emSetupImpl = new EntityManagerSetupImpl(info.getPersistenceUnitName(), info.getPersistenceUnitName()); |
| // predeploy assigns puInfo and does not do anything else. |
| // the session is not created, no need to add emSetupImpl to the global map. |
| emSetupImpl.predeploy(info, nonNullProperties); |
| } else { |
| boolean isNew = false; |
| ClassTransformer transformer = null; |
| String uniqueName = PersistenceUnitProcessor.buildPersistenceUnitName(info.getPersistenceUnitRootUrl(), info.getPersistenceUnitName()); |
| String sessionName = EntityManagerSetupImpl.getOrBuildSessionName(nonNullProperties, info, uniqueName); |
| synchronized (EntityManagerFactoryProvider.emSetupImpls) { |
| emSetupImpl = EntityManagerFactoryProvider.getEntityManagerSetupImpl(sessionName); |
| if (emSetupImpl == null){ |
| emSetupImpl = new EntityManagerSetupImpl(uniqueName, sessionName); |
| isNew = true; |
| emSetupImpl.setIsInContainerMode(true); |
| // if predeploy fails then emSetupImpl shouldn't be added to FactoryProvider |
| transformer = emSetupImpl.predeploy(info, nonNullProperties); |
| EntityManagerFactoryProvider.addEntityManagerSetupImpl(sessionName, emSetupImpl); |
| } |
| } |
| |
| if(!isNew) { |
| if(!uniqueName.equals(emSetupImpl.getPersistenceUnitUniqueName())) { |
| throw PersistenceUnitLoadingException.sessionNameAlreadyInUse(sessionName, uniqueName, emSetupImpl.getPersistenceUnitUniqueName()); |
| } |
| // synchronized to prevent undeploying by other threads. |
| boolean undeployed = false; |
| synchronized(emSetupImpl) { |
| if(emSetupImpl.isUndeployed()) { |
| undeployed = true; |
| } else { |
| // emSetupImpl has been already predeployed, predeploy will just increment factoryCount. |
| transformer = emSetupImpl.predeploy(emSetupImpl.getPersistenceUnitInfo(), nonNullProperties); |
| } |
| } |
| if(undeployed) { |
| // after the emSetupImpl has been obtained from emSetupImpls |
| // it has been undeployed by factory.close() in another thread - start all over again. |
| return createContainerEntityManagerFactory(info, properties); |
| } |
| } |
| if (transformer != null){ |
| info.addTransformer(transformer); |
| } |
| } |
| |
| EntityManagerFactoryImpl factory = null; |
| try { |
| factory = new EntityManagerFactoryImpl(emSetupImpl, nonNullProperties); |
| emSetupImpl.setRequiresConnection(requiresConnection); |
| emSetupImpl.preInitializeCanonicalMetamodel(factory); |
| |
| // This code has been added to allow validation to occur without actually calling createEntityManager |
| if (emSetupImpl.shouldGetSessionOnCreateFactory(nonNullProperties)) { |
| factory.getDatabaseSession(); |
| } |
| return factory; |
| } catch (RuntimeException ex) { |
| if(factory != null) { |
| factory.close(); |
| } else { |
| emSetupImpl.undeploy(); |
| } |
| throw ex; |
| } |
| } |
| |
| /** |
| * Return the utility interface implemented by the persistence |
| * provider. |
| * @return ProviderUtil interface |
| * |
| * @since Java Persistence 2.0 |
| */ |
| @Override |
| public ProviderUtil getProviderUtil(){ |
| return this; |
| } |
| |
| /** |
| * If the provider determines that the entity has been provided |
| * by itself and that the state of the specified attribute has |
| * been loaded, this method returns <code>LoadState.LOADED</code>. |
| * If the provider determines that the entity has been provided |
| * by itself and that either entity attributes with <code>FetchType.EAGER</code> |
| * have not been loaded or that the state of the specified |
| * attribute has not been loaded, this methods returns |
| * <code>LoadState.NOT_LOADED</code>. |
| * If a provider cannot determine the load state, this method |
| * returns <code>LoadState.UNKNOWN</code>. |
| * The provider's implementation of this method must not obtain |
| * a reference to an attribute value, as this could trigger the |
| * loading of entity state if the entity has been provided by a |
| * different provider. |
| * @param entity |
| * @param attributeName name of attribute whose load status is |
| * to be determined |
| * @return load status of the attribute |
| */ |
| @Override |
| public LoadState isLoadedWithoutReference(Object entity, String attributeName){ |
| if (entity instanceof PersistenceWeaved){ |
| return isLoadedWithReference(entity, attributeName); |
| } |
| return LoadState.UNKNOWN; |
| } |
| |
| /** |
| * If the provider determines that the entity has been provided |
| * by itself and that the state of the specified attribute has |
| * been loaded, this method returns <code>LoadState.LOADED</code>. |
| * If a provider determines that the entity has been provided |
| * by itself and that either the entity attributes with <code>FetchType.EAGER</code> |
| * have not been loaded or that the state of the specified |
| * attribute has not been loaded, this method returns |
| * return <code>LoadState.NOT_LOADED</code>. |
| * If the provider cannot determine the load state, this method |
| * returns <code>LoadState.UNKNOWN</code>. |
| * The provider's implementation of this method is permitted to |
| * obtain a reference to the attribute value. (This access is |
| * safe because providers which might trigger the loading of the |
| * attribute state will have already been determined by |
| * <code>isLoadedWithoutReference</code>. ) |
| * |
| * @param entity |
| * @param attributeName name of attribute whose load status is |
| * to be determined |
| * @return load status of the attribute |
| */ |
| @Override |
| public LoadState isLoadedWithReference(Object entity, String attributeName) { |
| synchronized (EntityManagerFactoryProvider.emSetupImpls) { |
| Iterator<EntityManagerSetupImpl> setups = EntityManagerFactoryProvider.emSetupImpls.values().iterator(); |
| while (setups.hasNext()){ |
| EntityManagerSetupImpl setup = setups.next(); |
| if (setup.isDeployed()){ |
| Boolean isLoaded = EntityManagerFactoryImpl.isLoaded(entity, setup.getSession()); |
| if (isLoaded != null){ |
| if (isLoaded && attributeName != null){ |
| isLoaded = EntityManagerFactoryImpl.isLoaded(entity, attributeName, setup.getSession()); |
| } |
| if (isLoaded != null){ |
| return isLoaded ? LoadState.LOADED : LoadState.NOT_LOADED; |
| } |
| } |
| } |
| } |
| return LoadState.UNKNOWN; |
| } |
| } |
| |
| /** |
| * If the provider determines that the entity has been provided |
| * by itself and that the state of all attributes for which |
| * <code>FetchType.EAGER</code> has been specified have been loaded, this |
| * method returns <code>LoadState.LOADED</code>. |
| * If the provider determines that the entity has been provided |
| * by itself and that not all attributes with <code>FetchType.EAGER</code> |
| * have been loaded, this method returns <code>LoadState.NOT_LOADED</code>. |
| * If the provider cannot determine if the entity has been |
| * provided by itself, this method returns <code>LoadState.UNKNOWN</code>. |
| * The provider's implementation of this method must not obtain |
| * a reference to any attribute value, as this could trigger the |
| * loading of entity state if the entity has been provided by a |
| * different provider. |
| * @param entity whose loaded status is to be determined |
| * @return load status of the entity |
| */ |
| @Override |
| public LoadState isLoaded(Object entity){ |
| if (entity instanceof PersistenceWeaved){ |
| return isLoadedWithReference(entity, null); |
| } |
| return LoadState.UNKNOWN; |
| } |
| |
| /** |
| * Answer the classloader to use to create an EntityManager. |
| * If a classloader is not found in the properties map then |
| * use the current thread classloader. |
| * |
| * @param properties |
| * @return ClassLoader |
| */ |
| public ClassLoader getClassLoader(String emName, Map properties) { |
| ClassLoader classloader = null; |
| if (properties != null) { |
| classloader = (ClassLoader)properties.get(PersistenceUnitProperties.CLASSLOADER); |
| } |
| if (classloader == null) { |
| classloader = Thread.currentThread().getContextClassLoader(); |
| } |
| return classloader; |
| } |
| } |
| |