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

org.eclipse.persistence.internal.identitymaps.AbstractIdentityMap Maven / Gradle / Ivy

There is a newer version: 4.0.2
Show newest version
/*
 * Copyright (c) 1998, 2018 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0,
 * or the Eclipse Distribution License v. 1.0 which is available at
 * http://www.eclipse.org/org/documents/edl-v10.php.
 *
 * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
 */

// Contributors:
//     Oracle - initial API and implementation from Oracle TopLink
package org.eclipse.persistence.internal.identitymaps;

import java.util.*;
import java.io.*;

import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.internal.helper.*;
import org.eclipse.persistence.internal.sessions.AbstractSession;

/**
 * 

Purpose: Caches objects, and allows their retrieval by their primary key. *

Responsibilities:

    *
  • Store CacheKeys containing objects and possibly writeLockValues. *
  • Insert & retrieve objects from the cache. *
  • Allow retrieval and modification of writeLockValue for a cached object. *
* @see CacheKey * @since TOPLink/Java 1.0 */ public abstract class AbstractIdentityMap implements IdentityMap, Serializable, Cloneable { /** The initial or maximum size of the cache depending upon the concrete implementation. */ protected int maxSize; /** PERF: Store the descriptor to allow lastAccessed cache lookup optimization. */ protected transient ClassDescriptor descriptor; /** Is this identity map within an IsolatedClientSession */ protected boolean isIsolated; /** Session that the map is on */ protected AbstractSession session; public AbstractIdentityMap(){ } /** * Instantiate an new IdentityMap with it's maximum size.

* NOTE: Subclasses may provide different behavior for maxSize. * @param size is the maximum size to be allocated for the receiver. */ public AbstractIdentityMap(int size, ClassDescriptor descriptor, AbstractSession session, boolean isolated) { this.maxSize = size; this.descriptor = descriptor; this.isIsolated = isolated; this.session = session; } /** * Acquire a deferred lock on the object. * This is used while reading if the object has relationships without indirection. * This first thread will get an active lock. * Other threads will get deferred locks, all threads will wait until all other threads are complete before releasing their locks. */ public CacheKey acquireDeferredLock(Object primaryKey, boolean isCacheCheckComplete) { CacheKey cacheKey = getCacheKey(primaryKey, false); if (cacheKey == null) { // Create and lock a new cacheKey. CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0); newCacheKey.acquireDeferredLock(); // PERF: To avoid synchronization, getIfAbsentPut is used. cacheKey = putCacheKeyIfAbsent(newCacheKey); // Return if the newCacheKey was put as it was already locked, otherwise must still lock it. if (cacheKey == null) { return newCacheKey; } else { newCacheKey.releaseDeferredLock(); } } // Acquire a lock on the cache key. cacheKey.acquireDeferredLock(); return cacheKey; } /** * Acquire an active lock on the object. * This is used by reading (when using indirection or no relationships) and by merge. */ public CacheKey acquireLock(Object primaryKey, boolean forMerge, boolean isCacheCheckComplete) { CacheKey cacheKey = getCacheKey(primaryKey, forMerge); if (cacheKey == null) { // Create and lock a new cacheKey. CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0); newCacheKey.acquire(forMerge); // PERF: To avoid synchronization, getIfAbsentPut is used. cacheKey = putCacheKeyIfAbsent(newCacheKey); // Return if the newCacheKey was put as it was already locked, otherwise must still lock it. if (cacheKey == null) { return newCacheKey; } else { newCacheKey.release(); } } // Acquire a lock on the cache key. cacheKey.acquire(); return cacheKey; } /** * Acquire an active lock on the object, if not already locked. * This is used by merge for missing existing objects. */ public CacheKey acquireLockNoWait(Object primaryKey, boolean forMerge) { CacheKey cacheKey = getCacheKey(primaryKey, forMerge); if (cacheKey == null) { // Create and lock a new cacheKey. CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0); newCacheKey.acquire(forMerge); // PERF: To avoid synchronization, getIfAbsentPut is used. cacheKey = putCacheKeyIfAbsent(newCacheKey); // Return if the newCacheKey was put as already lock, otherwise must still lock. if (cacheKey == null) { return newCacheKey; } else { newCacheKey.release(); } } // Acquire a lock on the cache key. // Return null if already locked. if (!cacheKey.acquireNoWait(forMerge)) { return null; } return cacheKey; } /** * Acquire an active lock on the object, if not already locked. * This is used by merge for missing existing objects. */ public CacheKey acquireLockWithWait(Object primaryKey, boolean forMerge, int wait) { CacheKey cacheKey = getCacheKey(primaryKey, forMerge); if (cacheKey == null) { // Create and lock a new cacheKey. CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0); newCacheKey.acquire(forMerge); // PERF: To avoid synchronization, getIfAbsentPut is used. cacheKey = putCacheKeyIfAbsent(newCacheKey); // Return if the newCacheKey was put as already lock, otherwise must still lock. if (cacheKey == null) { return newCacheKey; } else { newCacheKey.release(); } } // Acquire a lock on the cache key. // Return null if already locked. if (!cacheKey.acquireWithWait(forMerge, wait)) { return null; } return cacheKey; } /** * Acquire a read lock on the object. * This is used by UnitOfWork cloning. * This will allow multiple users to read the same object but prevent writes to the object while the read lock is held. */ public CacheKey acquireReadLockOnCacheKey(Object primaryKey) { CacheKey cacheKey = getCacheKey(primaryKey, false); if (cacheKey == null) { CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0); // Lock new cacheKey. newCacheKey.acquireReadLock(); // Create one but not put it in the cache, as we are only reading // and should not be writing to the identitymap. return newCacheKey; } // Acquire a lock on the cache key. // Return null if already locked. cacheKey.acquireReadLock(); return cacheKey; } /** * Acquire a read lock on the object, if not already locked. * This is used by UnitOfWork cloning. * This will allow multiple users to read the same object but prevent writes to the object while the read lock is held. */ public CacheKey acquireReadLockOnCacheKeyNoWait(Object primaryKey) { CacheKey cacheKey = getCacheKey(primaryKey, false); if (cacheKey == null) { CacheKey newCacheKey = createCacheKey(primaryKey, null, null, 0); // Lock new cacheKey. newCacheKey.acquireReadLock(); // Create one but not put it in the cache, as we are only reading // and should not be writing to the identitymap. return newCacheKey; } // Acquire a lock on the cache key. // Return null if already locked. if (!cacheKey.acquireReadLockNoWait()) { return null; } return cacheKey; } /** * Add all locked CacheKeys to the map grouped by thread. * Used to print all the locks in the identity map. */ public abstract void collectLocks(HashMap threadList); /** * Clone the map and all of the CacheKeys. * This is used by UnitOfWork commitAndResumeOnFailure to avoid corrupting the cache during a failed commit. */ public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException exception) { throw new InternalError(exception.toString()); } } /** * Return true if an CacheKey with the primary key is in the map. * User API. * @param primaryKey is the primary key for the object to search for. */ public boolean containsKey(Object primaryKey) { return getCacheKeyWithReadLock(primaryKey) != null; } /** * Create the correct type of CacheKey for this map. */ public CacheKey createCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime) { return new CacheKey(primaryKey, object, writeLockValue, readTime, this.isIsolated); } /** * Allow for the cache to be iterated on. */ public abstract Enumeration elements(); /** * Return the object cached in the identity map or null if it could not be found. * User API. */ public Object get(Object primaryKey) { CacheKey cacheKey = getCacheKeyWithReadLock(primaryKey); if (cacheKey == null) { return null; } return cacheKey.getObject(); } /** * ADVANCED: * Using a list of Entity PK this method will attempt to bulk load the entire list from the cache. * In certain circumstances this can have large performance improvements over loading each item individually. * @param pkList List of Entity PKs to extract from the cache * @param ClassDescriptor Descriptor type to be retrieved. * @return Map of Entity PKs associated to the Entities that were retrieved * @throws QueryException */ public Map getAllFromIdentityMapWithEntityPK(Object[] pkList, ClassDescriptor descriptor, AbstractSession session){ HashMap map = new HashMap(); CacheKey cachedObject = null; long currentTime = System.currentTimeMillis(); for (Object pk : pkList){ cachedObject = getCacheKey(pk, false); if ((cachedObject != null && cachedObject.getObject() != null && !descriptor.getCacheInvalidationPolicy().isInvalidated(cachedObject, currentTime))){ map.put(pk, cachedObject.getObject()); } } return map; } /** * ADVANCED: * Using a list of Entity PK this method will attempt to bulk load the entire list from the cache. * In certain circumstances this can have large performance improvements over loading each item individually. * @param pkList List of Entity PKs to extract from the cache * @param ClassDescriptor Descriptor type to be retrieved. * @return Map of Entity PKs associated to the Entities that were retrieved * @throws QueryException */ public Map getAllCacheKeysFromIdentityMapWithEntityPK(Object[] pkList, ClassDescriptor descriptor, AbstractSession session){ HashMap map = new HashMap(); CacheKey cachedObject = null; long currentTime = System.currentTimeMillis(); for (Object pk : pkList){ cachedObject = getCacheKey(pk, false); if ((cachedObject != null && cachedObject.getObject() != null && !descriptor.getCacheInvalidationPolicy().isInvalidated(cachedObject, currentTime))){ map.put(pk, cachedObject); } } return map; } /** * Get the cache key (with object) for the primary key. */ public abstract CacheKey getCacheKey(Object primaryKey, boolean forMerge); /** * Get the cache key (with object) for the primary key. */ public CacheKey getCacheKeyForLock(Object primaryKey) { return getCacheKey(primaryKey, true); } /** * Return the CacheKey (with object) matching the searchKey. * If the CacheKey is missing then put the searchKey in the map. * The searchKey should have already been locked. */ protected abstract CacheKey putCacheKeyIfAbsent(CacheKey cacheKey); /** * Get the cache key (with object) for the primary key with read lock. */ protected CacheKey getCacheKeyWithReadLock(Object primaryKey) { CacheKey key = getCacheKey(primaryKey, false); if (key != null) { key.checkReadLock(); } return key; } /** * Returns the class which should be used as an identity map in a descriptor by default. */ public static Class getDefaultIdentityMapClass() { return ClassConstants.SoftCacheWeakIdentityMap_Class; } /** * @return The maxSize for the IdentityMap (NOTE: some subclasses may use this differently). */ public int getMaxSize() { if (maxSize == -1) { maxSize = 100; } return maxSize; } /** * Return the number of CacheKeys in the IdentityMap. * This may contain weak referenced objects that have been garbage collected. */ public abstract int getSize(); /** * Return the number of actual objects of type myClass in the IdentityMap. * Recurse = true will include subclasses of myClass in the count. */ public abstract int getSize(Class myClass, boolean recurse); /** * Get the wrapper object from the cache key associated with the given primary key, * this is used for EJB2. */ public Object getWrapper(Object primaryKey) { CacheKey cacheKey = getCacheKeyWithReadLock(primaryKey); if (cacheKey == null) { return null; } else { return cacheKey.getWrapper(); } } /** * Get the write lock value from the cache key associated to the primarykey. * User API. */ public Object getWriteLockValue(Object primaryKey) { CacheKey cacheKey = getCacheKeyWithReadLock(primaryKey); if (cacheKey == null) { return null; } else { return cacheKey.getWriteLockValue(); } } /** * Allow for the CacheKeys to be iterated on. */ public abstract Enumeration keys(); /** * Store the object in the cache at its primary key. * This is used by InsertObjectQuery, typically into the UnitOfWork identity map. * Merge and reads do not use put, but acquireLock. * Also an advanced (very) user API. * @param primaryKey is the primary key for the object. * @param object is the domain object to cache. * @param writeLockValue is the current write lock value of object, if null the version is ignored. */ public abstract CacheKey put(Object primaryKey, Object object, Object writeLockValue, long readTime); /** * This method may be called during initialize all identity maps. It allows the identity map * or interceptor the opportunity to release any resources before being thrown away. */ public void release(){ //no-op } /** * Remove the CacheKey with the primaryKey from the map. * This is used by DeleteObjectQuery and merge. * This is also an advanced (very) user API. */ public Object remove(Object primaryKey, Object object) { CacheKey key = getCacheKeyForLock(primaryKey); return remove(key); } /** * Remove the CacheKey from the map. */ public abstract Object remove(CacheKey cacheKey); /** * Set the maximum size for the receiver. * @param size is the new maximum size. */ protected synchronized void setMaxSize(int size) { maxSize = size; } /** * This method will be used to update the max cache size, any objects exceeding the max cache size will * be remove from the cache. Please note that this does not remove the object from the identityMap, except in * the case of the CacheIdentityMap. */ public void updateMaxSize(int maxSize) { setMaxSize(maxSize); } /** * Return the class that this is the map for. */ public ClassDescriptor getDescriptor() { return descriptor; } /** * Return the class that this is the map for. */ public Class getDescriptorClass() { if (descriptor == null) { return null; } return descriptor.getJavaClass(); } /** * Set the descriptor that this is the map for. */ public void setDescriptor(ClassDescriptor descriptor) { this.descriptor = descriptor; } /** * Update the wrapper object in the CacheKey associated with the given primaryKey, * this is used for EJB2. */ public void setWrapper(Object primaryKey, Object wrapper) { CacheKey cacheKey = getCacheKeyForLock(primaryKey); if (cacheKey != null) { cacheKey.setWrapper(wrapper); } } /** * Update the write lock value of the CacheKey associated with the given primaryKey. * This is used by UpdateObjectQuery, and is also an advanced (very) user API. */ public void setWriteLockValue(Object primaryKey, Object writeLockValue) { CacheKey cacheKey = getCacheKeyForLock(primaryKey); if (cacheKey != null) { //lock/release the cache key during the lock value updating cacheKey.acquire(); cacheKey.setWriteLockValue(writeLockValue); cacheKey.release(); } } public String toString() { return Helper.getShortClassName(getClass()) + "[" + getSize() + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy