/**********************************************************************
Copyright (c) 2006 Erik Bengtson and others. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Contributors:
2006 Andy Jefferson - javadocs, reading of persistence.xml, overriding props
2008 Andy Jefferson - getCache(), getProperties(), getSupportedProperties()
...
**********************************************************************/
package org.datanucleus.jpa;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContextType;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.datanucleus.OMFContext;
import org.datanucleus.ObjectManagerFactoryImpl;
import org.datanucleus.PersistenceConfiguration;
import org.datanucleus.jdo.JDODataStoreCache;
import org.datanucleus.jdo.JDOPersistenceManagerFactory;
import org.datanucleus.jpa.exceptions.NotProviderException;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.PersistenceFileMetaData;
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.metadata.TransactionType;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.Localiser;
/**
* EntityManagerFactory implementation.
* Caches the "persistence-unit" MetaData information when encountered (in J2SE mode).
*/
public class EntityManagerFactoryImpl implements EntityManagerFactory
{
/** Localiser for messages. */
protected static final Localiser LOCALISER = Localiser.getInstance("org.datanucleus.jpa.Localisation",
NucleusJPAHelper.class.getClassLoader());
/** Underlying PersistenceManagerFactory that provides the persistence functionality. */
protected PersistenceManagerFactory pmf;
/** Cache of persistence-unit information for J2SE. */
static private Map<String, PersistenceUnitMetaData> unitMetaDataCache = null;
/** Persistence-Unit metadata that we are using in this EMF. */
private PersistenceUnitMetaData unitMetaData = null;
/** Flag for whether the factory is closed. */
private boolean closed = false;
/** Flag for whether this EMF is managed by a container. */
private boolean containerManaged = false;
/** Level 2 Cache. */
private Cache datastoreCache = null;
/**
* Constructor.
*/
public EntityManagerFactoryImpl()
{
}
/**
* Constructor when working in a J2EE environment.
* @param unitInfo The "persistent-unit" info
* @param overridingProps factory properties overriding those in the "persistence-unit"
*/
public EntityManagerFactoryImpl(PersistenceUnitInfo unitInfo, Map overridingProps)
{
containerManaged = true;
// Strictly speaking this is only required for the other constructor since the J2EE container should check
// before calling us but we check anyway
boolean validProvider = false;
if (unitInfo.getPersistenceProviderClassName() == null ||
unitInfo.getPersistenceProviderClassName().equals(PersistenceProviderImpl.class.getName()) ||
(overridingProps != null && PersistenceProviderImpl.class.getName().equals(overridingProps.get("javax.persistence.provider"))))
{
validProvider = true;
}
if (!validProvider)
{
// Not a valid provider
throw new NotProviderException(LOCALISER.msg("EMF.NotProviderForPersistenceUnit",
unitInfo.getPersistenceUnitName()));
}
// Create a PersistenceUnitMetaData
URI rootURI = null;
try
{
rootURI = unitInfo.getPersistenceUnitRootUrl().toURI();
}
catch (URISyntaxException e1)
{
}
if (unitInfo.getTransactionType() == PersistenceUnitTransactionType.JTA)
{
unitMetaData = new PersistenceUnitMetaData(unitInfo.getPersistenceUnitName(),
TransactionType.JTA.toString(), rootURI);
}
else if (unitInfo.getTransactionType() == PersistenceUnitTransactionType.RESOURCE_LOCAL)
{
unitMetaData = new PersistenceUnitMetaData(unitInfo.getPersistenceUnitName(),
TransactionType.RESOURCE_LOCAL.toString(), rootURI);
}
// Classes
List<String> classNames = unitInfo.getManagedClassNames();
Iterator<String> classIter = classNames.iterator();
while (classIter.hasNext())
{
unitMetaData.addClassName(classIter.next());
}
// Mapping files
List<String> mappingFileNames = unitInfo.getMappingFileNames();
Iterator<String> mappingFileIter = mappingFileNames.iterator();
while (mappingFileIter.hasNext())
{
unitMetaData.addMappingFile(mappingFileIter.next());
}
// Jars
List<URL> jarUrls = unitInfo.getJarFileUrls();
Iterator<URL> jarUrlIter = jarUrls.iterator();
while (jarUrlIter.hasNext())
{
unitMetaData.addJarFile(jarUrlIter.next());
}
// Properties
Properties props = unitInfo.getProperties();
for (Enumeration e = props.propertyNames(); e.hasMoreElements();)
{
String prop = (String) e.nextElement();
unitMetaData.addProperty(prop, props.getProperty(prop));
}
// Exclude unlisted classes
if (unitInfo.excludeUnlistedClasses())
{
unitMetaData.setExcludeUnlistedClasses();
}
// Provider
unitMetaData.setProvider(unitInfo.getPersistenceProviderClassName());
if (overridingProps == null)
{
overridingProps = new HashMap();
}
// unit info will give us a javax.sql.DataSource instance, so we give that to PMF
PersistenceUnitTransactionType type = unitInfo.getTransactionType();
if (type == PersistenceUnitTransactionType.RESOURCE_LOCAL)
{
// Assumed to have non-jta datasource for connections
if (unitInfo.getNonJtaDataSource() != null)
{
overridingProps.put("datanucleus.ConnectionFactory", unitInfo.getNonJtaDataSource());
}
}
else if (type == PersistenceUnitTransactionType.JTA)
{
// Assumed to have jta datasource for connections
if (unitInfo.getJtaDataSource() != null)
{
overridingProps.put("datanucleus.ConnectionFactory", unitInfo.getJtaDataSource());
}
if (unitInfo.getNonJtaDataSource() != null)
{
// Use non-jta for non-tx connections
overridingProps.put("datanucleus.ConnectionFactory2", unitInfo.getNonJtaDataSource());
}
}
else
{
if (unitInfo.getJtaDataSource() != null)
{
overridingProps.put("datanucleus.ConnectionFactory", unitInfo.getJtaDataSource());
}
if (unitInfo.getNonJtaDataSource() != null)
{
// Use non-jta for non-tx connections
overridingProps.put("datanucleus.ConnectionFactory2", unitInfo.getNonJtaDataSource());
}
}
if (unitInfo.getClassLoader() != null)
{
overridingProps.put("datanucleus.primaryClassLoader", unitInfo.getClassLoader());
}
// ClassTransformer
boolean addClassTransformer = true;
if (unitMetaData.getProperties() != null)
{
Object addCTVal = unitMetaData.getProperties().get("datanucleus.jpa.addClassTransformer");
if (addCTVal != null && ((String)addCTVal).equalsIgnoreCase("false"))
{
addClassTransformer = false;
}
}
if (overridingProps != null)
{
Object addCTVal = overridingProps.get("datanucleus.jpa.addClassTransformer");
if (addCTVal != null && ((String)addCTVal).equalsIgnoreCase("false"))
{
addClassTransformer = false;
}
}
if (addClassTransformer)
{
try
{
unitInfo.addTransformer(new JPAClassTransformer());
}
catch (IllegalStateException ise)
{
// Spring probably threw its toys out so log it
NucleusLogger.JPA.warn("Exception was caught when adding the class transformer. Ignoring it.", ise);
}
}
// Initialise the PMF
pmf = initialisePMF(unitMetaData, overridingProps);
// Initialise the L2 cache (if used)
JDODataStoreCache cache = (JDODataStoreCache)pmf.getDataStoreCache();
OMFContext omfCtx = ((JDOPersistenceManagerFactory)pmf).getOMFContext();
if (cache != null)
{
datastoreCache = new JPADataStoreCache(omfCtx, cache.getLevel2Cache());
}
// Turn off loading of metadata from here if required
boolean allowMetadataLoad =
omfCtx.getPersistenceConfiguration().getBooleanProperty("datanucleus.metadata.allowLoadAtRuntime");
if (!allowMetadataLoad)
{
omfCtx.getMetaDataManager().setAllowMetaDataLoad(false);
}
}
/**
* Constructor when working in a J2SE environment.
* @param unitName Name of the "persistent-unit" to use
* @param overridingProps factory properties overriding those in the "persistence-unit"
*/
public EntityManagerFactoryImpl(String unitName, Map overridingProps)
{
if (unitMetaDataCache == null)
{
// Create our cache so we save on lookups
unitMetaDataCache = new HashMap<String, PersistenceUnitMetaData>();
}
// Find the "persistence-unit"
unitMetaData = EntityManagerFactoryImpl.unitMetaDataCache.get(unitName);
if (unitMetaData == null)
{
// Find all "META-INF/persistence.xml" files in the current thread loader CLASSPATH and parse them
// Create a temporary PMFContext so we have a parser
OMFContext pmfCtxt = new OMFContext(new PersistenceConfiguration(){});
pmfCtxt.setApi("JPA");
MetaDataManager metadataMgr = pmfCtxt.getMetaDataManager();
PersistenceFileMetaData[] files = metadataMgr.parsePersistenceFiles();
if (files == null)
{
// No "persistence.xml" files found
NucleusLogger.JPA.warn(LOCALISER.msg("EMF.NoPersistenceXML"));
//throw new NoPersistenceXmlException(LOCALISER.msg("EMF.NoPersistenceXML"));
}
else
{
for (int i=0;i<files.length;i++)
{
PersistenceUnitMetaData[] unitmds = files[i].getPersistenceUnits();
for (int j=0;j<unitmds.length;j++)
{
// Cache the "persistence-unit" for future reference
EntityManagerFactoryImpl.unitMetaDataCache.put(unitmds[j].getName(), unitmds[j]);
if (unitmds[j].getName().equals(unitName))
{
unitMetaData = unitmds[j];
unitMetaData.clearJarFiles(); // Jar files not applicable to J2SE [JPA 6.3]
}
}
}
}
if (unitMetaData == null)
{
// No "persistence-unit" of the same name as requested so nothing to manage the persistence of
NucleusLogger.JPA.warn(LOCALISER.msg("EMF.PersistenceUnitNotFound", unitName));
}
else
{
EntityManagerFactoryImpl.unitMetaDataCache.put(unitMetaData.getName(), unitMetaData);
}
}
// Check the provider is ok for our use
boolean validProvider = false;
if (unitMetaData != null)
{
if (unitMetaData.getProvider() == null ||
unitMetaData.getProvider().equals(PersistenceProviderImpl.class.getName()))
{
validProvider = true;
}
}
if (overridingProps != null &&
PersistenceProviderImpl.class.getName().equals(overridingProps.get("javax.persistence.provider")))
{
validProvider = true;
}
if (!validProvider)
{
// Not a valid provider
throw new NotProviderException(LOCALISER.msg("EMF.NotProviderForPersistenceUnit",
unitName));
}
// Initialise the PMF (even if unitMetaData is null)
pmf = initialisePMF(unitMetaData, overridingProps);
// Initialise the L2 cache (if used)
JDODataStoreCache cache = (JDODataStoreCache)pmf.getDataStoreCache();
OMFContext omfCtx = ((JDOPersistenceManagerFactory)pmf).getOMFContext();
if (cache != null)
{
datastoreCache = new JPADataStoreCache(omfCtx, cache.getLevel2Cache());
}
// Turn off loading of metadata from here if required
boolean allowMetadataLoad =
omfCtx.getPersistenceConfiguration().getBooleanProperty("datanucleus.metadata.allowLoadAtRuntime");
if (!allowMetadataLoad)
{
omfCtx.getMetaDataManager().setAllowMetaDataLoad(false);
}
}
/**
* Accessor for whether the EMF is managed by a container.
* @return Whether managed by a container
*/
public boolean isContainerManaged()
{
return containerManaged;
}
/**
* Method to close the factory.
*/
public void close()
{
closed = true;
}
/**
* Accessor for whether the factory is open
* @return Whether it is open
*/
public boolean isOpen()
{
return !closed;
}
/**
* Method to create an entity manager.
* @return The Entity Manager
*/
public EntityManager createEntityManager()
{
// TODO Pass in the PersistenceContextType from metadata (if any)
return newEntityManager(PersistenceContextType.EXTENDED, pmf);
}
/**
* Method to create an entity manager with the specified properties.
* This creates a new underlying PersistenceManagerFactory since each PMF is locked
* when created to stop config changes.
* @param overridingProps Properties to use for this manager
* @return The Entity Manager
*/
public EntityManager createEntityManager(Map overridingProps)
{
// Create a PMF to do the actual persistence, using the original persistence-unit, plus these properties
PersistenceManagerFactory thePMF = initialisePMF(unitMetaData, overridingProps);
// TODO Pass in the PersistenceContextType from metadata (if any)
return newEntityManager(PersistenceContextType.EXTENDED, thePMF);
}
/**
* Creates an {@link EntityManager}.
* Override if you want to return a different type that implements this interface.
* @param contextType The persistence context type
* @param pmf The Underlying PMF
*/
protected EntityManager newEntityManager(PersistenceContextType contextType, PersistenceManagerFactory pmf)
{
return new EntityManagerImpl(this, pmf, contextType);
}
/**
* Method to initialise a PersistenceManagerFactory that will control the persistence.
* If the unitMetaData is null will simply create a default PMF without initialising any MetaData etc.
* If there is a unitMetaData then all metadata for that unit will be loaded/initialised.
* @param unitMetaData The "persistence-unit" metadata (if any)
* @param overridingProps Properties to override all others
* @return The PersistenceManagerFactory
*/
protected PersistenceManagerFactory initialisePMF(PersistenceUnitMetaData unitMetaData, Map overridingProps)
{
// Create a PMF to do the actual persistence
Map props = new HashMap();
props.put("javax.jdo.PersistenceManagerFactoryClass", "org.datanucleus.jdo.JDOPersistenceManagerFactory");
props.put("datanucleus.persistenceApiName", "JPA"); // PMF in "JPA mode"
if (unitMetaData.getJtaDataSource() != null)
{
props.put("datanucleus.ConnectionFactoryName", unitMetaData.getJtaDataSource());
}
if (unitMetaData.getNonJtaDataSource() != null)
{
props.put("datanucleus.ConnectionFactory2Name", unitMetaData.getNonJtaDataSource());
}
if (unitMetaData != null)
{
if (unitMetaData.getTransactionType() != null)
{
props.put("datanucleus.TransactionType",
unitMetaData.getTransactionType().toString());
}
Properties unitProps = unitMetaData.getProperties();
if (unitProps != null)
{
// Props for this "persistence-unit"
props.putAll(unitProps);
}
}
if (overridingProps != null)
{
// Apply the overriding properties
props.putAll(overridingProps);
}
props.put("datanucleus.autoStartMechanism", "None"); // Don't allow autostart with JPA
props.remove("datanucleus.PersistenceUnitName"); // Don't specify the persistence-unit
PersistenceManagerFactory thePMF = JDOHelper.getPersistenceManagerFactory(props);
if (unitMetaData != null)
{
// Load up the MetaData implied by this "persistence-unit"
ObjectManagerFactoryImpl omf = (ObjectManagerFactoryImpl)thePMF;
omf.getOMFContext().getMetaDataManager().loadPersistenceUnit(unitMetaData, null);
}
return thePMF;
}
/**
* Get the properties and associated values that are in effect for the entity manager factory.
* Changing the contents of the map does not change the configuration in effect.
* @return properties
*/
public Map<String, Object> getProperties()
{
PersistenceConfiguration conf =
((ObjectManagerFactoryImpl)pmf).getOMFContext().getPersistenceConfiguration();
return conf.getOptions();
}
/**
* Get the names of the properties that are supported for use with the entity manager factory.
* These correspond to properties that may be passed to the methods of the EntityManagerFactory
* interface that take a properties argument. These include all standard properties as well as
* vendor-specific properties supported by the provider. These properties may or may not currently
* be in effect.
* @return properties and hints
*/
public Set<String> getSupportedProperties()
{
PersistenceConfiguration conf =
((ObjectManagerFactoryImpl)pmf).getOMFContext().getPersistenceConfiguration();
return conf.getSupportedOptions();
}
/**
* Accessor for the second level cache.
* @return Level 2 cache
*/
public Cache getCache()
{
return datastoreCache;
}
public Metamodel getMetamodel()
{
throw new UnsupportedOperationException("Dont yet support getMetamodel");
}
}