All Downloads are FREE. Search and download functionalities are using the official Maven repository.

oracle.toplink.essentials.internal.ejb.cmp3.base.EntityManagerImpl Maven / Gradle / Ivy

There is a newer version: 2.1-60f
Show newest version
/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
 * 
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License. You can obtain
 * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
 * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [year]
 * [name of copyright owner]"
 * 
 * Contributor(s):
 * 
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package oracle.toplink.essentials.internal.ejb.cmp3.base;

import java.util.Vector;
import java.util.Map;

import javax.persistence.EntityExistsException;
import javax.persistence.EntityNotFoundException;
import javax.persistence.LockModeType;
import javax.persistence.PersistenceException;

import java.util.HashMap;

import oracle.toplink.essentials.descriptors.ClassDescriptor;
import oracle.toplink.essentials.exceptions.TopLinkException;
import oracle.toplink.essentials.exceptions.ValidationException;
import oracle.toplink.essentials.expressions.Expression;
import oracle.toplink.essentials.internal.ejb.cmp3.transaction.base.TransactionWrapperImpl;
import oracle.toplink.essentials.internal.localization.ExceptionLocalization;
import oracle.toplink.essentials.internal.sessions.IsolatedClientSession;
import oracle.toplink.essentials.internal.sessions.MergeManager;
import oracle.toplink.essentials.internal.helper.IdentityHashtable;
import oracle.toplink.essentials.internal.descriptors.OptimisticLockingPolicy;
import oracle.toplink.essentials.descriptors.VersionLockingPolicy;
import oracle.toplink.essentials.queryframework.*;
import oracle.toplink.essentials.sessions.Session;
import oracle.toplink.essentials.sessions.UnitOfWork;
import oracle.toplink.essentials.threetier.ServerSession;
import oracle.toplink.essentials.tools.sessionmanagement.SessionManager;
import oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider;
import oracle.toplink.essentials.config.TopLinkProperties;


/**
* 

* Purpose: Contains the implementation of the EntityManager. *

* Description: This class provides the implementation for the combined TopLink * and EJB3.0 EntityManager class. *

* Responsibilities:It is responcible for tracking transaction state and the * objects within that transaction. * @see javax.persistence.EntityManager * @see oracle.toplink.essentials.ejb.cmp3.EntityManager */ /* @author gyorke * @since TopLink 10.1.3 EJB 3.0 Preview */ public abstract class EntityManagerImpl { protected TransactionWrapperImpl transaction = null; protected boolean isOpen = true; protected RepeatableWriteUnitOfWork extendedPersistenceContext; //THis attribute references the ServerSession that this deployement is using. //This is a simple mechanism to reduce the number of SessionManager accesses. protected ServerSession serverSession; // References the factory that has created this entity manager // to make sure that the factory is not garbage collected protected EntityManagerFactoryImpl factory; //We have not removed these flags because we may want to use them at a later date to provide transactional EntityManagers in JAVA SE protected boolean extended; protected boolean propagatePersistenceContext; //gf3334, force begin early transaction flag. protected boolean beginEarlyTransaction = false; //gf3334, this is place holder for properties that passed from createEntityManager protected Map properties; protected abstract void setJTATransactionWrapper(); protected abstract void setEntityTransactionWrapper(); /** * Internal method. Indicates whether flushMode is AUTO. * @return boolean */ public abstract boolean isFlushModeAUTO(); /** * Constructor returns an EntityManager assigned to the a particular ServerSession. * @param sessionName the ServerSession name that should be used. * This constructor can potentially throw TopLink exceptions regarding the existence, or * errors with the specified session. */ public EntityManagerImpl(String sessionName, boolean propagatePersistenceContext, boolean extended){ this((ServerSession) SessionManager.getManager().getSession(sessionName), null, propagatePersistenceContext, extended); } /** * Constructor called from the EntityManagerFactory to create an EntityManager * @param serverSession the serverSession assigned to this deployment. */ public EntityManagerImpl(ServerSession serverSession, boolean propagatePersistenceContext, boolean extended){ this(serverSession, null, propagatePersistenceContext, extended); } /** * Constructor called from the EntityManagerFactory to create an EntityManager * @param serverSession the serverSession assigned to this deployment. * Note: The properties argument is provided to allow properties to be passed into this EntityManager, * but there are currently no such properties implemented */ public EntityManagerImpl(ServerSession serverSession, Map properties, boolean propagatePersistenceContext, boolean extended){ this.serverSession = serverSession; detectTransactionWrapper(); this.extended = true; this.propagatePersistenceContext = false; this.properties=properties; setBeginEarlyTransaction(); } /** * Constructor called from the EntityManagerFactory to create an EntityManager * @param factory the EntityMangerFactoryImpl that created this entity manager. * Note: The properties argument is provided to allow properties to be passed into this EntityManager, * but there are currently no such properties implemented */ public EntityManagerImpl(EntityManagerFactoryImpl factory, Map properties, boolean propagatePersistenceContext, boolean extended){ this.factory = factory; this.serverSession = factory.getServerSession(); detectTransactionWrapper(); this.extended = true; this.propagatePersistenceContext = false; this.properties=properties; setBeginEarlyTransaction(); } /** * Clear the persistence context, causing all managed * entities to become detached. Changes made to entities that * have not been flushed to the database will not be * persisted. */ public void clear(){ try { verifyOpen(); if (this.isExtended()){ if(this.extendedPersistenceContext != null){ if (checkForTransaction(false) == null){ // clear all change sets and cache this.extendedPersistenceContext.clearForClose(true); this.extendedPersistenceContext = null; } else { // clear all change sets created after the last flush and cache this.extendedPersistenceContext.clear(true); } } } else { transaction.clear(); } } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Internal method called by EntityTransactionImpl class in case of transaction rollback. * The caller is responsible for releasing extendedPersistenceContext and it's parent. */ public void removeExtendedPersistenceContext(){ this.extendedPersistenceContext = null; } /** * If in a transaction this method will check for existence and register the object if * it is new. The instance of the entity provided will become managed. * @param entity * @throws IllegalArgumentException if the given Object is not an entity */ public void persist(Object entity){ try { verifyOpen(); if (entity == null){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("not_an_entity", new Object[] {entity})); } try { getActivePersistenceContext(checkForTransaction(!isExtended())).registerNewObjectForPersist(entity, new IdentityHashtable()); } catch (RuntimeException e) { if (ValidationException.class.isAssignableFrom(e.getClass())){ throw new EntityExistsException(e.getLocalizedMessage() , e); } throw e; } } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Merge the state of the given entity into the * current persistence context, using the unqualified * class name as the entity name. * @param entity * @return the instance that the state was merged to * @throws IllegalArgumentException if given Object is not an entity or is a removed entity */ protected Object mergeInternal(Object entity){ if (entity == null){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("not_an_entity", new Object[] {entity})); } //gf830 - merging a removed entity should throw exception if (getActivePersistenceContext(checkForTransaction(!isExtended())).getDeletedObjects().contains(entity)){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("cannot_merge_removed_entity", new Object[]{entity})); } try { return getActivePersistenceContext(checkForTransaction(!isExtended())).mergeCloneWithReferences(entity, MergeManager.CASCADE_BY_MAPPING, true); } catch (oracle.toplink.essentials.exceptions.OptimisticLockException ole) { throw new javax.persistence.OptimisticLockException(ole); } } /** * Remove the instance. * @param entity * @throws IllegalArgumentException if Object passed in is not an entity */ public void remove(Object entity){ try { verifyOpen(); if (entity == null){ //gf732 - check for null throw new IllegalArgumentException(ExceptionLocalization.buildMessage("not_an_entity", new Object[] {entity})); } try{ getActivePersistenceContext(checkForTransaction(!isExtended())).performRemove(entity, new IdentityHashtable()); }catch (RuntimeException e){ throw e; } } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Find by primary key. * @param entityName * @param primaryKey * @return the found entity instance * @throws IllegalArgumentException if the first argument does not indicate an entity or if the * second argument is not a valid type for that entity's primaryKey */ public Object find(String entityName, Object primaryKey){ try { verifyOpen(); Session session = getActiveSession(); ClassDescriptor descriptor = session.getDescriptorForAlias(entityName); if (descriptor == null || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("unknown_entitybean_name", new Object[] {entityName})); } if (primaryKey == null){ //gf721 - check for null PK throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_pk")); } if ( ((CMP3Policy)descriptor.getCMPPolicy()).getPKClass() != null && !((CMP3Policy)descriptor.getCMPPolicy()).getPKClass().isAssignableFrom(primaryKey.getClass())){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("invalid_pk_class", new Object[] {((CMP3Policy)descriptor.getCMPPolicy()).getPKClass(), primaryKey.getClass()})); } return findInternal(descriptor, session, primaryKey); } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Find by primary key. * @param entityClass * @param primaryKey * @return the found entity instance or null * if the entity does not exist * @throws IllegalArgumentException if the first argument does * not denote an entity type or the second argument is not a valid type for that * entity's primary key */ protected Object findInternal(Class entityClass, Object primaryKey) { Session session = getActiveSession(); ClassDescriptor descriptor = session.getDescriptor(entityClass); if (descriptor == null || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("unknown_bean_class", new Object[]{ entityClass})); } if (primaryKey == null){ //gf721 - check for null PK throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_pk")); } if ( ((CMP3Policy)descriptor.getCMPPolicy()).getPKClass() != null && !((CMP3Policy)descriptor.getCMPPolicy()).getPKClass().isAssignableFrom(primaryKey.getClass())){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("invalid_pk_class", new Object[] {((CMP3Policy)descriptor.getCMPPolicy()).getPKClass(), primaryKey.getClass()})); } return findInternal(descriptor, session, primaryKey); } /** * Find by primary key. * @param descriptor * @param session * @param primaryKey * @return the found entity instance or null * if the entity does not exist * @throws IllegalArgumentException if the first argument does * not denote an entity type or the second argument is not a valid type for that * entity's primary key */ protected static Object findInternal(ClassDescriptor descriptor, Session session, Object primaryKey){ Vector pk; if(primaryKey instanceof Vector) { pk = (Vector)primaryKey; } else { pk = ((CMP3Policy) descriptor.getCMPPolicy()).createPkVectorFromKey(primaryKey, (oracle.toplink.essentials.internal.sessions.AbstractSession)session); } ReadObjectQuery query = new ReadObjectQuery(descriptor.getJavaClass()); query.setSelectionKey(pk); query.conformResultsInUnitOfWork(); return session.executeQuery(query); } /** * Synchronize the persistence context with the * underlying database. */ public void flush(){ try { verifyOpen(); try { getActivePersistenceContext(checkForTransaction(true)).writeChanges(); }catch (RuntimeException e) { if (TopLinkException.class.isAssignableFrom(e.getClass())){ throw new PersistenceException(e); } throw e; } } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } protected void detectTransactionWrapper(){ if (this.serverSession.hasExternalTransactionController()){ setJTATransactionWrapper(); } else { setEntityTransactionWrapper(); } } /** * Refresh the state of the instance from the * database. * @param entity */ public void refresh(Object entity){ try { verifyOpen(); UnitOfWork uow = getActivePersistenceContext(checkForTransaction(!isExtended())); if(!contains(entity, uow)) { throw new IllegalArgumentException(ExceptionLocalization.buildMessage("cant_refresh_not_managed_object", new Object[]{entity})); } ReadObjectQuery query = new ReadObjectQuery(); query.setSelectionObject(entity); query.refreshIdentityMapResult(); query.cascadeByMapping(); query.setLockMode(ObjectBuildingQuery.NO_LOCK); Object refreshedEntity = null; refreshedEntity = uow.executeQuery(query); if(refreshedEntity == null) { //bug3323, should also invalidate the shared cached object if object not exists in DB. uow.getParent().getIdentityMapAccessor().invalidateObject(entity); throw new EntityNotFoundException(ExceptionLocalization.buildMessage("entity_no_longer_exists_in_db", new Object[]{entity})); } } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Check if the instance belongs to the current persistence * context. * @param entity * @return * @throws IllegalArgumentException if given Object is not an entity */ public boolean contains(Object entity){ try { verifyOpen(); if (entity == null){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("not_an_entity", new Object[] {entity})); } ClassDescriptor descriptor = (ClassDescriptor)getServerSession().getDescriptors().get(entity.getClass()); if (descriptor == null || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()){ throw new IllegalArgumentException(ExceptionLocalization.buildMessage("not_an_entity", new Object[]{entity})); } if( (!hasActivePersistenceContext())) { return false; } return contains(entity,getActivePersistenceContext(checkForTransaction(false))); } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Check if the instance belongs to the current persistence * context. * @param entity * @param uow * @return */ protected boolean contains(Object entity, UnitOfWork uow){ return ((oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl)uow).isObjectRegistered(entity) && !((oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl)uow).isObjectDeleted(entity); } /** * This method returns the current session to the requestor. The current session * will be a the active UnitOfWork within a transaction and will be a 'scrap' * UnitOfWork outside of a transaction. The caller is conserned about the results * then the getSession() or getUnitOfWork() API should be called. */ public Session getActiveSession(){ Object txn = checkForTransaction(false); if ( txn == null && ! this.isExtended() ){ return this.serverSession.acquireNonSynchronizedUnitOfWork(); }else{ return getActivePersistenceContext(txn); } } /** * Return the underlying provider object for the EntityManager, * if available. The result of this method is implementation * specific. */ public Object getDelegate(){ try { verifyOpen(); return this; } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * The method search for user defined property passed in from EntityManager, if it is not found then * search for it from EntityManagerFactory properties. * @param name * @return */ public Object getProperty(String name) { Object propertyValue=null; if(name==null){ return null; } if(this.properties!=null){ propertyValue=this.properties.get(name); } if(propertyValue==null){ propertyValue=this.factory.getProperty(name); } return propertyValue; } /** * This method will return the active UnitOfWork */ public UnitOfWork getUnitOfWork(){ return getActivePersistenceContext(checkForTransaction(false)); } /** * This method will return a Session outside of a transaction and null within a transaction. */ public Session getSession(){ if (checkForTransaction(false) == null){ return this.serverSession.acquireNonSynchronizedUnitOfWork(); } return null; } /** * Return the underlying server session */ public ServerSession getServerSession(){ return this.serverSession; } /** * This method is used to create a query using SQL. The class, must be the expected * return type. */ protected DatabaseQuery createNativeQueryInternal(String sqlString, Class resultType){ ReadAllQuery query = new ReadAllQuery(resultType); query.setSQLString(sqlString); query.setIsUserDefined(true); return query; } /** * This method is used to create a query using a Toplink Expression and the return type. */ protected DatabaseQuery createQueryInternal(Expression expression, Class resultType){ ReadAllQuery query = new ReadAllQuery(resultType); query.setSelectionCriteria(expression); return query; } /** *

Closes this EntityManager. * *

After invoking this method, all methods on the instance will throw an * {@link IllegalStateException} except for {@link #isOpen}, which will return * false .

* *

This should be called when a method is finished with the EntityManager in a * bean-managed transaction environment or when executed outside a container. Closing * of the EntityManager is handled by the container when using container-managed * transactions.

*/ public void close(){ try { verifyOpen(); isOpen = false; factory = null; serverSession = null; if(extendedPersistenceContext != null) { if (checkForTransaction(false) == null){ // clear change sets but keep the cache extendedPersistenceContext.clearForClose(false); } else { // when commit will be called, all change sets will be cleared, but the cache will be kept extendedPersistenceContext.setShouldClearForCloseInsteadOfResume(true); } extendedPersistenceContext = null; } } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Indicates if this EntityManager is an extended Persistence Context */ public boolean isExtended(){ return this.extended; } /** * Indicates whether or not this entity manager is open. Returns true until * a call to {@link #close} is made. */ public boolean isOpen(){ return isOpen && factory.isOpen(); } /** * Set the lock mode for an entity object contained in the persistence context. * @param entity * @param lockMode * @throws PersistenceException if an unsupported lock call is made * @throws IllegalArgumentException if the instance is not an entity or is a detached entity * @throws TransactionRequiredException if there is no transaction */ public void lock(Object entity, LockModeType lockMode){ try { verifyOpen(); RepeatableWriteUnitOfWork context = getActivePersistenceContext(checkForTransaction(!isExtended())); ClassDescriptor descriptor = context.getDescriptor(entity); OptimisticLockingPolicy lockingPolicy = descriptor.getOptimisticLockingPolicy(); if ((lockingPolicy == null) || !(lockingPolicy instanceof VersionLockingPolicy)){ throw new PersistenceException(ExceptionLocalization.buildMessage("ejb30-wrong-lock_called_without_version_locking-index", null)); } context.forceUpdateToVersionField(entity, (lockMode == LockModeType.WRITE)); } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } public void verifyOpen(){ if (!isOpen()){ throw new IllegalStateException(ExceptionLocalization.buildMessage("operation_on_closed_entity_manager")); } } public RepeatableWriteUnitOfWork getActivePersistenceContext(Object txn) { if (this.isExtended()){ // use local uow as it will be local to this EM and not on the txn if (this.extendedPersistenceContext == null || !this.extendedPersistenceContext.isActive()){ this.extendedPersistenceContext = new RepeatableWriteUnitOfWork(this.serverSession.acquireClientSession()); this.extendedPersistenceContext.setResumeUnitOfWorkOnTransactionCompletion(true); this.extendedPersistenceContext.setShouldCascadeCloneToJoinedRelationship(true); this.extendedPersistenceContext.setProperties(properties); if (txn != null) { // if there is an active txn we must register with it on creation of PC transaction.registerUnitOfWorkWithTxn(this.extendedPersistenceContext); } } //gf3334, force persistencecontext begin early transaction if conditions meet. if (this.beginEarlyTransaction && txn != null && !this.extendedPersistenceContext.isInTransaction() ) { this.extendedPersistenceContext.beginEarlyTransaction(); } return this.extendedPersistenceContext; }else{ return getTransactionalUnitOfWork_new(txn); } } /* * This method is used in contains to check if we already have a persistence context. * If there is no active persistence context the method returns false */ private boolean hasActivePersistenceContext() { if (isExtended() && (this.extendedPersistenceContext == null || !this.extendedPersistenceContext.isActive())) return false; else return true; } protected RepeatableWriteUnitOfWork getTransactionalUnitOfWork_new(Object tnx) { return transaction.getTransactionalUnitOfWork(tnx); } protected Object checkForTransaction(boolean validateExistence) { return transaction.checkForTransaction(validateExistence); } public boolean shouldFlushBeforeQuery(){ Object foundTransaction = checkForTransaction(false); if ((foundTransaction!=null) && transaction.shouldFlushBeforeQuery(getActivePersistenceContext(foundTransaction))){ return true; } return false; } //This is used to determine if the Persistence Contexts (in the form of UOWs) //should be propagated or not, which effectively means we will use the //active unit of work public boolean shouldPropagatePersistenceContext(){ return this.propagatePersistenceContext; } //Indicate the early transaction should be forced to start. public boolean shouldBeginEarlyTransaction(){ return this.beginEarlyTransaction; } /** * Indicate to the EntityManager that a JTA transaction is * active. This method should be called on a JTA application * managed EntityManager that was created outside the scope * of the active transaction to associate it with the current * JTA transaction. * @throws TransactionRequiredException if there is * no transaction. */ public void joinTransaction(){ try { verifyOpen(); transaction.registerUnitOfWorkWithTxn(getActivePersistenceContext(checkForTransaction(true))); } catch (RuntimeException e) { this.setRollbackOnly(); throw e; } } /** * Internal method. Sets transaction to rollback only. */ protected void setRollbackOnly() { this.transaction.setRollbackOnlyInternal(); } /** * Internal method, set begin early transaction if property has been specified. */ private void setBeginEarlyTransaction(){ String beginEarlyTransactionProperty = (String)getProperty(TopLinkProperties.JOIN_EXISTING_TRANSACTION); if(beginEarlyTransactionProperty!=null){ this.beginEarlyTransaction="true".equalsIgnoreCase(beginEarlyTransactionProperty); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy