001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * // Copyright (c) 1998, 2007, Oracle. All rights reserved.
005: *
006: *
007: * The contents of this file are subject to the terms of either the GNU
008: * General Public License Version 2 only ("GPL") or the Common Development
009: * and Distribution License("CDDL") (collectively, the "License"). You
010: * may not use this file except in compliance with the License. You can obtain
011: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
012: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
013: * language governing permissions and limitations under the License.
014: *
015: * When distributing the software, include this License Header Notice in each
016: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
017: * Sun designates this particular file as subject to the "Classpath" exception
018: * as provided by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the License
020: * Header, with the fields enclosed by brackets [] replaced by your own
021: * identifying information: "Portions Copyrighted [year]
022: * [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * If you wish your version of this file to be governed by only the CDDL or
027: * only the GPL Version 2, indicate your decision by adding "[Contributor]
028: * elects to include this software in this distribution under the [CDDL or GPL
029: * Version 2] license." If you don't indicate a single choice of license, a
030: * recipient has the option to distribute your version of this file under
031: * either the CDDL, the GPL Version 2 or to extend the choice of license to
032: * its licensees as provided above. However, if you add GPL Version 2 code
033: * and therefore, elected the GPL Version 2 license, then the option applies
034: * only if the new code is made subject to such option by the copyright
035: * holder.
036: */
037: package oracle.toplink.essentials.internal.identitymaps;
038:
039: import java.util.*;
040: import java.io.*;
041: import oracle.toplink.essentials.internal.helper.*;
042:
043: /**
044: * <p><b>Purpose</b>: Caches objects, and allows their retrieval by their primary key.
045: * <p><b>Responsibilities</b>:<ul>
046: * <li> Store CacheKeys containing objects and possibly writeLockValues
047: * <li> Insert & retrieve objects from the cache
048: * <li> Allow retrieval and modification of writeLockValue for a cached object.
049: * </ul>
050: * @see CacheKey
051: * @since TOPLink/Java 1.0
052: */
053: public abstract class IdentityMap implements Serializable, Cloneable {
054:
055: /** The innitial or maximum size of the cache depending upon the concrete implementation */
056: protected int maxSize;
057:
058: /** Used to optimize get through avoiding recreation of the cache key each time. */
059: protected CacheKey searchKey;
060:
061: /**
062: * Instantiate an new IdentityMap with it's maximum size.<p>
063: * <b>NOTE</b>: Subclasses may provide different behaviour for maxSize.
064: * @param anInteger is the maximum size to be allocated for the recevier.
065: */
066: public IdentityMap(int size) {
067: maxSize = size;
068: searchKey = new CacheKey(new Vector(1), null, null);
069: }
070:
071: /**
072: * Acquire the deferred lock
073: */
074: public CacheKey acquireDeferredLock(Vector primaryKey) {
075: //check if the key is already in the hashtable
076: CacheKey key = null;
077:
078: //cache key should not be changed in other threads during the lock acquire operation
079: synchronized (this ) {
080: // bug 3094912 get must be synchronized as well
081: key = getCacheKey(primaryKey);
082: if (key == null) {
083: //create a chachKey and lock the object
084: CacheKey cacheKey = createCacheKey(primaryKey, null,
085: null);
086: cacheKey.acquireDeferredLock();
087: put(cacheKey);
088: return cacheKey;
089: }
090: }
091:
092: // code removed as key will never be null here, either one will be found or one created
093: key.acquireDeferredLock();
094:
095: return key;
096: }
097:
098: /**
099: * Set an exclusive lock on an object in the IdentityMap. This is provided so that when the object is being read from the database
100: * the reader can lock the object while it builds and caches the new object. This will prevent other threads
101: * from accessing this objects but will not stop other threads from inserting other objects into this IdentityMap.
102: */
103: public CacheKey acquireLock(Vector primaryKey, boolean forMerge) {
104: //check if the key is already in the hashtable
105: CacheKey key = null;
106:
107: //cache key should not be changed in other threads during the lock acquire operation
108: synchronized (this ) {
109: // bug 3094912 get must be synchronized as well
110: key = getCacheKey(primaryKey);
111: if (key == null) {
112: //create a chachKey and lock the object
113: CacheKey cacheKey = createCacheKey(primaryKey, null,
114: null);
115: cacheKey.acquire(forMerge);
116: put(cacheKey);
117: return cacheKey;
118: }
119: }
120:
121: // code removed as key will never be null here, either one will be found or one created
122: key.acquire();
123:
124: return key;
125: }
126:
127: /**
128: * INTERNAL:
129: * Used to print all the Locks in every identity map in this session.
130: * The output of this method will go to log passed in as a parameter.
131: */
132: public abstract void collectLocks(HashMap threadList);
133:
134: /**
135: * Set an exclusive lock on an object in the IdentityMap. This is provided so that when the object is being read from the database
136: * the reader can lock the object while it builds and caches the new object. This will prevent other threads
137: * from accessing this objects but will not stop other threads from inserting other objects into this IdentityMap.
138: */
139: public CacheKey acquireLockNoWait(Vector primaryKey,
140: boolean forMerge) {
141: //check if the key is already in the hashtable
142: CacheKey key = null;
143:
144: //cache key should not be changed in other threads during the lock acquire operation
145: synchronized (this ) {
146: key = getCacheKey(primaryKey);
147: if (key == null) {
148: //create a chachKey and lock the object
149: CacheKey cacheKey = createCacheKey(primaryKey, null,
150: null);
151: cacheKey.acquire(forMerge);
152: put(cacheKey);
153: return cacheKey;
154: }
155: }
156:
157: //find the key in the hashtable, lock the object
158: if (key != null) {
159: //couldn't acquire the key so do not return it
160: if (!key.acquireNoWait(forMerge)) {
161: key = null;
162: }
163: }
164:
165: return key;
166: }
167:
168: /**
169: * INTERNAL:
170: * Find the cachekey for the provided primary key and place a readlock on it.
171: * This will allow multiple users to read the same object but prevent writes to
172: * the object while the read lock is held.
173: */
174: public CacheKey acquireReadLockOnCacheKey(Vector primaryKey) {
175: //check if the key is already in the hashtable
176: CacheKey key = null;
177:
178: //cache key should not be changed in other threads during the lock acquire operation
179: synchronized (this ) {
180: key = getCacheKey(primaryKey);
181: if (key == null) {
182: //create a chachKey and lock the object
183: CacheKey cacheKey = createCacheKey(primaryKey, null,
184: null);
185:
186: //lets create one but not put it in the cache, as we are only reading
187: // should not be writing to the identitymap
188: cacheKey.acquireReadLock();
189: return cacheKey;
190: }
191: }
192:
193: key.acquireReadLock();
194:
195: return key;
196: }
197:
198: /**
199: * INTERNAL:
200: * Find the cachekey for the provided primary key and place a readlock on it.
201: * This will allow multiple users to read the same object but prevent writes to
202: * the object while the read lock is held.
203: * If no readlock can be acquired then do not wait but return null.
204: */
205: public CacheKey acquireReadLockOnCacheKeyNoWait(Vector primaryKey) {
206: //check if the key is already in the hashtable
207: CacheKey key = null;
208:
209: //cache key should not be changed in other threads during the lock acquire operation
210: synchronized (this ) {
211: key = getCacheKey(primaryKey);
212: if (key == null) {
213: //create a chachKey and lock the object
214: CacheKey cacheKey = createCacheKey(primaryKey, null,
215: null);
216: cacheKey.acquireReadLock();
217:
218: //lets create one but not put it in the cache, as we are only reading
219: // should not be writing to the identitymap
220: return cacheKey;
221: }
222: }
223:
224: //find the key in the hashtable, lock the object
225: if (key != null) {
226: //couldn't acquire the key so do not return it
227: if (!key.acquireReadLockNoWait()) {
228: key = null;
229: }
230: }
231:
232: return key;
233: }
234:
235: /**
236: * INTERNAL:
237: * Clones itself.
238: */
239: public Object clone() {
240: Object object = null;
241:
242: try {
243: object = super .clone();
244: } catch (Exception e) {
245: ;
246: }
247:
248: return object;
249: }
250:
251: /**
252: * Return true if an object is indexed in the recevier at the primary key <aVector>
253: * @param aVector is the primary key for the object to search for.
254: */
255: public boolean containsKey(Vector primaryKey) {
256: CacheKey wrapper = getCacheKeyWithReadLock(primaryKey);
257:
258: if (wrapper == null) {
259: return false;
260: } else {
261: return true;
262: }
263: }
264:
265: public CacheKey createCacheKey(Vector primaryKey, Object object,
266: Object writeLockValue) {
267: return createCacheKey(primaryKey, object, writeLockValue, 0);
268: }
269:
270: public CacheKey createCacheKey(Vector primaryKey, Object object,
271: Object writeLockValue, long readTime) {
272: return new CacheKey(primaryKey, object, writeLockValue,
273: readTime);
274: }
275:
276: /**
277: * Allow for the cache to be iterated on.
278: */
279: public abstract Enumeration elements();
280:
281: /**
282: * Return the object cached in the identity map or null if it could not be found.
283: */
284: public Object get(Vector primaryKey) {
285: CacheKey cacheKey = getCacheKeyWithReadLock(primaryKey);
286:
287: if (cacheKey == null) {
288: return null;
289: }
290: return cacheKey.getObject();
291: }
292:
293: /**
294: * Get the cache key (with object) for the primary key.
295: * This reuses the same instance of cache key (searchKey) for all of the get to improve performance.
296: */
297: protected CacheKey getCacheKey(Vector primaryKey) {
298: CacheKey key = null;
299:
300: synchronized (this ) {
301: getSearchKey().setKey(primaryKey);
302: key = getCacheKey(getSearchKey());
303: }
304:
305: return key;
306: }
307:
308: /**
309: * Return the cache key (with object) matching the cache key wrapper of the primary key.
310: */
311: protected abstract CacheKey getCacheKey(CacheKey cacheKey);
312:
313: /**
314: * Get the cache key (with object) for the primary key with read lock.
315: */
316: protected CacheKey getCacheKeyWithReadLock(Vector primaryKey) {
317: CacheKey key = getCacheKey(primaryKey);
318:
319: if (key != null) {
320: key.acquireReadLock();
321: key.releaseReadLock();
322: }
323:
324: return key;
325: }
326:
327: /**
328: * INTERNAL:
329: * Returns the class which should be used as an identity map. For JDK1.1.x
330: * FullIdentityMap is the default, for JDK1.2 it is SoftCacheWeakIdentityMap.
331: *
332: * @return java.lang.Class
333: */
334: public static Class getDefaultIdentityMapClass() {
335: return ClassConstants.SoftCacheWeakIdentityMap_Class;
336: }
337:
338: /**
339: * @return The maxSize for the IdentityMap (NOTE: some subclasses may use this differently).
340: */
341: public int getMaxSize() {
342: if (maxSize == -1) {
343: maxSize = 100;
344: }
345: return maxSize;
346: }
347:
348: protected CacheKey getSearchKey() {
349: return searchKey;
350: }
351:
352: /**
353: * Return the number of objects in the receiver.
354: */
355: public abstract int getSize();
356:
357: /**
358: * Return the number of actual objects of type myClass in the IdentityMap.
359: * Recurse = true will include subclasses of myClass in the count.
360: */
361: public abstract int getSize(Class myClass, boolean recurse);
362:
363: /**
364: * Get the wrapper object from the cache key associated with the given primary key,
365: * this is used for EJB.
366: */
367: public Object getWrapper(Vector primaryKey) {
368: CacheKey cacheKey = getCacheKeyWithReadLock(primaryKey);
369:
370: if (cacheKey == null) {
371: return null;
372: } else {
373: return cacheKey.getWrapper();
374: }
375: }
376:
377: /**
378: * Get the write lock value from the cache key associated to the primarykey
379: */
380: public Object getWriteLockValue(Vector primaryKey) {
381: CacheKey cacheKey = getCacheKeyWithReadLock(primaryKey);
382:
383: if (cacheKey == null) {
384: return null;
385: } else {
386: return cacheKey.getWriteLockValue();
387: }
388: }
389:
390: /**
391: * Initialize the newly allocated instance of this class. This method must be called in order for
392: * the IdentityMap to be functional.
393: * @param anInteger is the maximum size to be allocated for the recevier.
394: */
395: public void initialize(int size) {
396: setMaxSize(size);
397: }
398:
399: /**
400: * Allow for the cache keys to be iterated on.
401: */
402: public abstract Enumeration keys();
403:
404: /**
405: * Store the object in the cache at its primary key.
406: * @param primaryKey is the primary key for the object.
407: * @param object is the domain object to cache.
408: * @param writeLockValue is the current write lock value of object, if null the version is ignored.
409: * @param readTime the read time of the object to be stored in the cache
410: */
411: public abstract CacheKey put(Vector primaryKey, Object object,
412: Object writeLockValue, long readTime);
413:
414: /**
415: * Store the object in the cache with the cache key.
416: * Should be overide by the sub-class
417: */
418: protected abstract void put(CacheKey cacheKey);
419:
420: /**
421: * Remove the primary key from the cache.
422: */
423: public Object remove(Vector primaryKey) {
424: CacheKey key = getCacheKey(primaryKey);
425: return remove(key);
426: }
427:
428: /**
429: * Remove the cache key from the cache.
430: */
431: public abstract Object remove(CacheKey cacheKey);
432:
433: /**
434: * Set the maximum size for the recevier.
435: * @param anInteger is the new maximum size.
436: */
437: protected void setMaxSize(int size) {
438: maxSize = size;
439: }
440:
441: /**
442: * INTERNAL:
443: * This method will be used to update the max cache size, any objects exceeding the max cache size will
444: * be remove from the cache. Please note that this does not remove the object from the identityMap, except in
445: * the case of the CacheIdentityMap.
446: */
447: public synchronized void updateMaxSize(int maxSize) {
448: setMaxSize(maxSize);
449: }
450:
451: protected void setSearchKey(CacheKey searchKey) {
452: this .searchKey = searchKey;
453: }
454:
455: /**
456: * Update the wrapper object the cache key associated with the given primary key,
457: * this is used for EJB.
458: */
459: public void setWrapper(Vector primaryKey, Object wrapper) {
460: CacheKey cacheKey = getCacheKey(primaryKey);
461:
462: if (cacheKey != null) {
463: cacheKey.setWrapper(wrapper);
464: }
465: }
466:
467: /**
468: * Update the write lock value of the cache key associated with the given primary key,
469: */
470: public void setWriteLockValue(Vector primaryKey,
471: Object writeLockValue) {
472: CacheKey cacheKey = getCacheKey(primaryKey);
473:
474: if (cacheKey != null) {
475: //lock/release the cache key during the lock value updating
476: cacheKey.acquire();
477: cacheKey.setWriteLockValue(writeLockValue);
478: cacheKey.release();
479: }
480: }
481:
482: public String toString() {
483: return oracle.toplink.essentials.internal.helper.Helper
484: .getShortClassName(getClass())
485: + "[" + getSize() + "]";
486: }
487:
488: /**
489: * This is used to notify the identity map of a locked keys modification to allow updating of weak refs.
490: */
491: public void updateCacheKey(CacheKey cacheKey) {
492: return;
493: }
494: }
|