org.eclipse.persistence.internal.identitymaps.AbstractIdentityMap Maven / Gradle / Ivy
Show all versions of eclipselink Show documentation
/*
* Copyright (c) 1998, 2019 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.io.Serializable;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.exceptions.QueryException;
import org.eclipse.persistence.internal.helper.ClassConstants;
import org.eclipse.persistence.internal.helper.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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
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.
*/
@Override
public abstract Enumeration elements();
/**
* Return the object cached in the identity map or null if it could not be found.
* User API.
*/
@Override
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 descriptor Descriptor type to be retrieved.
* @return Map of Entity PKs associated to the Entities that were retrieved
* @throws QueryException
*/
@Override
public Map