KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > identitymaps > IdentityMapManager


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
package oracle.toplink.essentials.internal.identitymaps;
23
24 import java.security.AccessController JavaDoc;
25 import java.security.PrivilegedActionException JavaDoc;
26 import java.util.*;
27 import java.io.*;
28 import java.lang.reflect.*;
29
30 import oracle.toplink.essentials.internal.helper.*;
31 import oracle.toplink.essentials.internal.descriptors.*;
32 import oracle.toplink.essentials.exceptions.*;
33 import oracle.toplink.essentials.expressions.*;
34 import oracle.toplink.essentials.queryframework.*;
35 import oracle.toplink.essentials.internal.localization.*;
36 import oracle.toplink.essentials.logging.SessionLog;
37 import oracle.toplink.essentials.sessions.SessionProfiler;
38 import oracle.toplink.essentials.sessions.Record;
39 import oracle.toplink.essentials.internal.security.PrivilegedAccessHelper;
40 import oracle.toplink.essentials.internal.security.PrivilegedGetConstructorFor;
41 import oracle.toplink.essentials.internal.security.PrivilegedMethodInvoker;
42 import oracle.toplink.essentials.internal.security.PrivilegedInvokeConstructor;
43 import oracle.toplink.essentials.internal.sessions.AbstractRecord;
44 import oracle.toplink.essentials.internal.sessions.UnitOfWorkImpl;
45 import oracle.toplink.essentials.internal.sessions.AbstractSession;
46 import oracle.toplink.essentials.descriptors.ClassDescriptor;
47
48 /**
49  * <p><b>Purpose</b>: Maintain identity maps for domain classes mapped with TopLink.
50  * <p><b>Responsibilities</b>:<ul>
51  * <li> Build new identity maps lazily using info from the descriptor
52  * <li> Insert objects into appropriate identity map
53  * <li> Get object from appropriate identity map using object or primary key with class
54  * <li> Get and Set write lock values for cached objects
55  * </ul>
56  * @since TOPLink/Java 1.0
57  */

58 public class IdentityMapManager implements Serializable, Cloneable JavaDoc {
59
60     /** A table of identity maps with the key being the domain Class. */
61     protected Hashtable identityMaps;
62
63     /** A table of identity maps with the key being the query */
64     protected Map queryResults;
65
66     /** A reference to the session owning this manager. */
67     protected AbstractSession session;
68
69     /** Ensure mutual exclusion depending on the cache isolation.*/
70     protected transient ConcurrencyManager cacheMutex;
71
72     /** Optimize the object retrival from the identity map. */
73     protected IdentityMap lastAccessedIdentityMap = null;
74     protected Class JavaDoc lastAccessedIdentityMapClass = null;
75
76     /** Used to store the write lock manager used for merging. */
77     protected transient WriteLockManager writeLockManager;
78
79     /** PERF: Used to avoid readLock and profiler checks to improve performance. */
80     protected Boolean JavaDoc isCacheAccessPreCheckRequired;
81
82     public IdentityMapManager(AbstractSession session) {
83         this.session = session;
84         this.cacheMutex = new ConcurrencyManager();
85         this.identityMaps = new Hashtable();
86         this.queryResults = JavaPlatform.getQueryCacheMap();
87     }
88
89     /**
90      * Provides access for setting a deferred lock on an object in the IdentityMap.
91      */

92     public CacheKey acquireDeferredLock(Vector primaryKey, Class JavaDoc domainClass, ClassDescriptor descriptor) {
93         CacheKey cacheKey = null;
94         if (isCacheAccessPreCheckRequired()) {
95             getSession().startOperationProfile(SessionProfiler.CACHE);
96             acquireReadLock();
97             try {
98                 cacheKey = getIdentityMap(descriptor).acquireDeferredLock(primaryKey);
99             } finally {
100                 releaseReadLock();
101             }
102             getSession().endOperationProfile(SessionProfiler.CACHE);
103         } else {
104             cacheKey = getIdentityMap(descriptor).acquireDeferredLock(primaryKey);
105         }
106
107         return cacheKey;
108     }
109
110     /**
111      * Provides access for setting a concurrency lock on an object in the IdentityMap.
112      * called with true from the merge process, if true then the refresh will not refresh the object
113      * @see IdentityMap#aquire
114      */

115     public CacheKey acquireLock(Vector primaryKey, Class JavaDoc domainClass, boolean forMerge, ClassDescriptor descriptor) {
116         CacheKey cacheKey = null;
117         if (isCacheAccessPreCheckRequired()) {
118             getSession().startOperationProfile(SessionProfiler.CACHE);
119             acquireReadLock();
120             try {
121                 cacheKey = getIdentityMap(descriptor).acquireLock(primaryKey, forMerge);
122             } finally {
123                 releaseReadLock();
124             }
125             getSession().endOperationProfile(SessionProfiler.CACHE);
126         } else {
127             cacheKey = getIdentityMap(descriptor).acquireLock(primaryKey, forMerge);
128         }
129
130         return cacheKey;
131     }
132
133     /**
134      * Provides access for setting a concurrency lock on an object in the IdentityMap.
135      * called with true from the merge process, if true then the refresh will not refresh the object
136      * @see IdentityMap#aquire
137      */

138     public CacheKey acquireLockNoWait(Vector primaryKey, Class JavaDoc domainClass, boolean forMerge, ClassDescriptor descriptor) {
139         CacheKey cacheKey = null;
140         if (isCacheAccessPreCheckRequired()) {
141             getSession().startOperationProfile(SessionProfiler.CACHE);
142             acquireReadLock();
143             try {
144                 cacheKey = getIdentityMap(descriptor).acquireLockNoWait(primaryKey, forMerge);
145             } finally {
146                 releaseReadLock();
147             }
148             getSession().endOperationProfile(SessionProfiler.CACHE);
149         } else {
150             cacheKey = getIdentityMap(descriptor).acquireLockNoWait(primaryKey, forMerge);
151         }
152
153         return cacheKey;
154     }
155
156     /**
157      * PERF: Used to micro optimize cache access.
158      * Avoid the readLock and profile checks if not required.
159      */

160     protected boolean isCacheAccessPreCheckRequired() {
161         if (this.isCacheAccessPreCheckRequired == null) {
162             if ((getSession().getProfiler() != null) || getSession().getDatasourceLogin().shouldSynchronizedReadOnWrite()) {
163                 this.isCacheAccessPreCheckRequired = Boolean.TRUE;
164             } else {
165                 this.isCacheAccessPreCheckRequired = Boolean.FALSE;
166             }
167         }
168         return this.isCacheAccessPreCheckRequired.booleanValue();
169     }
170
171     /**
172      * Clear the cache access pre-check flag, used from session when profiler .
173      */

174     public void clearCacheAccessPreCheck() {
175         this.isCacheAccessPreCheckRequired = null;
176     }
177
178     /**
179      * Provides access for setting a concurrency lock on an IdentityMap.
180      * @see IdentityMap#aquire
181      */

182     public void acquireReadLock() {
183         getSession().startOperationProfile(SessionProfiler.CACHE);
184
185         if (getSession().getDatasourceLogin().shouldSynchronizedReadOnWrite()) {
186             getCacheMutex().acquireReadLock();
187         }
188
189         getSession().endOperationProfile(SessionProfiler.CACHE);
190     }
191
192     /**
193      * INTERNAL:
194      * Find the cachekey for the provided primary key and place a readlock on it.
195      * This will allow multiple users to read the same object but prevent writes to
196      * the object while the read lock is held.
197      */

198     public CacheKey acquireReadLockOnCacheKey(Vector primaryKey, Class JavaDoc domainClass, ClassDescriptor descriptor) {
199         CacheKey cacheKey = null;
200         if (isCacheAccessPreCheckRequired()) {
201             getSession().startOperationProfile(SessionProfiler.CACHE);
202             acquireReadLock();
203             try {
204                 cacheKey = getIdentityMap(descriptor).acquireReadLockOnCacheKey(primaryKey);
205             } finally {
206                 releaseReadLock();
207             }
208             getSession().endOperationProfile(SessionProfiler.CACHE);
209         } else {
210             cacheKey = getIdentityMap(descriptor).acquireReadLockOnCacheKey(primaryKey);
211         }
212
213         return cacheKey;
214     }
215
216     /**
217      * INTERNAL:
218      * Find the cachekey for the provided primary key and place a readlock on it.
219      * This will allow multiple users to read the same object but prevent writes to
220      * the object while the read lock is held.
221      * If no readlock can be acquired then do not wait but return null.
222      */

223     public CacheKey acquireReadLockOnCacheKeyNoWait(Vector primaryKey, Class JavaDoc domainClass, ClassDescriptor descriptor) {
224         CacheKey cacheKey = null;
225         if (isCacheAccessPreCheckRequired()) {
226             getSession().startOperationProfile(SessionProfiler.CACHE);
227             acquireReadLock();
228             try {
229                 cacheKey = getIdentityMap(descriptor).acquireReadLockOnCacheKeyNoWait(primaryKey);
230             } finally {
231                 releaseReadLock();
232             }
233             getSession().endOperationProfile(SessionProfiler.CACHE);
234         } else {
235             cacheKey = getIdentityMap(descriptor).acquireReadLockOnCacheKeyNoWait(primaryKey);
236         }
237
238         return cacheKey;
239     }
240
241     /**
242      * Lock the entire cache if the cache isolation requires.
243      * By default concurrent reads and writes are allowed.
244      * By write, unit of work merge is meant.
245      */

246     public boolean acquireWriteLock() {
247         if (getSession().getDatasourceLogin().shouldSynchronizedReadOnWrite() || getSession().getDatasourceLogin().shouldSynchronizeWrites()) {
248             getCacheMutex().acquire();
249             return true;
250         }
251         return false;
252     }
253
254     /**
255      * INTERNAL: (Public to allow testing to access)
256      * Return a new empty identity map to cache instances of the class.
257      */

258     public IdentityMap buildNewIdentityMap(ClassDescriptor descriptor) throws ValidationException, DescriptorException {
259         if (getSession().isUnitOfWork()) {
260             return new FullIdentityMap(100);
261         }
262
263         try {
264             Constructor constructor = null;
265             if (PrivilegedAccessHelper.shouldUsePrivilegedAccess()){
266                 try {
267                     constructor = (Constructor)AccessController.doPrivileged(new PrivilegedGetConstructorFor(descriptor.getIdentityMapClass(), new Class JavaDoc[] { ClassConstants.PINT }, false));
268                     return (IdentityMap)AccessController.doPrivileged(new PrivilegedInvokeConstructor(constructor, new Object JavaDoc[] { new Integer JavaDoc(descriptor.getIdentityMapSize())}));
269                 } catch (PrivilegedActionException JavaDoc exception) {
270                     throw DescriptorException.invalidIdentityMap(descriptor, exception.getException());
271                 }
272             } else {
273                 constructor = PrivilegedAccessHelper.getConstructorFor(descriptor.getIdentityMapClass(), new Class JavaDoc[] { ClassConstants.PINT }, false);
274                 return (IdentityMap)PrivilegedAccessHelper.invokeConstructor(constructor, new Object JavaDoc[] { new Integer JavaDoc(descriptor.getIdentityMapSize())});
275
276             }
277         } catch (Exception JavaDoc exception) {
278             throw DescriptorException.invalidIdentityMap(descriptor, exception);
279         }
280     }
281
282     /**
283      * INTERNAL:
284      * Clear the the lastAccessedIdentityMap and the lastAccessedIdentityMapClass
285      */

286     public void clearLastAccessedIdentityMap() {
287         lastAccessedIdentityMap = null;
288         lastAccessedIdentityMapClass = null;
289     }
290
291     /**
292      * INTERNAL:
293      * Clones itself, used for uow commit and resume on failure.
294      */

295     public Object JavaDoc clone() {
296         IdentityMapManager manager = null;
297
298         try {
299             manager = (IdentityMapManager)super.clone();
300             manager.setIdentityMaps(new Hashtable());
301             for (Enumeration identityMapEnum = getIdentityMaps().keys();
302                      identityMapEnum.hasMoreElements();) {
303                 Class JavaDoc theClass = (Class JavaDoc)identityMapEnum.nextElement();
304                 manager.getIdentityMaps().put(theClass, ((IdentityMap)getIdentityMaps().get(theClass)).clone());
305             }
306         } catch (Exception JavaDoc e) {
307             ;
308         }
309
310         return manager;
311     }
312
313     /**
314      * Clear all the query caches
315      */

316     public void clearQueryCache() {
317         this.queryResults = JavaPlatform.getQueryCacheMap();
318     }
319
320     /**
321      * Remove the cache key related to a query.
322      * Note this method is not synchronized and care should be taken to ensure
323      * there are no other threads accessing the cache key. This is used to clean up
324      * cached clones of queries
325      */

326     public void clearQueryCache(ReadQuery query) {
327         if (query != null) {
328             queryResults.remove(query);
329         }
330     }
331
332     public boolean containsKey(Vector key, Class JavaDoc theClass, ClassDescriptor descriptor) {
333         // Check for null, contains causes null pointer.
334
for (int index = 0; index < key.size(); index++) {
335             if (key.elementAt(index) == null) {
336                 return false;
337             }
338         }
339
340         IdentityMap map = getIdentityMap(descriptor);
341         boolean contains;
342
343         if (isCacheAccessPreCheckRequired()) {
344             getSession().startOperationProfile(SessionProfiler.CACHE);
345             acquireReadLock();
346             try {
347                 contains = map.containsKey(key);
348             } finally {
349                 releaseReadLock();
350                 getSession().endOperationProfile(SessionProfiler.CACHE);
351             }
352         } else {
353             contains = map.containsKey(key);
354         }
355
356         return contains;
357     }
358
359     /**
360      * Query the cache in-memory.
361      */

362     public Vector getAllFromIdentityMap(Expression selectionCriteria, Class JavaDoc theClass, Record translationRow, InMemoryQueryIndirectionPolicy valueHolderPolicy, boolean shouldReturnInvalidatedObjects) {
363         ClassDescriptor descriptor = getSession().getDescriptor(theClass);
364         getSession().startOperationProfile(SessionProfiler.CACHE);
365         Vector objects = null;
366         try {
367             Expression selectionCriteriaClone = selectionCriteria;
368
369             // Only clone if required.
370
if ((selectionCriteria != null) && (selectionCriteriaClone.getBuilder().getSession() == null)) {
371                 selectionCriteriaClone = (Expression)selectionCriteria.clone();
372                 selectionCriteriaClone.getBuilder().setSession(getSession());
373                 selectionCriteriaClone.getBuilder().setQueryClass(theClass);
374             }
375             objects = new Vector();
376             IdentityMap map = getIdentityMap(descriptor);
377
378             // cache the current time to avoid calculating it every time through the loop
379
long currentTimeInMillis = System.currentTimeMillis();
380             for (Enumeration cacheEnum = map.keys(); cacheEnum.hasMoreElements();) {
381                 CacheKey key = (CacheKey)cacheEnum.nextElement();
382                 if ((key.getObject() == null) || (!shouldReturnInvalidatedObjects && getSession().getDescriptor(theClass).getCacheInvalidationPolicy().isInvalidated(key, currentTimeInMillis))) {
383                     continue;
384                 }
385                 Object JavaDoc object = key.getObject();
386
387                 // Bug # 3216337 - key.getObject() should check for null; object may be GC'd (MWN)
388
if (object == null) {
389                     continue;
390                 }
391
392                 // Must check for inheritance.
393
if ((object.getClass() == theClass) || (theClass.isInstance(object))) {
394                     if (selectionCriteriaClone == null) {
395                         objects.addElement(object);
396                         getSession().incrementProfile(SessionProfiler.CacheHits);
397                     } else {
398                         try {
399                             if (selectionCriteriaClone.doesConform(object, getSession(), (AbstractRecord)translationRow, valueHolderPolicy)) {
400                                 objects.addElement(object);
401                                 getSession().incrementProfile(SessionProfiler.CacheHits);
402                             }
403                         } catch (QueryException queryException) {
404                             if (queryException.getErrorCode() == QueryException.MUST_INSTANTIATE_VALUEHOLDERS) {
405                                 if (valueHolderPolicy.shouldIgnoreIndirectionExceptionReturnConformed()) {
406                                     objects.addElement(object);
407                                     getSession().incrementProfile(SessionProfiler.CacheHits);
408                                 } else if (valueHolderPolicy.shouldThrowIndirectionException()) {
409                                     throw queryException;
410                                 }
411                             } else {
412                                 throw queryException;
413                             }
414                         }
415                     }
416                 }
417             }
418         } finally {
419             getSession().endOperationProfile(SessionProfiler.CACHE);
420         }
421         return objects;
422     }
423
424     /**
425      * INTERNAL:
426      * Retrieve the cache key for the given identity information
427      * @param Vector the primary key of the cache key to be retrieved
428      * @param Class the class of the cache key to be retrieved
429      * @return CacheKey
430      */

431     public CacheKey getCacheKeyForObject(Vector primaryKey, Class JavaDoc myClass, ClassDescriptor descriptor) {
432         IdentityMap map = getIdentityMap(descriptor);
433         CacheKey cacheKey = null;
434         if (isCacheAccessPreCheckRequired()) {
435             getSession().startOperationProfile(SessionProfiler.CACHE);
436             acquireReadLock();
437             try {
438                 cacheKey = map.getCacheKey(primaryKey);
439             } finally {
440                 releaseReadLock();
441                 getSession().endOperationProfile(SessionProfiler.CACHE);
442             }
443         } else {
444             cacheKey = map.getCacheKey(primaryKey);
445         }
446         return cacheKey;
447     }
448
449     /**
450      * Return the cache mutex.
451      * This allows for the entire cache to be locked.
452      * This is done for transaction isolations on merges, although never locked by default.
453      */

454     public ConcurrencyManager getCacheMutex() {
455         return cacheMutex;
456     }
457
458     /**
459      * INTERNAL:
460      * This method is used to get a list of those classes with IdentityMaps in the Session.
461      */

462     public Vector getClassesRegistered() {
463         Enumeration classes = getIdentityMaps().keys();
464         Vector results = new Vector(getIdentityMaps().size());
465         while (classes.hasMoreElements()) {
466             results.add(((Class JavaDoc)classes.nextElement()).getName());
467         }
468         return results;
469     }
470
471     /**
472      * Get the object from the identity map which has the same identity information
473      * as the given object.
474      */

475     public Object JavaDoc getFromIdentityMap(Object JavaDoc object) {
476         ClassDescriptor descriptor = getSession().getDescriptor(object);
477         Vector primaryKey = descriptor.getObjectBuilder().extractPrimaryKeyFromObject(object, getSession());
478         return getFromIdentityMap(primaryKey, object.getClass(), descriptor);
479     }
480
481     /**
482      * Get the object from the identity map which has the given primary key and class
483      */

484     public Object JavaDoc getFromIdentityMap(Vector key, Class JavaDoc theClass, ClassDescriptor descriptor) {
485         return getFromIdentityMap(key, theClass, true, descriptor);
486     }
487
488     /**
489      * Get the object from the identity map which has the given primary key and class
490      * Only return the object if it has not Invalidated
491      */

492     public Object JavaDoc getFromIdentityMap(Vector key, Class JavaDoc theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) {
493         if (key == null) {
494             return null;
495         }
496
497         // Check for null, contains causes null pointer.
498
for (int index = 0; index < key.size(); index++) {
499             if (key.elementAt(index) == null) {
500                 return null;
501             }
502         }
503
504         CacheKey cacheKey;
505         IdentityMap map = getIdentityMap(descriptor);
506         Object JavaDoc domainObject = null;
507         if (isCacheAccessPreCheckRequired()) {
508             getSession().startOperationProfile(SessionProfiler.CACHE);
509             acquireReadLock();
510             try {
511                 cacheKey = map.getCacheKey(key);
512             } finally {
513                 releaseReadLock();
514             }
515         } else {
516             cacheKey = map.getCacheKey(key);
517         }
518
519         if ((cacheKey != null) && (shouldReturnInvalidatedObjects || !getSession().getDescriptor(theClass).getCacheInvalidationPolicy().isInvalidated(cacheKey, System.currentTimeMillis()))) {
520             //bug 4772232- acquire readlock on cachekey then release to ensure object is fully built before being returned
521
try {
522                 cacheKey.acquireReadLock();
523             } finally {
524                 cacheKey.releaseReadLock();
525             }
526             //bug 4772232- acquire readlock on cachekey then release to ensure object is fully built before being returned
527
try {
528                 cacheKey.acquireReadLock();
529                 domainObject = cacheKey.getObject();
530             } finally {
531                 cacheKey.releaseReadLock();
532             }
533             //reslove the inheritance issues
534
domainObject = checkForInheritance(domainObject, theClass);
535         }
536
537         if (isCacheAccessPreCheckRequired()) {
538             getSession().endOperationProfile(SessionProfiler.CACHE);
539             if (domainObject == null) {
540                 getSession().incrementProfile(SessionProfiler.CacheMisses);
541             } else {
542                 getSession().incrementProfile(SessionProfiler.CacheHits);
543             }
544         }
545
546         return domainObject;
547     }
548
549     public Object JavaDoc getFromIdentityMap(Expression selectionCriteria, Class JavaDoc theClass, Record translationRow, InMemoryQueryIndirectionPolicy valueHolderPolicy, boolean conforming, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) {
550         UnitOfWorkImpl unitOfWork = (conforming) ? (UnitOfWorkImpl)getSession() : null;
551         getSession().startOperationProfile(SessionProfiler.CACHE);
552         try {
553             Expression selectionCriteriaClone = selectionCriteria;
554
555             // Only clone if required.
556
if ((selectionCriteria != null) && (selectionCriteriaClone.getBuilder().getSession() == null)) {
557                 selectionCriteriaClone = (Expression)selectionCriteria.clone();
558                 selectionCriteriaClone.getBuilder().setSession(getSession());
559                 selectionCriteriaClone.getBuilder().setQueryClass(theClass);
560             }
561             IdentityMap map = getIdentityMap(descriptor);
562
563             // cache the current time to avoid calculating it every time through the loop
564
long currentTimeInMillis = System.currentTimeMillis();
565             for (Enumeration cacheEnum = map.keys(); cacheEnum.hasMoreElements();) {
566                 CacheKey key = (CacheKey)cacheEnum.nextElement();
567                 if (!shouldReturnInvalidatedObjects && descriptor.getCacheInvalidationPolicy().isInvalidated(key, currentTimeInMillis)) {
568                     continue;
569                 }
570                 Object JavaDoc object = key.getObject();
571
572                 // Bug # 3216337 - key.getObject() should check for null; object may be GC'd (MWN)
573
if (object == null) {
574                     continue;
575                 }
576
577                 // Must check for inheritance.
578
if ((object.getClass() == theClass) || (theClass.isInstance(object))) {
579                     if (selectionCriteriaClone == null) {
580                         // bug 2782991: if first found was deleted nothing returned.
581
if (!(conforming && unitOfWork.isObjectDeleted(object))) {
582                             getSession().incrementProfile(SessionProfiler.CacheHits);
583                             return object;
584                         }
585                     }
586
587                     //CR 3677 integration of a ValueHolderPolicy
588
try {
589                         if (selectionCriteriaClone.doesConform(object, getSession(), (AbstractRecord)translationRow, valueHolderPolicy)) {
590                             // bug 2782991: if first found was deleted nothing returned.
591
if (!(conforming && unitOfWork.isObjectDeleted(object))) {
592                                 getSession().incrementProfile(SessionProfiler.CacheHits);
593                                 return object;
594                             }
595                         }
596                     } catch (QueryException queryException) {
597                         if (queryException.getErrorCode() == QueryException.MUST_INSTANTIATE_VALUEHOLDERS) {
598                             if (valueHolderPolicy.shouldIgnoreIndirectionExceptionReturnConformed()) {
599                                 // bug 2782991: if first found was deleted nothing returned.
600
if (!(conforming && unitOfWork.isObjectDeleted(object))) {
601                                     getSession().incrementProfile(SessionProfiler.CacheHits);
602                                     return object;
603                                 }
604                             } else if (valueHolderPolicy.shouldIgnoreIndirectionExceptionReturnNotConformed()) {
605                                 // For bug 2667870 just skip this item, but do not abort.
606
} else {
607                                 throw queryException;
608                             }
609                         } else {
610                             throw queryException;
611                         }
612                     }
613                 }
614             }
615         } finally {
616             getSession().endOperationProfile(SessionProfiler.CACHE);
617         }
618         return null;
619     }
620
621     /**
622      * Get the object from the cache with the given primary key and class
623      * do not return the object if it was invalidated
624      */

625     public Object JavaDoc getFromIdentityMapWithDeferredLock(Vector key, Class JavaDoc theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) {
626         if (key == null) {
627             getSession().incrementProfile(SessionProfiler.CacheMisses);
628             return null;
629         }
630
631         // Check for null, contains causes null pointer.
632
for (int index = 0; index < key.size(); index++) {
633             if (key.elementAt(index) == null) {
634                 getSession().incrementProfile(SessionProfiler.CacheMisses);
635                 return null;
636             }
637         }
638
639         IdentityMap map = getIdentityMap(descriptor);
640         CacheKey cacheKey;
641         Object JavaDoc domainObject = null;
642         if (isCacheAccessPreCheckRequired()) {
643             getSession().startOperationProfile(SessionProfiler.CACHE);
644             acquireReadLock();
645             try {
646                 cacheKey = map.getCacheKey(key);
647             } finally {
648                 releaseReadLock();
649             }
650         } else {
651             cacheKey = map.getCacheKey(key);
652         }
653
654         if ((cacheKey != null) && (shouldReturnInvalidatedObjects || !descriptor.getCacheInvalidationPolicy().isInvalidated(cacheKey, System.currentTimeMillis()))) {
655             cacheKey.acquireDeferredLock();
656             domainObject = cacheKey.getObject();
657             cacheKey.releaseDeferredLock();
658         }
659
660         //reslove the inheritance issues
661
domainObject = checkForInheritance(domainObject, theClass);
662
663         if (isCacheAccessPreCheckRequired()) {
664             getSession().endOperationProfile(SessionProfiler.CACHE);
665             if (domainObject == null) {
666                 getSession().incrementProfile(SessionProfiler.CacheMisses);
667             } else {
668                 getSession().incrementProfile(SessionProfiler.CacheHits);
669             }
670         }
671
672         return domainObject;
673     }
674
675     /**
676      * INTERNAL: (public to allow test cases to check)
677      * Return the identity map for the class, if missing create a new one.
678      */

679     public IdentityMap getIdentityMap(ClassDescriptor descriptor) {
680         IdentityMap identityMap;
681
682         // Enusre that an im is only used for the root descriptor for inheritence.
683
// This is required to obtain proper cache hits.
684
if (descriptor.hasInheritance()) {
685             descriptor = descriptor.getInheritancePolicy().getRootParentDescriptor();
686         }
687         Class JavaDoc descriptorClass = descriptor.getJavaClass();
688
689         // PERF: Synchronize around caching of last accessed map and lookup/build,
690
// note that get is synchronized anyway so this is not adding any additional synching.
691
synchronized (this) {
692             // Optimazition for object retrival.
693
IdentityMap tempMap = this.lastAccessedIdentityMap;
694             if ((tempMap != null) && (this.lastAccessedIdentityMapClass == descriptorClass)) {
695                 return tempMap;
696             }
697
698             // PERF: Only synch around creation.
699
identityMap = (IdentityMap)getIdentityMaps().get(descriptorClass);
700             if (identityMap == null) {
701                 identityMap = buildNewIdentityMap(descriptor);
702                 getIdentityMaps().put(descriptorClass, identityMap);
703             }
704             this.lastAccessedIdentityMap = identityMap;
705             this.lastAccessedIdentityMapClass = descriptorClass;
706         }
707         return identityMap;
708     }
709
710     protected Hashtable getIdentityMaps() {
711         return identityMaps;
712     }
713     
714     /**
715      * INTERNAL:
716      *
717      * @return an enumeration of the classes in the identity map.
718      */

719     public Enumeration getIdentityMapClasses() {
720         return identityMaps.keys();
721     }
722
723     protected Vector getKey(Object JavaDoc domainObject) {
724         return getSession().keyFromObject(domainObject);
725     }
726
727     protected AbstractSession getSession() {
728         return session;
729     }
730
731     /**
732      * Get the wrapper object from the cache key associated with the given primary key,
733      * this is used for EJB.
734      */

735     public Object JavaDoc getWrapper(Vector primaryKey, Class JavaDoc theClass) {
736         ClassDescriptor descriptor = getSession().getDescriptor(theClass);
737         IdentityMap map = getIdentityMap(descriptor);
738         Object JavaDoc wrapper;
739         if (isCacheAccessPreCheckRequired()) {
740             getSession().startOperationProfile(SessionProfiler.CACHE);
741             acquireReadLock();
742             try {
743                 wrapper = map.getWrapper(primaryKey);
744             } finally {
745                 releaseReadLock();
746             }
747             getSession().endOperationProfile(SessionProfiler.CACHE);
748         } else {
749             wrapper = map.getWrapper(primaryKey);
750         }
751         return wrapper;
752     }
753
754     /**
755      * INTERNAL:
756      * Returns the single write Lock manager for this session
757      */

758     public WriteLockManager getWriteLockManager() {
759         // With Isolated Sessions not all Identity maps need a WriteLockManager so
760
//lazy initialize
761
synchronized (this) {
762             if (this.writeLockManager == null) {
763                 this.writeLockManager = new WriteLockManager();
764             }
765         }
766         return this.writeLockManager;
767     }
768
769     /**
770      * Retrieve the write lock value of the cache key associated with the given primary key,
771      */

772     public Object JavaDoc getWriteLockValue(Vector primaryKey, Class JavaDoc domainClass, ClassDescriptor descriptor) {
773         IdentityMap map = getIdentityMap(descriptor);
774         Object JavaDoc value;
775         if (isCacheAccessPreCheckRequired()) {
776             getSession().startOperationProfile(SessionProfiler.CACHE);
777             acquireReadLock();
778             try {
779                 value = map.getWriteLockValue(primaryKey);
780             } finally {
781                 releaseReadLock();
782             }
783             getSession().endOperationProfile(SessionProfiler.CACHE);
784         } else {
785             value = map.getWriteLockValue(primaryKey);
786         }
787         return value;
788     }
789
790     /**
791      * Reset the identity map for only the instances of the class.
792      * For inheritence the user must make sure that they only use the root class.
793      */

794     public void initializeIdentityMap(Class JavaDoc theClass) throws TopLinkException {
795         ClassDescriptor descriptor = getSession().getDescriptor(theClass);
796
797         if (descriptor == null) {
798             throw ValidationException.missingDescriptor(String.valueOf(theClass));
799         }
800         if (descriptor.isChildDescriptor()) {
801             throw ValidationException.childDescriptorsDoNotHaveIdentityMap();
802         }
803
804         // Bug 3736313 - look up identity map by descriptor's java class
805
Class JavaDoc javaClass = descriptor.getJavaClass();
806
807         // bug 3745484 - clear the cached identity map if we are clearing the associated identity map
808
if (javaClass == lastAccessedIdentityMapClass) {
809             clearLastAccessedIdentityMap();
810         }
811         IdentityMap identityMap = buildNewIdentityMap(descriptor);
812         getIdentityMaps().put(javaClass, identityMap);
813     }
814
815     public void initializeIdentityMaps() {
816         clearLastAccessedIdentityMap();
817         setIdentityMaps(new Hashtable());
818         clearQueryCache();
819     }
820
821     /**
822      * INTERNAL:
823      * Used to print all the objects in the identity map of the passed in class.
824      * The output of this method will be logged to this session's SessionLog at SEVERE level.
825      */

826     public void printIdentityMap(Class JavaDoc businessClass) {
827         String JavaDoc cr = Helper.cr();
828         ClassDescriptor descriptor = getSession().getDescriptor(businessClass);
829         int cacheCounter = 0;
830         StringWriter writer = new StringWriter();
831         if (descriptor.isAggregateDescriptor()) {
832             return;//do nothing if descriptor is aggregate
833
}
834
835         IdentityMap map = getIdentityMap(descriptor);
836         writer.write(LoggingLocalization.buildMessage("identitymap_for", new Object JavaDoc[] { cr, Helper.getShortClassName(map.getClass()), Helper.getShortClassName(businessClass) }));
837         if (descriptor.hasInheritance()) {
838             if (descriptor.getInheritancePolicy().isRootParentDescriptor()) {
839                 writer.write(LoggingLocalization.buildMessage("includes"));
840                 Vector childDescriptors;
841                 childDescriptors = descriptor.getInheritancePolicy().getChildDescriptors();
842                 if ((childDescriptors != null) && (childDescriptors.size() != 0)) {//Bug#2675242
843
Enumeration enum2 = childDescriptors.elements();
844                     writer.write(Helper.getShortClassName((Class JavaDoc)((ClassDescriptor)enum2.nextElement()).getJavaClass()));
845                     while (enum2.hasMoreElements()) {
846                         writer.write(", " + Helper.getShortClassName((Class JavaDoc)((ClassDescriptor)enum2.nextElement()).getJavaClass()));
847                     }
848                 }
849                 writer.write(")");
850             }
851         }
852
853         for (Enumeration enumtr = map.keys(); enumtr.hasMoreElements();) {
854             oracle.toplink.essentials.internal.identitymaps.CacheKey cacheKey = (oracle.toplink.essentials.internal.identitymaps.CacheKey)enumtr.nextElement();
855             Object JavaDoc object = cacheKey.getObject();
856             if (businessClass.isInstance(object)) {
857                 cacheCounter++;
858                 if (object == null) {
859                     writer.write(LoggingLocalization.buildMessage("key_object_null", new Object JavaDoc[] { cr, cacheKey.getKey(), "\t" }));
860                 } else {
861                     writer.write(LoggingLocalization.buildMessage("key_identity_hash_code_object", new Object JavaDoc[] { cr, cacheKey.getKey(), "\t", String.valueOf(System.identityHashCode(object)), object }));
862                 }
863             }
864         }
865         writer.write(LoggingLocalization.buildMessage("elements", new Object JavaDoc[] { cr, String.valueOf(cacheCounter) }));
866         getSession().log(SessionLog.SEVERE, SessionLog.CACHE, writer.toString(), null, null, false);
867     }
868
869     /**
870      * INTERNAL:
871      * Used to print all the objects in every identity map in this session.
872      * The output of this method will be logged to this session's SessionLog at SEVERE level.
873      */

874     public void printIdentityMaps() {
875         for (Iterator iterator = getSession().getDescriptors().keySet().iterator();
876                  iterator.hasNext();) {
877             Class JavaDoc businessClass = (Class JavaDoc)iterator.next();
878             ClassDescriptor descriptor = getSession().getDescriptor(businessClass);
879             if (descriptor.hasInheritance()) {
880                 if (descriptor.getInheritancePolicy().isRootParentDescriptor()) {
881                     printIdentityMap(businessClass);
882                 }
883             } else {
884                 printIdentityMap(businessClass);
885             }
886         }
887     }
888
889     /**
890      * INTERNAL:
891      * Used to print all the Locks in every identity map in this session.
892      * The output of this method will be logged to this session's SessionLog at FINEST level.
893      */

894     public void printLocks() {
895         StringWriter writer = new StringWriter();
896         HashMap threadCollection = new HashMap();
897         writer.write(TraceLocalization.buildMessage("lock_writer_header", (Object JavaDoc[])null) + Helper.cr());
898         Iterator idenityMapsIterator = this.session.getIdentityMapAccessorInstance().getIdentityMapManager().getIdentityMaps().values().iterator();
899         while (idenityMapsIterator.hasNext()) {
900             IdentityMap idenityMap = (IdentityMap)idenityMapsIterator.next();
901             idenityMap.collectLocks(threadCollection);
902         }
903         Object JavaDoc[] parameters = new Object JavaDoc[1];
904         for (Iterator threads = threadCollection.keySet().iterator(); threads.hasNext();) {
905             Thread JavaDoc activeThread = (Thread JavaDoc)threads.next();
906             parameters[0] = activeThread.getName();
907             writer.write(TraceLocalization.buildMessage("active_thread", parameters) + Helper.cr());
908             for (Iterator cacheKeys = ((HashSet)threadCollection.get(activeThread)).iterator();
909                      cacheKeys.hasNext();) {
910                 CacheKey cacheKey = (CacheKey)cacheKeys.next();
911                 parameters[0] = cacheKey.getObject();
912                 writer.write(TraceLocalization.buildMessage("locked_object", parameters) + Helper.cr());
913                 parameters[0] = new Integer JavaDoc(cacheKey.getMutex().getDepth());
914                 writer.write(TraceLocalization.buildMessage("depth", parameters) + Helper.cr());
915             }
916             DeferredLockManager deferredLockManager = ConcurrencyManager.getDeferredLockManager(activeThread);
917             if (deferredLockManager != null) {
918                 for (Iterator deferredLocks = deferredLockManager.getDeferredLocks().iterator();
919                          deferredLocks.hasNext();) {
920                     ConcurrencyManager lock = (ConcurrencyManager)deferredLocks.next();
921                     parameters[0] = lock.getOwnerCacheKey().getObject();
922                     writer.write(TraceLocalization.buildMessage("deferred_locks", parameters) + Helper.cr());
923                 }
924             }
925         }
926         writer.write(Helper.cr() + TraceLocalization.buildMessage("lock_writer_footer", (Object JavaDoc[])null) + Helper.cr());
927         getSession().log(SessionLog.FINEST, SessionLog.CACHE, writer.toString(), null, null, false);
928     }
929
930     /**
931      * INTERNAL:
932      * Used to print all the Locks in the specified identity map in this session.
933      * The output of this method will be logged to this session's SessionLog at FINEST level.
934      */

935     public void printLocks(Class JavaDoc theClass) {
936         ClassDescriptor descriptor = getSession().getDescriptor(theClass);
937         StringWriter writer = new StringWriter();
938         HashMap threadCollection = new HashMap();
939         writer.write(TraceLocalization.buildMessage("lock_writer_header", (Object JavaDoc[])null) + Helper.cr());
940         IdentityMap identityMap = getIdentityMap(descriptor);
941         identityMap.collectLocks(threadCollection);
942
943         Object JavaDoc[] parameters = new Object JavaDoc[1];
944         for (Iterator threads = threadCollection.keySet().iterator(); threads.hasNext();) {
945             Thread JavaDoc activeThread = (Thread JavaDoc)threads.next();
946             parameters[0] = activeThread.getName();
947             writer.write(TraceLocalization.buildMessage("active_thread", parameters) + Helper.cr());
948             for (Iterator cacheKeys = ((HashSet)threadCollection.get(activeThread)).iterator();
949                      cacheKeys.hasNext();) {
950                 CacheKey cacheKey = (CacheKey)cacheKeys.next();
951                 parameters[0] = cacheKey.getObject();
952                 writer.write(TraceLocalization.buildMessage("locked_object", parameters) + Helper.cr());
953                 parameters[0] = new Integer JavaDoc(cacheKey.getMutex().getDepth());
954                 writer.write(TraceLocalization.buildMessage("depth", parameters) + Helper.cr());
955             }
956             DeferredLockManager deferredLockManager = ConcurrencyManager.getDeferredLockManager(activeThread);
957             if (deferredLockManager != null) {
958                 for (Iterator deferredLocks = deferredLockManager.getDeferredLocks().iterator();
959                          deferredLocks.hasNext();) {
960                     ConcurrencyManager lock = (ConcurrencyManager)deferredLocks.next();
961                     parameters[0] = lock.getOwnerCacheKey().getObject();
962                     writer.write(TraceLocalization.buildMessage("deferred_locks", parameters) + Helper.cr());
963                 }
964             }
965         }
966         writer.write(Helper.cr() + TraceLocalization.buildMessage("lock_writer_footer", (Object JavaDoc[])null) + Helper.cr());
967         getSession().log(SessionLog.FINEST, SessionLog.CACHE, writer.toString(), null, null, false);
968     }
969
970     /**
971      * Register the object with the identity map.
972      * The object must always be registered with its version number if optimistic locking is used.
973      * The readTime may also be included in the cache key as it is constructed
974      */

975     public CacheKey putInIdentityMap(Object JavaDoc domainObject, Vector keys, Object JavaDoc writeLockValue, long readTime, ClassDescriptor descriptor) {
976         ObjectBuilder builder = descriptor.getObjectBuilder();
977         Object JavaDoc implementation = builder.unwrapObject(domainObject, getSession());
978
979         IdentityMap map = getIdentityMap(descriptor);
980         CacheKey cacheKey;
981
982         if (isCacheAccessPreCheckRequired()) {
983             getSession().startOperationProfile(SessionProfiler.CACHE);
984             // This is atomic so considered a read lock.
985
acquireReadLock();
986             try {
987                 cacheKey = map.put(keys, implementation, writeLockValue, readTime);
988             } finally {
989                 releaseReadLock();
990             }
991             getSession().endOperationProfile(SessionProfiler.CACHE);
992         } else {
993             cacheKey = map.put(keys, implementation, writeLockValue, readTime);
994         }
995         return cacheKey;
996     }
997
998     /**
999      * Read-release the local-map and the entire cache.
1000     */

1001    protected void releaseReadLock() {
1002        if (getSession().getDatasourceLogin().shouldSynchronizedReadOnWrite()) {
1003            getCacheMutex().releaseReadLock();
1004        }
1005    }
1006
1007    /**
1008     * Lock the entire cache if the cache isolation requires.
1009     * By default concurrent reads and writes are allowed.
1010     * By write, unit of work merge is meant.
1011     */

1012    public void releaseWriteLock() {
1013       if (getSession().getDatasourceLogin().shouldSynchronizedReadOnWrite() || getSession().getDatasourceLogin().shouldSynchronizeWrites()) {
1014             getCacheMutex().release();
1015        }
1016    }
1017
1018    /**
1019     * Remove the object from the object cache.
1020     */

1021    public Object JavaDoc removeFromIdentityMap(Vector key, Class JavaDoc domainClass, ClassDescriptor descriptor) {
1022        IdentityMap map = getIdentityMap(descriptor);
1023        Object JavaDoc value;
1024
1025        if (isCacheAccessPreCheckRequired()) {
1026            getSession().startOperationProfile(SessionProfiler.CACHE);
1027            // This is atomic so considered a read lock.
1028
acquireReadLock();
1029            try {
1030                value = map.remove(key);
1031            } finally {
1032                releaseReadLock();
1033            }
1034            getSession().endOperationProfile(SessionProfiler.CACHE);
1035        } else {
1036            value = map.remove(key);
1037        }
1038        return value;
1039    }
1040
1041    /**
1042     * Set the cache mutex.
1043     * This allows for the entire cache to be locked.
1044     * This is done for transaction isolations on merges, although never locked by default.
1045     */

1046    protected void setCacheMutex(ConcurrencyManager cacheMutex) {
1047        this.cacheMutex = cacheMutex;
1048    }
1049
1050    public void setIdentityMaps(Hashtable identityMaps) {
1051        clearLastAccessedIdentityMap();
1052        this.identityMaps = identityMaps;
1053    }
1054
1055    protected void setSession(AbstractSession session) {
1056        this.session = session;
1057    }
1058
1059    /**
1060     * Update the wrapper object the cache key associated with the given primary key,
1061     * this is used for EJB.
1062     */

1063    public void setWrapper(Vector primaryKey, Class JavaDoc theClass, Object JavaDoc wrapper) {
1064        ClassDescriptor descriptor = getSession().getDescriptor(theClass);
1065        IdentityMap map = getIdentityMap(descriptor);
1066
1067        if (isCacheAccessPreCheckRequired()) {
1068            getSession().startOperationProfile(SessionProfiler.CACHE);
1069            // This is atomic so considered a read lock.
1070
acquireReadLock();
1071            try {
1072                map.setWrapper(primaryKey, wrapper);
1073            } finally {
1074                releaseReadLock();
1075            }
1076            getSession().endOperationProfile(SessionProfiler.CACHE);
1077        } else {
1078            map.setWrapper(primaryKey, wrapper);
1079        }
1080    }
1081
1082    /**
1083     * Update the write lock value of the cache key associated with the given primary key,
1084     */

1085    public void setWriteLockValue(Vector primaryKey, Class JavaDoc theClass, Object JavaDoc writeLockValue) {
1086        ClassDescriptor descriptor = getSession().getDescriptor(theClass);
1087        IdentityMap map = getIdentityMap(descriptor);
1088
1089        if (isCacheAccessPreCheckRequired()) {
1090            getSession().startOperationProfile(SessionProfiler.CACHE);
1091            // This is atomic so considered a read lock.
1092
acquireReadLock();
1093            try {
1094                map.setWriteLockValue(primaryKey, writeLockValue);
1095            } finally {
1096                releaseReadLock();
1097            }
1098            getSession().endOperationProfile(SessionProfiler.CACHE);
1099        } else {
1100            map.setWriteLockValue(primaryKey, writeLockValue);
1101        }
1102    }
1103
1104    /**
1105     * This method is used to resolve the inheritance issues arisen when conforming from the identity map
1106     * 1. Avoid reading the unintended subclass during in-memory queyr(e.g. when querying on large project, do not want
1107     * to check small project, both are inheritanced from the project, and stored in the same identity map).
1108     * 2. EJB container-generated classes broke the inheritance hirearchy. Need to use associated descriptor to track
1109     * the relationship. CR4005-2612426, King-Sept-18-2002
1110     */

1111    private Object JavaDoc checkForInheritance(Object JavaDoc domainObject, Class JavaDoc superClass) {
1112        if ((domainObject != null) && ((domainObject.getClass() != superClass) && (!superClass.isInstance(domainObject)))) {
1113            //before returning null, check if we are using EJB inheritance.
1114
ClassDescriptor descriptor = getSession().getDescriptor(superClass);
1115
1116            if (descriptor.hasInheritance() && descriptor.getInheritancePolicy().getUseDescriptorsToValidateInheritedObjects()) {
1117                //EJB inheritance on the descriptors, not the container-generated classes/objects. We need to check the
1118
//identity map for the bean instance through the descriptor.
1119
if (descriptor.getInheritancePolicy().getSubclassDescriptor(domainObject.getClass()) == null) {
1120                    return null;
1121                }
1122
1123                //else
1124
return domainObject;
1125            }
1126
1127            //else
1128
return null;
1129        }
1130
1131        //else
1132
return domainObject;
1133    }
1134}
1135
Popular Tags