/**********************************************************************
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()
2011 Andy Jefferson - removed all use of PMF, using NucleusContext instead
...
**********************************************************************/
package org.datanucleus.api.jpa;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.Cache;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceContextType;
import javax.persistence.PersistenceException;
import javax.persistence.PersistenceUnitUtil;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.metamodel.Metamodel;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.persistence.spi.PersistenceUnitTransactionType;
import org.datanucleus.NucleusContext;
import org.datanucleus.PersistenceConfiguration;
import org.datanucleus.api.jpa.criteria.CriteriaBuilderImpl;
import org.datanucleus.api.jpa.exceptions.NotProviderException;
import org.datanucleus.api.jpa.metamodel.MetamodelImpl;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.PersistenceFileMetaData;
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.metadata.TransactionType;
import org.datanucleus.query.cache.QueryCompilationCache;
import org.datanucleus.store.ExecutionContext;
import org.datanucleus.store.ObjectProvider;
import org.datanucleus.store.connection.ConnectionFactory;
import org.datanucleus.store.connection.ConnectionResourceType;
import org.datanucleus.store.query.cache.QueryDatastoreCompilationCache;
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 JPAEntityManagerFactory implements EntityManagerFactory, PersistenceUnitUtil
{
/** Localiser for messages. */
protected static final Localiser LOCALISER = Localiser.getInstance("org.datanucleus.api.jpa.Localisation",
NucleusJPAHelper.class.getClassLoader());
/** Logger for enhancing. */
public static final NucleusLogger LOGGER = NucleusLogger.getLoggerInstance("DataNucleus.JPA");
/** Cache of EMF keyed by the name. Only used when having single-EMF property enabled. */
private static ConcurrentHashMap<String, JPAEntityManagerFactory> emfByName = null;
/** Context for this EMF. */
NucleusContext nucleusCtx = null;
/** 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;
/** Query results Cache. */
private JPAQueryCache queryCache = null;
/** Metamodel, for Criteria queries. */
private MetamodelImpl metamodel = null;
/**
* Constructor.
*/
public JPAEntityManagerFactory()
{
}
public NucleusContext getNucleusContext()
{
return nucleusCtx;
}
/**
* Constructor when working in a J2EE environment.
* @param unitInfo The "persistent-unit" info
* @param overridingProps factory properties overriding those in the "persistence-unit"
*/
public JPAEntityManagerFactory(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();
if (props != null)
{
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();
}
else
{
//create a new hashmap, because we cannot modify the user map
overridingProps = new HashMap(overridingProps);
}
// unit info will give us a javax.sql.DataSource instance, so we give that to context
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
{
// 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());
}
}
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
LOGGER.warn("Exception was caught when adding the class transformer. Ignoring it.", ise);
}
}
// Initialise the NucleusContext
nucleusCtx = initialiseNucleusContext(unitMetaData, overridingProps);
assertSingleton(unitMetaData.getName(), this);
// Turn off loading of metadata from here if required
boolean allowMetadataLoad =
nucleusCtx.getPersistenceConfiguration().getBooleanProperty("datanucleus.metadata.allowLoadAtRuntime");
if (!allowMetadataLoad)
{
nucleusCtx.getMetaDataManager().setAllowMetaDataLoad(false);
}
}
/**
* Convenience constructor to allow for dynamic persistence-unit creation.
* @param pumd Persistence unit metadata
* @param overridingProps Properties overriding those defined for this unit
*/
public JPAEntityManagerFactory(PersistenceUnitMetaData pumd, Map overridingProps)
{
if (unitMetaDataCache == null)
{
// Create our cache so we save on lookups
unitMetaDataCache = new HashMap<String, PersistenceUnitMetaData>();
}
// Check the provider is ok for our use
boolean validProvider = false;
if (pumd != null)
{
if (pumd.getProvider() == null ||
pumd.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", pumd.getName()));
}
// Initialise the context (even if unitMetaData is null)
nucleusCtx = initialiseNucleusContext(pumd, overridingProps);
unitMetaData = pumd;
unitMetaDataCache.put(pumd.getName(), pumd);
assertSingleton(pumd.getName(), this);
// Turn off loading of metadata from here if required
boolean allowMetadataLoad =
nucleusCtx.getPersistenceConfiguration().getBooleanProperty("datanucleus.metadata.allowLoadAtRuntime");
if (!allowMetadataLoad)
{
nucleusCtx.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 JPAEntityManagerFactory(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 = JPAEntityManagerFactory.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 context so we have a parser
NucleusContext nucleusCtx = new NucleusContext("JPA", overridingProps);
MetaDataManager metadataMgr = nucleusCtx.getMetaDataManager();
PersistenceFileMetaData[] files = metadataMgr.parsePersistenceFiles();
if (files == null)
{
// No "persistence.xml" files found
LOGGER.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
JPAEntityManagerFactory.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
LOGGER.warn(LOCALISER.msg("EMF.PersistenceUnitNotFound", unitName));
}
else
{
JPAEntityManagerFactory.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 context (even if unitMetaData is null)
nucleusCtx = initialiseNucleusContext(unitMetaData, overridingProps);
assertSingleton(unitMetaData.getName(), this);
// Turn off loading of metadata from here if required
boolean allowMetadataLoad =
nucleusCtx.getPersistenceConfiguration().getBooleanProperty("datanucleus.metadata.allowLoadAtRuntime");
if (!allowMetadataLoad)
{
nucleusCtx.getMetaDataManager().setAllowMetaDataLoad(false);
}
}
private static synchronized void assertSingleton(String puName, JPAEntityManagerFactory emf)
{
Boolean singleton =
emf.getNucleusContext().getPersistenceConfiguration().getBooleanObjectProperty("datanucleus.singletonEMFForName");
if (singleton != null && singleton)
{
// Check on singleton pattern
if (emfByName == null)
{
emfByName = new ConcurrentHashMap<String, JPAEntityManagerFactory>();
}
if (emfByName.containsKey(puName))
{
emf.close();
NucleusLogger.PERSISTENCE.warn("Requested EMF of name \"" + puName +
"\" but already exists and using singleton pattern, so returning existing EMF");
throw new SingletonEMFException("Requested EMF that already exists", emfByName.get(puName));
}
emfByName.putIfAbsent(puName, emf);
}
}
/**
* 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 synchronized void close()
{
assertIsClosed();
if (emfByName != null && unitMetaData != null)
{
// Closing so clean out from singleton pattern handler
emfByName.remove(unitMetaData.getName());
}
closed = true;
}
/**
* Accessor for whether the factory is open
* @return Whether it is open
*/
public boolean isOpen()
{
return !closed;
}
/**
* Accessor for the query results cache.
* @return Query results cache
*/
public JPAQueryCache getQueryCache()
{
assertIsClosed();
if (queryCache != null)
{
return queryCache;
}
queryCache = new JPAQueryCache(nucleusCtx.getStoreManager().getQueryManager().getQueryResultsCache());
return queryCache;
}
/**
* Accessor for the query generic compilation cache.
* @return Query generic compilation cache
*/
public QueryCompilationCache getQueryGenericCompilationCache()
{
return nucleusCtx.getStoreManager().getQueryManager().getQueryCompilationCache();
}
/**
* Accessor for the query datastore compilation cache.
* @return Query datastore compilation cache
*/
public QueryDatastoreCompilationCache getQueryDatastoreCompilationCache()
{
return nucleusCtx.getStoreManager().getQueryManager().getQueryDatastoreCompilationCache();
}
/**
* Method to create an entity manager.
* @return The Entity Manager
*/
public EntityManager createEntityManager()
{
assertIsClosed();
return newEntityManager(nucleusCtx,
containerManaged ? PersistenceContextType.TRANSACTION : PersistenceContextType.EXTENDED);
}
/**
* Method to create an entity manager with the specified properties.
* This creates a new underlying context since each EMF 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)
{
assertIsClosed();
// Create a NucleusContext to do the actual persistence, using the original persistence-unit, plus these properties
NucleusContext nucleusCtx = initialiseNucleusContext(unitMetaData, overridingProps);
PersistenceContextType persistenceContext = PersistenceContextType.EXTENDED;
if (containerManaged)
{
persistenceContext = PersistenceContextType.TRANSACTION;
}
return newEntityManager(nucleusCtx, persistenceContext);
}
/**
* Creates an {@link EntityManager}.
* Override if you want to return a different type that implements this interface.
* @param nucleusCtx Nucleus Context
* @param contextType The persistence context type
*/
protected EntityManager newEntityManager(NucleusContext nucleusCtx, PersistenceContextType contextType)
{
return new JPAEntityManager(this, nucleusCtx, contextType);
}
/**
* Method to initialise a PersistenceManagerFactory that will control the persistence.
* If the unitMetaData is null will simply create a default context 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 NucleusContext initialiseNucleusContext(PersistenceUnitMetaData unitMetaData, Map overridingProps)
{
// Build map of properties for the NucleusContext, with all properties in lower-case
// We use lower-case so we can detect presence of some properties, hence allowing case-insensitivity
Map<String, Object> props = new HashMap();
if (unitMetaData.getJtaDataSource() != null)
{
props.put("datanucleus.ConnectionFactoryName".toLowerCase(Locale.ENGLISH), unitMetaData.getJtaDataSource());
}
if (unitMetaData.getNonJtaDataSource() != null)
{
props.put("datanucleus.ConnectionFactory2Name".toLowerCase(Locale.ENGLISH), unitMetaData.getNonJtaDataSource());
}
if (unitMetaData.getTransactionType() != null)
{
props.put("datanucleus.TransactionType".toLowerCase(Locale.ENGLISH), unitMetaData.getTransactionType().toString());
}
Properties unitProps = unitMetaData.getProperties();
if (unitProps != null)
{
// Props for this "persistence-unit"
for (Object key : unitProps.keySet())
{
String propName = (String)key;
props.put(propName.toLowerCase(Locale.ENGLISH), unitProps.getProperty(propName));
}
}
// Set properties for persistence context
String persistenceContextTypeProp = null;
if (props.containsKey("datanucleus.jpa.persistencecontexttype"))
{
persistenceContextTypeProp = (String)props.get("datanucleus.jpa.persistencecontexttype");
}
if (overridingProps != null && overridingProps.containsKey("datanucleus.jpa.persistencecontexttype"))
{
persistenceContextTypeProp = (String)overridingProps.get("datanucleus.jpa.persistencecontexttype");
}
if (persistenceContextTypeProp != null && persistenceContextTypeProp.equalsIgnoreCase("transaction"))
{
// Need to detach instances at transaction commit
if (!props.containsKey("datanucleus.detachalloncommit"))
{
props.put("datanucleus.detachalloncommit", "true");
}
if (!props.containsKey("datanucleus.detachonclose"))
{
props.put("datanucleus.detachonclose", "false");
}
}
else if (persistenceContextTypeProp != null && persistenceContextTypeProp.equalsIgnoreCase("extended"))
{
// Need to keep instances active until close of EntityManager and then detach
if (!props.containsKey("datanucleus.detachalloncommit"))
{
props.put("datanucleus.detachalloncommit", "false");
}
if (!props.containsKey("datanucleus.detachonclose"))
{
props.put("datanucleus.detachonclose", "true");
}
}
else if (containerManaged)
{
// Need to detach instances at transaction commit
if (!props.containsKey("datanucleus.detachalloncommit"))
{
props.put("datanucleus.detachalloncommit", "true");
}
if (!props.containsKey("datanucleus.detachonclose"))
{
props.put("datanucleus.detachonclose", "false");
}
}
else
{
// Need to keep instances active until close of EntityManager and then detach
if (!props.containsKey("datanucleus.detachalloncommit"))
{
props.put("datanucleus.detachalloncommit", "false");
}
if (!props.containsKey("datanucleus.detachonclose"))
{
props.put("datanucleus.detachonclose", "true");
}
}
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
props.remove("datanucleus.jpa.persistencecontexttype"); // Processed above
// Extract any properties that affect NucleusContext startup
Map startupProps = null;
if (props != null)
{
// Possible startup properties to check for
for (String startupPropName : NucleusContext.STARTUP_PROPERTIES)
{
for (String currentPropName : props.keySet())
{
if (currentPropName.equalsIgnoreCase(startupPropName))
{
if (startupProps == null)
{
startupProps = new HashMap();
}
startupProps.put(startupPropName, props.get(currentPropName));
}
}
}
}
// Initialise the context for JPA
NucleusContext nucleusCtx = new NucleusContext("JPA", startupProps);
PersistenceConfiguration propConfig = nucleusCtx.getPersistenceConfiguration();
// Apply remaining persistence properties
Map ctxProps = new HashMap();
ctxProps.putAll(props);
if (!props.containsKey("datanucleus.transactiontype") &&
!props.containsKey("javax.jdo.option.transactiontype"))
{
// Default to RESOURCE_LOCAL txns
ctxProps.put("datanucleus.transactiontype", TransactionType.RESOURCE_LOCAL.toString());
}
else
{
// let TransactionType.JTA imply ResourceType.JTA
String transactionType = props.get("datanucleus.transactiontype") != null ?
(String)props.get("datanucleus.transactiontype") : (String)props.get("javax.jdo.option.transactiontype");
if (TransactionType.JTA.toString().equalsIgnoreCase(transactionType))
{
ctxProps.put(ConnectionFactory.DATANUCLEUS_CONNECTION_RESOURCE_TYPE.toLowerCase(Locale.ENGLISH),
ConnectionResourceType.JTA.toString());
ctxProps.put(ConnectionFactory.DATANUCLEUS_CONNECTION2_RESOURCE_TYPE.toLowerCase(Locale.ENGLISH),
ConnectionResourceType.JTA.toString());
}
}
propConfig.setPersistenceProperties(ctxProps);
// Load up the MetaData implied by this "persistence-unit"
nucleusCtx.getMetaDataManager().loadPersistenceUnit(unitMetaData, null);
nucleusCtx.initialise();
// Load up any persistence-unit classes into the StoreManager
boolean loadClasses = propConfig.getBooleanProperty("datanucleus.persistenceunitloadclasses");
if (loadClasses)
{
// Load all classes into StoreManager so it knows about them
Collection<String> loadedClasses = nucleusCtx.getMetaDataManager().getClassesWithMetaData();
nucleusCtx.getStoreManager().addClasses(loadedClasses.toArray(new String[loadedClasses.size()]), nucleusCtx.getClassLoaderResolver(null));
}
return nucleusCtx;
}
/**
* 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()
{
return nucleusCtx.getPersistenceConfiguration().getPersistenceProperties();
}
/**
* 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()
{
return nucleusCtx.getPersistenceConfiguration().getSupportedProperties();
}
protected void assertIsClosed()
{
if (closed)
{
throw new IllegalStateException("EntityManagerFactory is already closed");
}
}
/**
* Accessor for the second level cache.
* @return Level 2 cache
*/
public Cache getCache()
{
assertIsClosed();
if (datastoreCache == null)
{
// Initialise the L2 cache (if used)
if (nucleusCtx.hasLevel2Cache())
{
datastoreCache = new JPADataStoreCache(nucleusCtx, nucleusCtx.getLevel2Cache());
}
}
return datastoreCache;
}
public Metamodel getMetamodel()
{
assertIsClosed();
if (metamodel != null)
{
return metamodel;
}
metamodel = new MetamodelImpl(nucleusCtx.getMetaDataManager());
return metamodel;
}
public CriteriaBuilder getCriteriaBuilder()
{
assertIsClosed();
return new CriteriaBuilderImpl((MetamodelImpl) getMetamodel());
}
/* (non-Javadoc)
* @see javax.persistence.EntityManagerFactory#getPersistenceUnitUtil()
*/
public PersistenceUnitUtil getPersistenceUnitUtil()
{
return this;
}
/* (non-Javadoc)
* @see javax.persistence.PersistenceUnitUtil#getIdentifier(java.lang.Object)
*/
public Object getIdentifier(Object entity)
{
return nucleusCtx.getApiAdapter().getIdForObject(entity);
}
/* (non-Javadoc)
* @see javax.persistence.PersistenceUnitUtil#isLoaded(java.lang.Object, java.lang.String)
*/
public boolean isLoaded(Object entity, String attrName)
{
ExecutionContext ec = nucleusCtx.getApiAdapter().getExecutionContext(entity);
if (ec == null)
{
return false;
}
ObjectProvider sm = ec.findObjectProvider(entity);
if (sm == null)
{
// Not managed
return false;
}
AbstractClassMetaData cmd = nucleusCtx.getMetaDataManager().getMetaDataForClass(entity.getClass(),
nucleusCtx.getClassLoaderResolver(entity.getClass().getClassLoader()));
if (cmd == null)
{
// No metadata
return false;
}
return nucleusCtx.getApiAdapter().isLoaded(sm, cmd.getAbsolutePositionOfMember(attrName));
}
/* (non-Javadoc)
* @see javax.persistence.PersistenceUnitUtil#isLoaded(java.lang.Object)
*/
public boolean isLoaded(Object entity)
{
if (nucleusCtx.getApiAdapter().getObjectState(entity).equals("hollow"))
{
return false;
}
return true;
}
/**
* Return an object of the specified type to allow access to the provider-specific API.
* If the provider's EntityManagerFactory implementation does not support the specified class, the
* PersistenceException is thrown.
* @param cls the class of the object to be returned. This is normally either the underlying
* EntityManagerFactory implementation class or an interface that it implements.
* @return an instance of the specified class
* @throws PersistenceException if the provider does not support the call.
*/
public <T> T unwrap(Class<T> cls)
{
if (NucleusContext.class.isAssignableFrom(cls))
{
return (T) nucleusCtx;
}
throw new PersistenceException("Not yet supported");
}
}