/* * The contents of this file are subject to the terms * of the Common Development and Distribution License * (the "License"). You may not use this file except * in compliance with the License. * * You can obtain a copy of the license at * glassfish/bootstrap/legal/CDDLv1.0.txt or * https://glassfish.dev.java.net/public/CDDLv1.0.html. * See the License for the specific language governing * permissions and limitations under the License. * * When distributing Covered Code, include this CDDL * HEADER in each file and include the License file at * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable, * add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your * own identifying information: Portions Copyright [yyyy] * [name of copyright owner] */ // Copyright (c) 1998, 2006, Oracle. All rights reserved. package oracle.toplink.essentials.internal.identitymaps; import java.io.*; import java.util.Arrays; import java.util.Vector; import oracle.toplink.essentials.internal.helper.*; import oracle.toplink.essentials.sessions.Record; /** *

Purpose: Container class for storing objects in an IdentityMap. *

Responsibilities:

* @since TOPLink/Java 1.0 */ public class CacheKey implements Serializable, Cloneable { /** The key holds the vector of primary key values for the object. */ protected Vector key; /** Calculated hash value for CacheKey from primary key values. */ protected int hash; protected Object object; //used to store a reference to the map this cachkey is in in cases where the //cache key is to be removed, prevents us from having to track down the owning //map protected IdentityMap mapOwner; /** The writeLock value is being held as an object so that it might contain a number or timestamp. */ protected Object writeLockValue; /** The cached wrapper for the object, used in EJB. */ protected Object wrapper; /** The cache key hold a reference to the concurrency manager to perform the cache key level locking. */ protected ConcurrencyManager mutex; /** This is used for Document Preservation to cache the record that this object was built from */ protected Record record; /** This attribute is the system time in milli seconds that the object was last refreshed on */ //CR #4365 // CR #2698903 - fix for the previous fix. No longer using millis. protected long lastUpdatedQueryId; /** Invalidation State can be used to indicate whether this cache key is considered valid */ protected int invalidationState = CHECK_INVALIDATION_POLICY; /** The following constants are used for the invalidationState variable */ public static final int CHECK_INVALIDATION_POLICY = 0; public static final int CACHE_KEY_INVALID = -1; /** The read time stores the millisecond value of the last time the object help by this cache key was confirmed as up to date. */ protected long readTime = 0; public CacheKey(Vector primaryKeys) { this.key = primaryKeys; this.hash = computeHash(primaryKeys); } public CacheKey(Vector primaryKey, Object object, Object lockValue) { this(primaryKey); //bug4649617 use setter instead of this.object = object to avoid hard reference on object in subclasses setObject(object); this.writeLockValue = lockValue; } public CacheKey(Vector primaryKey, Object object, Object lockValue, long readTime) { this(primaryKey, object, lockValue); this.readTime = readTime; } /** * Acquire the lock on the cachek key object. */ public void acquire() { getMutex().acquire(false); } /** * Acquire the lock on the cachek key object. For the merge process * called with true from the merge process, if true then the refresh will not refresh the object */ public void acquire(boolean forMerge) { getMutex().acquire(forMerge); } /** * Acquire the lock on the cache key object. But only if the object has no lock on it * Added for CR 2317 */ public boolean acquireNoWait() { return getMutex().acquireNoWait(false); } /** * Acquire the lock on the cache key object. But only if the object has no lock on it * Added for CR 2317 * called with true from the merge process, if true then the refresh will not refresh the object */ public boolean acquireNoWait(boolean forMerge) { return getMutex().acquireNoWait(forMerge); } /** * Acquire the deferred lcok. */ public void acquireDeferredLock() { getMutex().acquireDeferredLock(); } /** * Check the read lock on the cachek key object. * This can be called to ensure the cache key has a valid built object. * It does not hold a lock, so the object could be refreshed afterwards. */ public void checkReadLock() { getMutex().checkReadLock(); } /** * Acquire the read lock on the cachek key object. */ public void acquireReadLock() { getMutex().acquireReadLock(); } /** * Acquire the read lock on the cache key object. */ public boolean acquireReadLockNoWait() { return getMutex().acquireReadLockNoWait(); } /** * INTERNAL: * Clones itself. */ public Object clone() { Object object = null; try { object = super.clone(); } catch (Exception exception) { throw new InternalError(exception.toString()); } return object; } /** * Compute a hash value for the CacheKey dependent upon the values of the primary key * instead of the identity of the receiver. * This method is intended for use by constructors only. */ protected int computeHash(Vector primaryKey) { int computedHashValue = 0; for (int index = 0; index < primaryKey.size(); index++) { Object value = primaryKey.elementAt(index); if (value != null) { //make sure to handle array hashcodes properly if (value.getClass().isArray()) { computedHashValue = computedHashValue ^ computeArrayHashCode(value); } else { computedHashValue = computedHashValue ^ (value.hashCode()); } } } return computedHashValue; } /** * Compute the hashcode for supported array types * @param obj - an array * @return hashCode value */ private int computeArrayHashCode(Object obj) { if (obj.getClass() == ClassConstants.APBYTE) { return Arrays.hashCode((byte []) obj); } else if (obj.getClass() == ClassConstants.APCHAR) { return Arrays.hashCode((char []) obj); } else { return Arrays.hashCode((Object []) obj); } } /** * Determine if the receiver is equal to anObject. * If anObject is a CacheKey, do further comparison, otherwise, return false. * @see CacheKey#equals(CacheKey) */ public boolean equals(Object object) { if (object instanceof CacheKey) { return equals((CacheKey)object); } return false; } /** * Determine if the receiver is equal to key. * Use an index compare, because it is much faster than enumerations. */ public boolean equals(CacheKey key) { if (this == key) { return true; } if (getKey().size() == key.getKey().size()) { for (int index = 0; index < getKey().size(); index++) { Object myValue = getKey().elementAt(index); Object comparisionValue = key.getKey().elementAt(index); if (myValue == null) { if (comparisionValue != null) { return false; } } else if (myValue.getClass().isArray()) { if (areArraysNotEqual(myValue, comparisionValue)) { return false; } } else { if (!(myValue.equals(comparisionValue))) { return false; } } } return true; } return false; } /** * Test arrays for equality based on supported types * currently, byte, char and object []'s * @param myValue * @param comparisonValue * @return true if they are equal, false otherwise */ private boolean areArraysNotEqual(Object myValue, Object comparisonValue) { boolean isNotEqual = true; if (isByteArray(myValue,comparisonValue)) { if (isEqual((byte []) myValue, (byte[]) comparisonValue)) { return false; } } else if (isCharArray(myValue,comparisonValue)) { if (isEqual((char []) myValue, (char[]) comparisonValue)) { return false; } } else { /* * Still kind of broken. If the primitive array type is not support * you could get class cast exceptions...but at least byte [] and char [] work */ if (isEqual((Object []) myValue, (Object []) comparisonValue)) { return false; } } return isNotEqual; } /** * Test two object arrays for equality * @param myValue * @param comparisionValue * @return true if both arrays are equal */ private boolean isEqual(Object [] myValue, Object [] comparisionValue) { return Helper.compareArrays((Object[])myValue, (Object[])comparisionValue); } /** * Test two byte []'s for equality * @param myValue * @param comparisionValue * @return true if the byte[]'s are equal, false otherwise */ private boolean isEqual(byte [] myValue, byte [] comparisionValue) { return Helper.compareByteArrays(myValue,comparisionValue); } /** * Test two char []'s for equality * @param myValue * @param comparisionValue * @return true if the char[]'s are equal, false otherwise */ private boolean isEqual(char [] myValue, char [] comparisionValue) { return Helper.compareCharArrays(myValue, comparisionValue); } /** * Tests if byte arrays are used for comparison and target cache key tests * @param myValue * @param comparisionValue * @return true if myValue and comparisonValue are both byte [], * false otherwise */ private boolean isByteArray(Object myValue, Object comparisionValue) { return (myValue.getClass() == ClassConstants.APBYTE) && (comparisionValue.getClass() == ClassConstants.APBYTE); } /** * Tests if char arrays are used for comparison and target cache key tests * @param myValue * @param comparisionValue * @return true if myValue and comparisonValue are both char [], * false otherwise */ private boolean isCharArray(Object myValue, Object comparisionValue) { return (myValue.getClass() == ClassConstants.APCHAR) && (comparisionValue.getClass() == ClassConstants.APCHAR); } /** * INTERNAL: * This method returns the system time in millis seconds at which this object was last refreshed * CR #4365 * CR #2698903 ... instead of using millis we will now use id's instead. Method * renamed appropriately. */ public long getLastUpdatedQueryId() { return this.lastUpdatedQueryId; } public Vector getKey() { return key; } /** * Return the concurrency manager. */ public synchronized ConcurrencyManager getMutex() { if (mutex == null) { mutex = new ConcurrencyManager(this); } return mutex; } public Object getObject() { return object; } public IdentityMap getOwningMap(){ return this.mapOwner; } /** * INTERNAL: * Return the current value of the Read Time variable */ public long getReadTime() { return readTime; } public Record getRecord() { return record; } public Object getWrapper() { return wrapper; } public Object getWriteLockValue() { return writeLockValue; } /** * Overrides hashCode() in Object to use the primaryKey's hashCode for storage in data structures. */ public int hashCode() { return hash; } /** * Return if the lock is acquired */ public boolean isAcquired() { return getMutex().isAcquired(); } /** * INTERNAL: * Return the value of the invalidationState Variable * The return value will be a constant * CHECK_INVALIDATION_POLICY - The Invalidation policy is must be checked for this cache key's sate * CACHE_KEY_INVALID - This cache key has been labeled invalid. */ public int getInvalidationState() { return invalidationState; } /** * Release the lock on the cachek key object. */ public void release() { getMutex().release(); } /** * Release the deferred lock */ public void releaseDeferredLock() { getMutex().releaseDeferredLock(); } /** * Release the read lock on the cachek key object. */ public void releaseReadLock() { getMutex().releaseReadLock(); } /** * INTERNAL: * Set the value of the invalidationState Variable * The possible values are from an enumeration of constants * CHECK_INVALIDATION_POLICY - The invalidation policy is must be checked for this cache key's sate * CACHE_KEY_INVALID - This cache key has been labeled invalid. */ public void setInvalidationState(int invalidationState) { this.invalidationState = invalidationState; } /** * INTERNAL: * This method sets the system time in millis seconds at which this object was last refreshed * CR #4365 * CR #2698903 ... instead of using millis we will now use ids instead. Method * renamed appropriately. */ public void setLastUpdatedQueryId(long id) { this.lastUpdatedQueryId = id; } public void setKey(Vector key) { this.key = key; this.hash = computeHash(key); } /** * Set the concurrency manager. */ public void setMutex(ConcurrencyManager mutex) { this.mutex = mutex; } public void setObject(Object object) { this.object = object; } public void setOwningMap(IdentityMap map){ this.mapOwner = map; } /** * INTERNAL: * Set the read time of this cache key */ public void setReadTime(long readTime) { this.readTime = readTime; invalidationState = CHECK_INVALIDATION_POLICY; } public void setRecord(Record newRecord) { this.record = newRecord; } public void setWrapper(Object wrapper) { this.wrapper = wrapper; } public void setWriteLockValue(Object writeLockValue) { this.writeLockValue = writeLockValue; } public String toString() { int hashCode = 0; if (getObject() != null) { hashCode = getObject().hashCode(); } return "[" + getKey() + ": " + hashCode + ": " + getWriteLockValue() + ": " + getReadTime() + ": " + getObject() + "]"; } /** * Notifies that cache key that it has been accessed. * Allows the LRU sub-cache to be maintained. */ public void updateAccess() { // Nothing required by default. } }