KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > ejb > cmp3 > base > EntityManagerImpl


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22

23 package oracle.toplink.essentials.internal.ejb.cmp3.base;
24
25 import java.util.Vector JavaDoc;
26 import java.util.Map JavaDoc;
27
28 import javax.persistence.EntityExistsException;
29 import javax.persistence.EntityNotFoundException;
30 import javax.persistence.LockModeType;
31 import javax.persistence.PersistenceException;
32
33 import oracle.toplink.essentials.descriptors.ClassDescriptor;
34 import oracle.toplink.essentials.exceptions.TopLinkException;
35 import oracle.toplink.essentials.exceptions.ValidationException;
36 import oracle.toplink.essentials.expressions.Expression;
37 import oracle.toplink.essentials.internal.ejb.cmp3.transaction.base.TransactionWrapperImpl;
38 import oracle.toplink.essentials.internal.localization.ExceptionLocalization;
39 import oracle.toplink.essentials.internal.sessions.MergeManager;
40 import oracle.toplink.essentials.internal.helper.IdentityHashtable;
41 import oracle.toplink.essentials.internal.descriptors.OptimisticLockingPolicy;
42 import oracle.toplink.essentials.descriptors.VersionLockingPolicy;
43 import oracle.toplink.essentials.queryframework.*;
44 import oracle.toplink.essentials.sessions.Session;
45 import oracle.toplink.essentials.sessions.UnitOfWork;
46 import oracle.toplink.essentials.threetier.ServerSession;
47 import oracle.toplink.essentials.tools.sessionmanagement.SessionManager;
48
49 /**
50 * <p>
51 * <b>Purpose</b>: Contains the implementation of the EntityManager.
52 * <p>
53 * <b>Description</b>: This class provides the implementation for the combined TopLink
54 * and EJB3.0 EntityManager class.
55 * <p>
56 * <b>Responsibilities</b>:It is responcible for tracking transaction state and the
57 * objects within that transaction.
58 * @see javax.persistence.EntityManager
59 * @see oracle.toplink.essentials.ejb.cmp3.EntityManager
60 */

61
62 /* @author gyorke
63  * @since TopLink 10.1.3 EJB 3.0 Preview
64  */

65
66
67 public abstract class EntityManagerImpl {
68    
69     protected TransactionWrapperImpl transaction = null;
70     protected boolean isOpen = true;
71     
72     protected RepeatableWriteUnitOfWork extendedPersistenceContext;
73     //THis attribute references the ServerSession that this deployement is using.
74
//This is a simple mechanism to reduce the number of SessionManager accesses.
75
protected ServerSession serverSession;
76     // References the factory that has created this entity manager
77
// to make sure that the factory is not garbage collected
78
protected EntityManagerFactoryImpl factory;
79     
80     //We have not removed these flags because we may want to use them at a later date to provide transactional EntityManagers in JAVA SE
81
protected boolean extended;
82     protected boolean propagatePersistenceContext;
83
84     protected abstract void setJTATransactionWrapper();
85     protected abstract void setEntityTransactionWrapper();
86     /**
87     * Internal method. Indicates whether flushMode is AUTO.
88     * @return boolean
89     */

90     public abstract boolean isFlushModeAUTO();
91     
92     /**
93      * Constructor returns an EntityManager assigned to the a particular ServerSession.
94      * @param sessionName the ServerSession name that should be used.
95      * This constructor can potentially throw TopLink exceptions regarding the existence, or
96      * errors with the specified session.
97      */

98     public EntityManagerImpl(String JavaDoc sessionName, boolean propagatePersistenceContext, boolean extended){
99         this((ServerSession) SessionManager.getManager().getSession(sessionName), null, propagatePersistenceContext, extended);
100     }
101
102     /**
103      * Constructor called from the EntityManagerFactory to create an EntityManager
104      * @param serverSession the serverSession assigned to this deployment.
105      */

106     public EntityManagerImpl(ServerSession serverSession, boolean propagatePersistenceContext, boolean extended){
107         this(serverSession, null, propagatePersistenceContext, extended);
108     }
109
110
111     /**
112      * Constructor called from the EntityManagerFactory to create an EntityManager
113      * @param serverSession the serverSession assigned to this deployment.
114      * Note: The properties argument is provided to allow properties to be passed into this EntityManager,
115      * but there are currently no such properties implemented
116      */

117     public EntityManagerImpl(ServerSession serverSession, Map JavaDoc properties, boolean propagatePersistenceContext, boolean extended){
118         this.serverSession = serverSession;
119         detectTransactionWrapper();
120         this.extended = true;
121         this.propagatePersistenceContext = false;
122     }
123
124     /**
125      * Constructor called from the EntityManagerFactory to create an EntityManager
126      * @param factory the EntityMangerFactoryImpl that created this entity manager.
127      * Note: The properties argument is provided to allow properties to be passed into this EntityManager,
128      * but there are currently no such properties implemented
129      */

130     public EntityManagerImpl(EntityManagerFactoryImpl factory, Map JavaDoc properties, boolean propagatePersistenceContext, boolean extended){
131         this.factory = factory;
132         this.serverSession = factory.getServerSession();
133         detectTransactionWrapper();
134         this.extended = true;
135         this.propagatePersistenceContext = false;
136     }
137
138     /**
139     * Clear the persistence context, causing all managed
140     * entities to become detached. Changes made to entities that
141     * have not been flushed to the database will not be
142     * persisted.
143     */

144     public void clear(){
145         if (this.isExtended()){
146             if (this.extendedPersistenceContext != null) {
147                 if (checkForTransaction(false) != null){
148                     this.extendedPersistenceContext.clear();
149                 }else{
150                     this.extendedPersistenceContext = null;
151                 }
152             }
153         } else {
154             transaction.clear();
155         }
156     }
157
158     /**
159      * If in a transaction this method will check for existence and register the object if
160      * it is new. The instance of the entity provided will become managed.
161      * @param entity
162      * @throws IllegalArgumentException if the given Object is not an entity
163      */

164     public void persist(Object JavaDoc entity){
165         verifyOpen();
166         if (entity == null){
167             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[] {entity}));
168         }
169         try {
170             getActivePersistenceContext(checkForTransaction(!isExtended())).registerNewObjectForPersist(entity, new IdentityHashtable());
171         } catch (RuntimeException JavaDoc e) {
172             this.transaction.setRollbackOnlyInternal();
173             if (ValidationException.class.isAssignableFrom(e.getClass())){
174                 throw new EntityExistsException(e.getLocalizedMessage() , e);
175             }
176             throw e;
177         }
178     }
179     
180     /**
181     * Merge the state of the given entity into the
182     * current persistence context, using the unqualified
183     * class name as the entity name.
184     * @param entity
185     * @return the instance that the state was merged to
186     * @throws IllegalArgumentException if given Object is not an entity or is a removed entity
187     */

188     protected Object JavaDoc mergeInternal(Object JavaDoc entity){
189         verifyOpen();
190         if (entity == null){
191             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[] {entity}));
192         }
193         //gf830 - merging a removed entity should throw exception
194
if (getActivePersistenceContext(checkForTransaction(!isExtended())).getDeletedObjects().contains(entity)){
195             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("cannot_merge_removed_entity", new Object JavaDoc[]{entity}));
196         }
197         try {
198             return getActivePersistenceContext(checkForTransaction(!isExtended())).mergeCloneWithReferences(entity, MergeManager.CASCADE_BY_MAPPING);
199         } catch (oracle.toplink.essentials.exceptions.OptimisticLockException ole) {
200             throw new javax.persistence.OptimisticLockException(ole);
201         }
202     }
203     
204     /**
205     * Remove the instance.
206     * @param entity
207     * @throws IllegalArgumentException if Object passed in is not an entity
208     */

209     public void remove(Object JavaDoc entity){
210             verifyOpen();
211             if (entity == null){ //gf732 - check for null
212
throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[] {entity}));
213             }
214             try{
215                 getActivePersistenceContext(checkForTransaction(!isExtended())).performRemove(entity, new IdentityHashtable());
216             }catch (RuntimeException JavaDoc e){
217                 this.transaction.setRollbackOnlyInternal();
218                 throw e;
219             }
220     }
221     
222     /**
223     * Find by primary key.
224     * @param entityName
225     * @param primaryKey
226     * @return the found entity instance
227     * @throws IllegalArgumentException if the first argument does not indicate an entity or if the
228     * second argument is not a valid type for that entity's primaryKey
229     */

230     public Object JavaDoc find(String JavaDoc entityName, Object JavaDoc primaryKey){
231             verifyOpen();
232             try{
233                 Session session = getActiveSession();
234                 ClassDescriptor descriptor = session.getDescriptorForAlias(entityName);
235                 if (descriptor == null || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()){
236                     throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("unknown_entitybean_name", new Object JavaDoc[] {entityName}));
237                 }
238                 if (primaryKey == null){ //gf721 - check for null PK
239
throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("null_pk"));
240                 }
241                 if ( ((CMP3Policy)descriptor.getCMPPolicy()).getPKClass() != null && !((CMP3Policy)descriptor.getCMPPolicy()).getPKClass().isAssignableFrom(primaryKey.getClass())){
242                     throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("invalid_pk_class", new Object JavaDoc[] {((CMP3Policy)descriptor.getCMPPolicy()).getPKClass(), primaryKey.getClass()}));
243                 }
244                 return findInternal(descriptor, session, primaryKey);
245             }catch (RuntimeException JavaDoc e){
246                 this.transaction.setRollbackOnlyInternal();
247                 throw e;
248             }
249     }
250     
251     /**
252     * Find by primary key.
253     * @param entityClass
254     * @param primaryKey
255     * @return the found entity instance or null
256     * if the entity does not exist
257     * @throws IllegalArgumentException if the first argument does
258     * not denote an entity type or the second argument is not a valid type for that
259     * entity's primary key
260     */

261     protected Object JavaDoc findInternal(Class JavaDoc entityClass, Object JavaDoc primaryKey) {
262         verifyOpen();
263         Session session = getActiveSession();
264         ClassDescriptor descriptor = session.getDescriptor(entityClass);
265         if (descriptor == null || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()){
266             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("unknown_bean_class", new Object JavaDoc[]{ entityClass}));
267         }
268         if (primaryKey == null){ //gf721 - check for null PK
269
throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("null_pk"));
270         }
271         if ( ((CMP3Policy)descriptor.getCMPPolicy()).getPKClass() != null && !((CMP3Policy)descriptor.getCMPPolicy()).getPKClass().isAssignableFrom(primaryKey.getClass())){
272             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("invalid_pk_class", new Object JavaDoc[] {((CMP3Policy)descriptor.getCMPPolicy()).getPKClass(), primaryKey.getClass()}));
273         }
274         return findInternal(descriptor, session, primaryKey);
275     }
276     
277     /**
278     * Find by primary key.
279     * @param descriptor
280     * @param session
281     * @param primaryKey
282     * @return the found entity instance or null
283     * if the entity does not exist
284     * @throws IllegalArgumentException if the first argument does
285     * not denote an entity type or the second argument is not a valid type for that
286     * entity's primary key
287     */

288     protected static Object JavaDoc findInternal(ClassDescriptor descriptor, Session session, Object JavaDoc primaryKey){
289         Vector JavaDoc pk;
290         if(primaryKey instanceof Vector JavaDoc) {
291             pk = (Vector JavaDoc)primaryKey;
292         } else {
293             pk = ((CMP3Policy) descriptor.getCMPPolicy()).createPkVectorFromKey(primaryKey, (oracle.toplink.essentials.internal.sessions.AbstractSession)session);
294         }
295         ReadObjectQuery query = new ReadObjectQuery(descriptor.getJavaClass());
296         query.setSelectionKey(pk);
297         query.conformResultsInUnitOfWork();
298         return session.executeQuery(query);
299     }
300     
301     /**
302     * Synchronize the persistence context with the
303     * underlying database.
304     */

305     public void flush(){
306             verifyOpen();
307         
308             try {
309                 getActivePersistenceContext(checkForTransaction(true)).writeChanges();
310             }catch (RuntimeException JavaDoc e) {
311                 this.transaction.setRollbackOnlyInternal();
312                 if (TopLinkException.class.isAssignableFrom(e.getClass())){
313                     throw new PersistenceException(e);
314                 }
315                 throw e;
316             }
317     }
318
319     protected void detectTransactionWrapper(){
320         if (this.serverSession.hasExternalTransactionController()){
321             setJTATransactionWrapper();
322         } else {
323             setEntityTransactionWrapper();
324         }
325      }
326
327     /**
328     * Refresh the state of the instance from the
329     * database.
330     * @param entity
331     */

332     public void refresh(Object JavaDoc entity){
333         verifyOpen();
334         UnitOfWork uow = getActivePersistenceContext(checkForTransaction(!isExtended()));
335         if(!contains(entity, uow)) {
336             this.transaction.setRollbackOnlyInternal();
337             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("cant_refresh_not_managed_object", new Object JavaDoc[]{entity}));
338         }
339         ReadObjectQuery query = new ReadObjectQuery();
340         query.setSelectionObject(entity);
341         query.refreshIdentityMapResult();
342         query.cascadeByMapping();
343         query.setLockMode(ObjectBuildingQuery.NO_LOCK);
344         Object JavaDoc refreshedEntity = null;
345         try{
346              refreshedEntity = uow.executeQuery(query);
347         }catch (RuntimeException JavaDoc e){
348             this.transaction.setRollbackOnlyInternal();
349             throw e;
350         }
351         if(refreshedEntity == null) {
352             throw new EntityNotFoundException(ExceptionLocalization.buildMessage("entity_no_longer_exists_in_db", new Object JavaDoc[]{entity}));
353         }
354     }
355     
356     /**
357     * Check if the instance belongs to the current persistence
358     * context.
359     * @param entity
360     * @return
361     * @throws IllegalArgumentException if given Object is not an entity
362     */

363     public boolean contains(Object JavaDoc entity){
364         verifyOpen();
365         if (entity == null){
366             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[] {entity}));
367         }
368         ClassDescriptor descriptor = (ClassDescriptor)getServerSession().getDescriptors().get(entity.getClass());
369         if (descriptor == null || descriptor.isAggregateDescriptor() || descriptor.isAggregateCollectionDescriptor()){
370             throw new IllegalArgumentException JavaDoc(ExceptionLocalization.buildMessage("not_an_entity", new Object JavaDoc[]{entity}));
371         }
372         
373         if( (!hasActivePersistenceContext())) {
374             return false;
375         }
376         
377         return contains(entity,getActivePersistenceContext(checkForTransaction(false)));
378     }
379
380     /**
381     * Check if the instance belongs to the current persistence
382     * context.
383     * @param entity
384     * @param uow
385     * @return
386     */

387     protected boolean contains(Object JavaDoc entity, UnitOfWork uow){
388         
389         return ((oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl)uow).isObjectRegistered(entity) &&
390                 !((oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl)uow).isObjectDeleted(entity);
391     }
392
393     /**
394      * This method returns the current session to the requestor. The current session
395      * will be a the active UnitOfWork within a transaction and will be a 'scrap'
396      * UnitOfWork outside of a transaction. The caller is conserned about the results
397      * then the getSession() or getUnitOfWork() API should be called.
398      */

399     public Session getActiveSession(){
400         Object JavaDoc txn = checkForTransaction(false);
401         if ( txn == null && ! this.isExtended() ){
402             return this.serverSession.acquireNonSynchronizedUnitOfWork();
403         }else{
404             return getActivePersistenceContext(txn);
405         }
406         
407     }
408     
409     /**
410      * Return the underlying provider object for the EntityManager,
411      * if available. The result of this method is implementation
412      * specific.
413      */

414     public Object JavaDoc getDelegate(){
415         return this;
416     }
417
418     /**
419      * This method will return the active UnitOfWork
420      */

421     public UnitOfWork getUnitOfWork(){
422         return getActivePersistenceContext(checkForTransaction(false));
423     }
424     
425     /**
426      * This method will return a Session outside of a transaction and null within a transaction.
427      */

428     public Session getSession(){
429         if (checkForTransaction(false) == null){
430             return this.serverSession.acquireNonSynchronizedUnitOfWork();
431         }
432         return null;
433     }
434     
435     /**
436      * Return the underlying server session
437      */

438     public ServerSession getServerSession(){
439         return this.serverSession;
440     }
441     /**
442      * This method is used to create a query using SQL. The class, must be the expected
443      * return type.
444      */

445     protected DatabaseQuery createNativeQueryInternal(String JavaDoc sqlString, Class JavaDoc resultType){
446         verifyOpen();
447         ReadAllQuery query = new ReadAllQuery(resultType);
448         query.setSQLString(sqlString);
449         query.setIsUserDefined(true);
450         return query;
451     }
452      
453     /**
454      * This method is used to create a query using a Toplink Expression and the return type.
455      */

456     protected DatabaseQuery createQueryInternal(Expression expression, Class JavaDoc resultType){
457         ReadAllQuery query = new ReadAllQuery(resultType);
458         query.setSelectionCriteria(expression);
459         return query;
460     }
461     
462
463     /**
464      * <p>Closes this EntityManager.
465      *
466      * <p>After invoking this method, all methods on the instance will throw an
467      * {@link IllegalStateException} except for {@link #isOpen}, which will return
468      * <code>false</code> .</p>
469      *
470      * <p>This should be called when a method is finished with the EntityManager in a
471      * bean-managed transaction environment or when executed outside a container. Closing
472      * of the EntityManager is handled by the container when using container-managed
473      * transactions.</p>
474      */

475     public void close(){
476         verifyOpen();
477         isOpen = false;
478         try {
479             if (!transaction.shouldClose()){
480                 this.transaction.markLastTransaction();
481             }
482         } finally {
483             factory = null;
484             serverSession = null;
485         }
486     }
487
488
489     /**
490      * Indicates if this EntityManager is an extended Persistence Context
491      */

492     public boolean isExtended(){
493         return this.extended;
494     }
495     
496     /**
497      * Indicates whether or not this entity manager is open. Returns <code>true</code> until
498      * a call to {@link #close} is made.
499      */

500     public boolean isOpen(){
501         return isOpen && factory.isOpen();
502     }
503  
504     /**
505     * Set the lock mode for an entity object contained in the persistence context.
506     * @param entity
507     * @param lockMode
508     * @throws PersistenceException if an unsupported lock call is made
509     * @throws IllegalArgumentException if the instance is not an entity or is a detached entity
510     * @throws TransactionRequiredException if there is no transaction
511     */

512     public void lock(Object JavaDoc entity, LockModeType lockMode){
513         RepeatableWriteUnitOfWork context = getActivePersistenceContext(checkForTransaction(!isExtended()));
514         ClassDescriptor descriptor = context.getDescriptor(entity);
515         OptimisticLockingPolicy lockingPolicy = descriptor.getOptimisticLockingPolicy();
516         if ((lockingPolicy == null) || !(lockingPolicy instanceof VersionLockingPolicy)){
517             this.transaction.setRollbackOnlyInternal();
518             throw new PersistenceException(ExceptionLocalization.buildMessage("ejb30-wrong-lock_called_without_version_locking-index", null));
519         }
520         context.forceUpdateToVersionField(entity, (lockMode == LockModeType.WRITE));
521     }
522
523     public void verifyOpen(){
524         if (!isOpen()){
525             throw new IllegalStateException JavaDoc(ExceptionLocalization.buildMessage("operation_on_closed_entity_manager"));
526         }
527     }
528     
529     public RepeatableWriteUnitOfWork getActivePersistenceContext(Object JavaDoc txn) {
530         if (this.isExtended()){
531             // use local uow as it will be local to this EM and not on the txn
532
if (this.extendedPersistenceContext == null || !this.extendedPersistenceContext.isActive()){
533                 this.extendedPersistenceContext = new RepeatableWriteUnitOfWork(this.serverSession.acquireClientSession());
534                 this.extendedPersistenceContext.setResumeUnitOfWorkOnTransactionCompletion(true);
535                 this.extendedPersistenceContext.setShouldCascadeCloneToJoinedRelationship(true);
536                 if (txn != null) {
537                     // if there is an active txn we must register with it on creation of PC
538
transaction.registerUnitOfWorkWithTxn(this.extendedPersistenceContext);
539                 }
540             }
541             return this.extendedPersistenceContext;
542         }else{
543             return getTransactionalUnitOfWork_new(txn);
544         }
545     }
546     
547     /*
548      * This method is used in contains to check if we already have a persistence context.
549      * If there is no active persistence context the method returns false
550      */

551     private boolean hasActivePersistenceContext()
552     {
553         if (isExtended() && (this.extendedPersistenceContext == null || !this.extendedPersistenceContext.isActive()))
554             return false;
555         else
556             return true;
557     }
558     
559     protected RepeatableWriteUnitOfWork getTransactionalUnitOfWork_new(Object JavaDoc tnx) {
560         return transaction.getTransactionalUnitOfWork(tnx);
561     }
562     
563     protected Object JavaDoc checkForTransaction(boolean validateExistence) {
564         return transaction.checkForTransaction(validateExistence);
565     }
566     
567     public boolean shouldFlushBeforeQuery(){
568         Object JavaDoc foundTransaction = checkForTransaction(false);
569         if ((foundTransaction!=null) && transaction.shouldFlushBeforeQuery(getActivePersistenceContext(foundTransaction))){
570             return true;
571         }
572         return false;
573     }
574     
575     //This is used to determine if the Persistence Contexts (in the form of UOWs)
576
//should be propagated or not, which effectively means we will use the
577
//active unit of work
578
public boolean shouldPropagatePersistenceContext(){
579         return this.propagatePersistenceContext;
580     }
581
582     /**
583     * Indicate to the EntityManager that a JTA transaction is
584     * active. This method should be called on a JTA application
585     * managed EntityManager that was created outside the scope
586     * of the active transaction to associate it with the current
587     * JTA transaction.
588     * @throws TransactionRequiredException if there is
589     * no transaction.
590     */

591     public void joinTransaction(){
592         transaction.registerUnitOfWorkWithTxn(getActivePersistenceContext(checkForTransaction(true)));
593     }
594 }
595
Popular Tags