| /* |
| * Copyright (c) 1998, 2021 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.sessions; |
| |
| import java.util.*; |
| |
| import org.eclipse.persistence.internal.identitymaps.*; |
| import org.eclipse.persistence.queries.*; |
| import org.eclipse.persistence.expressions.*; |
| import org.eclipse.persistence.exceptions.*; |
| import org.eclipse.persistence.descriptors.CacheIndex; |
| import org.eclipse.persistence.descriptors.ClassDescriptor; |
| import org.eclipse.persistence.sessions.DataRecord; |
| import org.eclipse.persistence.logging.SessionLog; |
| import org.eclipse.persistence.internal.descriptors.ObjectBuilder; |
| import org.eclipse.persistence.internal.descriptors.PersistenceEntity; |
| import org.eclipse.persistence.internal.helper.WriteLockManager; |
| |
| /** |
| * INTERNAL: |
| * Internal subclass that provides access to identity maps through the session. |
| * Implements the IdentityMapAccessor interface which provides all publicly available |
| * identity map functionality to users. |
| * This is the main class that should be used to access identity maps. In general, any |
| * function that accesses the identity map manager should go through this class |
| * Any session specific functionality appears in subclasses |
| */ |
| public class IsolatedClientSessionIdentityMapAccessor extends org.eclipse.persistence.internal.sessions.IdentityMapAccessor { |
| |
| protected Map objectsLockedForClone; |
| |
| /** |
| * INTERNAL: |
| * An IdentityMapAccessor sits between the session and the identityMapManager |
| * It needs references in both directions |
| */ |
| public IsolatedClientSessionIdentityMapAccessor(AbstractSession session) { |
| super(session); |
| } |
| |
| /** |
| * INTERNAL: |
| * Deferred lock the identity map for the object, this is used for avoiding deadlock |
| * The return cacheKey should be used to release the deferred lock |
| */ |
| @Override |
| public CacheKey acquireDeferredLock(Object primaryKey, Class<?> javaClass, ClassDescriptor descriptor, boolean isCacheCheckComplete) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().acquireDeferredLock(primaryKey, javaClass, descriptor, isCacheCheckComplete); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireDeferredLock(primaryKey, javaClass, descriptor, isCacheCheckComplete); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Provides access for setting a concurrency lock on an object in the IdentityMap. |
| * called with true from the merge process, if true then the refresh will not refresh the object. |
| */ |
| @Override |
| public CacheKey acquireLock(Object primaryKey, Class<?> domainClass, boolean forMerge, ClassDescriptor descriptor, boolean isCacheCheckComplete) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().acquireLock(primaryKey, domainClass, forMerge, descriptor, isCacheCheckComplete); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireLock(primaryKey, domainClass, forMerge, descriptor, isCacheCheckComplete); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Provides access for setting a concurrency lock on an object in the IdentityMap. |
| * called with true from the merge process, if true then the refresh will not refresh the object. |
| */ |
| @Override |
| public CacheKey acquireLockNoWait(Object primaryKey, Class<?> domainClass, boolean forMerge, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().acquireLockNoWait(primaryKey, domainClass, forMerge, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireLockNoWait(primaryKey, domainClass, forMerge, descriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Provides access for setting a concurrency lock on an object in the IdentityMap. |
| * called with true from the merge process, if true then the refresh will not refresh the object. |
| */ |
| @Override |
| public CacheKey acquireLockWithWait(Object primaryKey, Class<?> domainClass, boolean forMerge, ClassDescriptor descriptor, int wait) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().acquireLockWithWait(primaryKey, domainClass, forMerge, descriptor, wait); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireLockWithWait(primaryKey, domainClass, forMerge, descriptor, wait); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Find the cachekey for the provided primary key and place a readlock on it. |
| * 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, Class<?> domainClass, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().acquireReadLockOnCacheKey(primaryKey, domainClass, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireReadLockOnCacheKey(primaryKey, domainClass, descriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Find the cachekey for the provided primary key and place a readlock on it. |
| * This will allow multiple users to read the same object but prevent writes to |
| * the object while the read lock is held. |
| * If no readlock can be acquired then do not wait but return null. |
| */ |
| @Override |
| public CacheKey acquireReadLockOnCacheKeyNoWait(Object primaryKey, Class<?> domainClass, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().acquireReadLockOnCacheKeyNoWait(primaryKey, domainClass, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireReadLockOnCacheKeyNoWait(primaryKey, domainClass, descriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Lock the entire cache if the cache isolation requires. |
| * By default concurrent reads and writes are allowed. |
| * By write, unit of work merge is meant. |
| */ |
| @Override |
| public boolean acquireWriteLock() { |
| getIdentityMapManager().acquireWriteLock(); |
| // must lock the parents cache as well. |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().acquireWriteLock(); |
| } |
| |
| /** |
| * ADVANCED: |
| * Return if their is an object for the primary key. |
| */ |
| @Override |
| public boolean containsObjectInIdentityMap(Object primaryKey, Class<?> theClass, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| if (this.identityMapManager == null) { |
| return false; |
| } |
| return getIdentityMapManager().containsKey(primaryKey, theClass, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().containsObjectInIdentityMap(primaryKey, theClass, descriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * This method is used to get a list of those classes with IdentityMaps in the Session. |
| */ |
| @Override |
| public Vector getClassesRegistered() { |
| Vector results = getIdentityMapManager().getClassesRegistered(); |
| results.addAll(((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getClassesRegistered()); |
| return results; |
| } |
| |
| /** |
| * ADVANCED: |
| * Query the cache in-memory. |
| * If the expression is too complex an exception will be thrown. |
| * Only return objects that are invalid in the cache if specified. |
| */ |
| @Override |
| public Vector getAllFromIdentityMap(Expression selectionCriteria, Class<?> theClass, DataRecord translationRow, int valueHolderPolicy, boolean shouldReturnInvalidatedObjects) throws QueryException { |
| if (!session.getDescriptor(theClass).getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().getAllFromIdentityMap(selectionCriteria, theClass, translationRow, valueHolderPolicy, shouldReturnInvalidatedObjects); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getAllFromIdentityMap(selectionCriteria, theClass, translationRow, valueHolderPolicy, shouldReturnInvalidatedObjects); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Retrieve the cache key for the given identity information. |
| * @param primaryKey the primary key of the cache key to be retrieved. |
| * @param myClass the class of the cache key to be retrieved. |
| */ |
| @Override |
| public CacheKey getCacheKeyForObject(Object primaryKey, Class<?> myClass, ClassDescriptor descriptor, boolean forMerge) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| if (this.identityMapManager == null) { |
| return null; |
| } |
| return getIdentityMapManager().getCacheKeyForObject(primaryKey, myClass, descriptor, forMerge); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getCacheKeyForObject(primaryKey, myClass, descriptor, forMerge); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Retrieve the cache key for the given identity information. |
| * @param primaryKey the primary key of the cache key to be retrieved. |
| * @param myClass the class of the cache key to be retrieved. |
| */ |
| @Override |
| public CacheKey getCacheKeyForObjectForLock(Object primaryKey, Class<?> myClass, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| if (this.identityMapManager == null) { |
| return null; |
| } |
| return getIdentityMapManager().getCacheKeyForObjectForLock(primaryKey, myClass, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getCacheKeyForObjectForLock(primaryKey, myClass, descriptor); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Return the object from the identity with the primary and class. |
| */ |
| @Override |
| public Object getFromIdentityMap(Object primaryKey, Object object, Class<?> theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()){ |
| Object cachedObject = null; |
| if (this.identityMapManager != null){ |
| cachedObject = getIdentityMapManager().getFromIdentityMap(primaryKey, theClass, shouldReturnInvalidatedObjects, descriptor); |
| } |
| if (descriptor.getCachePolicy().isIsolated()) { |
| return cachedObject; |
| }else{ |
| return getAndCloneCacheKeyFromParent(primaryKey, object, theClass, shouldReturnInvalidatedObjects, descriptor); |
| } |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getFromIdentityMap(primaryKey, object, theClass, shouldReturnInvalidatedObjects, descriptor); |
| } |
| } |
| |
| protected Object getAndCloneCacheKeyFromParent(Object primaryKey, Object objectToClone, Class<?> theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) { |
| CacheKey cacheKey = null; |
| if (objectToClone != null && objectToClone instanceof PersistenceEntity){ |
| cacheKey = ((PersistenceEntity)objectToClone)._persistence_getCacheKey(); |
| } |
| if (cacheKey == null || cacheKey.isIsolated() || cacheKey.getOwningMap() == null){ |
| org.eclipse.persistence.internal.sessions.IdentityMapAccessor parentIdentityMapAccessor = session.getParent().getIdentityMapAccessorInstance(); |
| cacheKey = parentIdentityMapAccessor.getCacheKeyForObject(primaryKey, theClass, descriptor, false); |
| } |
| Object objectFromCache = null; |
| // this check could be simplified to one line but would create a window |
| // in which GC could remove the object and we would end up with a null pointer |
| // as well we must inspect the cacheKey without locking on it. |
| if ((cacheKey != null) && (shouldReturnInvalidatedObjects || !descriptor.getCacheInvalidationPolicy().isInvalidated(cacheKey))) { |
| synchronized (cacheKey) { |
| //if the object in the cachekey is null but the key is acquired then |
| //someone must be rebuilding it or creating a new one. Sleep until |
| // it's finished. A plain wait here would be more efficient but we may not |
| // get notified for quite some time (ie deadlock) if the other thread |
| //is building the object. Must wait and not sleep in order for the monitor to be released |
| objectFromCache = cacheKey.getObject(); |
| try { |
| while (cacheKey.isAcquired() && (objectFromCache == null)) { |
| cacheKey.wait(5); |
| } |
| } catch (InterruptedException ex) { |
| } |
| if (objectFromCache == null) { |
| return null; |
| } |
| } |
| } else { |
| return null; |
| } |
| |
| ClassDescriptor concreteDescriptor = descriptor; |
| // Ensure correct subclass descriptor. |
| if (objectFromCache.getClass() != descriptor.getJavaClass()) { |
| concreteDescriptor = session.getDescriptor(objectFromCache); |
| } |
| ObjectBuilder builder = concreteDescriptor.getObjectBuilder(); |
| Object workingClone = null; |
| |
| // The cache/objects being registered must first be locked to ensure |
| // that a merge or refresh does not occur on the object while being cloned to |
| // avoid cloning a partially merged/refreshed object. |
| // If a cache isolation level is used, then lock the entire cache. |
| // otherwise lock the object and it related objects (not using indirection) as a unit. |
| // If just a simple object (all indirection) a simple read-lock can be used. |
| // PERF: Cache if check to write is required. |
| org.eclipse.persistence.internal.sessions.IdentityMapAccessor parentIdentityMapAccessor = session.getParent().getIdentityMapAccessorInstance(); |
| boolean identityMapLocked = parentIdentityMapAccessor.acquireWriteLock(); |
| boolean rootOfCloneRecursion = false; |
| if (identityMapLocked) { |
| session.checkAndRefreshInvalidObject(objectFromCache, cacheKey, descriptor); |
| } else { |
| // Check if we have locked all required objects already. |
| if (this.objectsLockedForClone == null) { |
| // PERF: If a simple object just acquire a simple read-lock. |
| if (concreteDescriptor.shouldAcquireCascadedLocks()) { |
| this.objectsLockedForClone = parentIdentityMapAccessor.getWriteLockManager().acquireLocksForClone(objectFromCache, concreteDescriptor, cacheKey, session); |
| } else { |
| session.checkAndRefreshInvalidObject(objectFromCache, cacheKey, descriptor); |
| cacheKey.acquireReadLock(); |
| } |
| rootOfCloneRecursion = true; |
| } |
| } |
| try { |
| // bug:6167576 Must acquire the lock before cloning. |
| workingClone = builder.instantiateWorkingCopyClone(objectFromCache, session); |
| // PERF: Cache the primary key if implements PersistenceEntity. |
| if (workingClone instanceof PersistenceEntity) { |
| ((PersistenceEntity)workingClone)._persistence_setId(cacheKey.getKey()); |
| } |
| CacheKey localCacheKey = acquireLock(primaryKey, theClass, descriptor, false); |
| try{ |
| localCacheKey.setObject(workingClone); |
| localCacheKey.setReadTime(cacheKey.getReadTime()); |
| localCacheKey.setWriteLockValue(cacheKey.getWriteLockValue()); |
| builder.populateAttributesForClone(objectFromCache, cacheKey, workingClone, null, session); |
| }finally{ |
| localCacheKey.release(); |
| } |
| |
| //also clone the fetch group reference if applied |
| if (concreteDescriptor.hasFetchGroupManager()) { |
| concreteDescriptor.getFetchGroupManager().copyFetchGroupInto(objectFromCache, workingClone, session); |
| } |
| } finally { |
| // If the entire cache was locked, release the cache lock, |
| // otherwise either release the cache-key for a simple lock, |
| // otherwise release the entire set of locks for related objects if this was the root. |
| if (identityMapLocked) { |
| parentIdentityMapAccessor.releaseWriteLock(); |
| } else { |
| if (rootOfCloneRecursion) { |
| if (this.objectsLockedForClone == null) { |
| cacheKey.releaseReadLock(); |
| } else { |
| for (Iterator iterator = this.objectsLockedForClone.values().iterator(); iterator.hasNext();) { |
| ((CacheKey)iterator.next()).releaseReadLock(); |
| } |
| this.objectsLockedForClone = null; |
| } |
| session.executeDeferredEvents(); |
| } |
| } |
| } |
| concreteDescriptor.getObjectBuilder().instantiateEagerMappings(workingClone, session); |
| return workingClone; |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the object from the local identity map with the primary and class. |
| * This avoids checking the parent cache for the unit of work. |
| */ |
| @Override |
| public Object getFromLocalIdentityMap(Object primaryKey, Class<?> theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) { |
| return getFromIdentityMap(primaryKey, null, theClass, shouldReturnInvalidatedObjects, descriptor); |
| } |
| |
| /** |
| * INTERNAL: |
| * Query the cache in-memory. |
| * If the object is not found null is returned. |
| * If the expression is too complex an exception will be thrown. |
| */ |
| @Override |
| public Object getFromIdentityMap(Expression selectionCriteria, Class<?> theClass, DataRecord translationRow, int valueHolderPolicy, boolean conforming, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().getFromIdentityMap(selectionCriteria, theClass, translationRow, valueHolderPolicy, conforming, shouldReturnInvalidatedObjects, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getFromIdentityMap(selectionCriteria, theClass, translationRow, valueHolderPolicy, conforming, shouldReturnInvalidatedObjects, descriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Return the object from the identity with the primary and class. |
| * Only return invalidated objects if requested |
| */ |
| @Override |
| public Object getFromIdentityMapWithDeferredLock(Object primaryKey, Class<?> theClass, boolean shouldReturnInvalidatedObjects, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| if (this.identityMapManager == null) { |
| return null; |
| } |
| return getIdentityMapManager().getFromIdentityMapWithDeferredLock(primaryKey, theClass, shouldReturnInvalidatedObjects, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getFromIdentityMapWithDeferredLock(primaryKey, theClass, shouldReturnInvalidatedObjects, descriptor); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the IdentityMapManager for this IdentityMapAccessor |
| * This method should be used for all IdentityMapManager access since it may |
| * be overridden in sub classes. |
| */ |
| @Override |
| public IdentityMapManager getIdentityMapManager() { |
| // PERF: Lazy init manager as normally isolated object are only read in the unit of work. |
| if (this.identityMapManager == null) { |
| this.identityMapManager = new IdentityMapManager(this.session); |
| } |
| return this.identityMapManager; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the identity map for the given class from the IdentityMapManager |
| */ |
| @Override |
| public IdentityMap getIdentityMap(ClassDescriptor descriptor, boolean returnNullIfMissing) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().getIdentityMap(descriptor, returnNullIfMissing); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getIdentityMap(descriptor, returnNullIfMissing); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the cached results associated with a query. Results are cached by the |
| * values of the parameters to the query so different parameters will have |
| * different cached results. |
| */ |
| @Override |
| public Object getQueryResult(ReadQuery query, List parameters, boolean checkExpiry) { |
| if (((IsolatedClientSession)session).isIsolatedQuery(query)) { |
| return getIdentityMapManager().getQueryResult(query, parameters, checkExpiry); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getQueryResult(query, parameters, checkExpiry); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * get the session associated with this IdentityMapAccessor |
| */ |
| @Override |
| public AbstractSession getSession() { |
| return session; |
| } |
| |
| /** |
| * INTERNAL: |
| * Get the wrapper object from the cache key associated with the given primary key, |
| * this is used for EJB. |
| */ |
| @Override |
| public Object getWrapper(Object primaryKey, Class<?> theClass) { |
| if (!session.getDescriptor(theClass).getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().getWrapper(primaryKey, theClass); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getWrapper(primaryKey, theClass); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Returns the single write Lock manager for this session |
| */ |
| @Override |
| public WriteLockManager getWriteLockManager() { |
| // As there should only be one write lock manager per server session |
| // get the one from the parent. |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getWriteLockManager(); |
| } |
| |
| /** |
| * ADVANCED: |
| * Extract the write lock value from the identity map. |
| */ |
| @Override |
| public Object getWriteLockValue(Object primaryKey, Class<?> theClass, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().getWriteLockValue(primaryKey, theClass, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getWriteLockValue(primaryKey, theClass, descriptor); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Reset the entire object cache. |
| * <p> NOTE: be careful using this method. This method blows away both this session's and its parents caches, |
| * this includes the server cache or any other cache. This throws away any objects that have been read in. |
| * Extream caution should be used before doing this because object identity will no longer |
| * be maintained for any objects currently read in. This should only be called |
| * if the application knows that it no longer has references to object held in the cache. |
| */ |
| @Override |
| public void initializeAllIdentityMaps() { |
| super.initializeAllIdentityMaps(); |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().initializeAllIdentityMaps(); |
| } |
| |
| /** |
| * PUBLIC: |
| * Reset the identity map for only the instances of the class. |
| * For inheritance the user must make sure that they only use the root class. |
| * Caution must be used in doing this to ensure that the objects within the identity map |
| * are not referenced from other objects of other classes or from the application. |
| */ |
| @Override |
| public void initializeIdentityMap(Class<?> theClass) { |
| getSession().log(SessionLog.FINER, SessionLog.CACHE, "initialize_identitymap", theClass); |
| if (!session.getDescriptor(theClass).getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().initializeIdentityMap(theClass); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().initializeIdentityMap(theClass); |
| } |
| } |
| |
| /** |
| * Invalidate/remove any results for the class from the query cache. |
| * This is used to invalidate the query cache on any change. |
| */ |
| @Override |
| public void invalidateQueryCache(Class<?> classThatChanged) { |
| if (!session.getDescriptor(classThatChanged).getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().invalidateQueryCache(classThatChanged); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().invalidateQueryCache(classThatChanged); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Reset the entire local object cache. |
| * This throws away any objects that have been read in. |
| * Extreme caution should be used before doing this because object identity will no longer |
| * be maintained for any objects currently read in. This should only be called |
| * if the application knows that it no longer has references to object held in the cache. |
| */ |
| @Override |
| public void initializeIdentityMaps() { |
| getSession().log(SessionLog.FINER, SessionLog.CACHE, "initialize_identitymaps"); |
| getIdentityMapManager().initializeIdentityMaps(); |
| getSession().getCommitManager().reinitialize(); |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the results for a query. |
| * Query results are cached based on the parameter values provided to the query |
| * different parameter values access different caches. |
| */ |
| @Override |
| public void putQueryResult(ReadQuery query, List parameters, Object results) { |
| if (((IsolatedClientSession)session).isIsolatedQuery(query)) { |
| getIdentityMapManager().putQueryResult(query, parameters, results); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().putQueryResult(query, parameters, results); |
| } |
| } |
| |
| /** |
| * Index the cache key by the index values. |
| */ |
| @Override |
| public void putCacheKeyByIndex(CacheIndex index, CacheId indexValues, CacheKey cacheKey, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().putCacheKeyByIndex(index, indexValues, cacheKey, descriptor); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().putCacheKeyByIndex(index, indexValues, cacheKey, descriptor); |
| } |
| } |
| |
| /** |
| * Return the cache key for the cache index or null if not found. |
| */ |
| @Override |
| public CacheKey getCacheKeyByIndex(CacheIndex index, CacheId indexValues, boolean shouldCheckExpiry, ClassDescriptor descriptor) { |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().getCacheKeyByIndex(index, indexValues, shouldCheckExpiry, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().getCacheKeyByIndex(index, indexValues, shouldCheckExpiry, descriptor); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Used to print all the objects in the identity map of the passed in class. |
| * The output of this method will be logged to this session's SessionLog at SEVERE level. |
| */ |
| @Override |
| public void printIdentityMap(Class<?> businessClass) { |
| if (getSession().shouldLog(SessionLog.SEVERE, SessionLog.CACHE)) { |
| if (!session.getDescriptor(businessClass).getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().printIdentityMap(businessClass); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().printIdentityMap(businessClass); |
| } |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Used to print all the objects in every identity map in this session. |
| * The output of this method will be logged to this session's SessionLog at SEVERE level. |
| */ |
| @Override |
| public void printIdentityMaps() { |
| if (getSession().shouldLog(SessionLog.SEVERE, SessionLog.CACHE)) { |
| getIdentityMapManager().printIdentityMaps(); |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().printIdentityMaps(); |
| } |
| } |
| |
| /** |
| * PUBLIC: |
| * Used to print all the locks in every identity map in this session. |
| * The output of this method will be logged to this session's SessionLog at FINEST level. |
| */ |
| @Override |
| public void printIdentityMapLocks() { |
| if (getSession().shouldLog(SessionLog.FINEST, SessionLog.CACHE)) { |
| getIdentityMapManager().printLocks(); |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().printIdentityMapLocks(); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Register the object with the identity map. |
| * The object must always be registered with its version number if optimistic locking is used. |
| * The readTime may also be included in the cache key as it is constructed. |
| */ |
| @Override |
| public CacheKey internalPutInIdentityMap(Object domainObject, Object key, Object writeLockValue, long readTime, ClassDescriptor descriptor) { |
| //no need to unwrap as the put will unwrap later anyway |
| if (!descriptor.getCachePolicy().isSharedIsolation()) { |
| return getIdentityMapManager().putInIdentityMap(domainObject, key, writeLockValue, readTime, descriptor); |
| } else { |
| return ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().internalPutInIdentityMap(domainObject, key, writeLockValue, readTime, descriptor); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Queries the cache in-memory with the passed in criteria and invalidates matching Objects. |
| * If the expression is too complex either all or none object of theClass invalidated (depending on shouldInvalidateOnException value). |
| * @param selectionCriteria Expression selecting the Objects to be returned |
| * @param theClass Class to be considered |
| * @param translationRow Record |
| * @param shouldInvalidateOnException boolean indicates weather to invalidate the object if conform threw exception. |
| */ |
| @Override |
| public void invalidateObjects(Expression selectionCriteria, Class<?> theClass, DataRecord translationRow, boolean shouldInvalidateOnException) { |
| if (!session.getDescriptor(theClass).getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().invalidateObjects(selectionCriteria, theClass, translationRow, shouldInvalidateOnException); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().invalidateObjects(selectionCriteria, theClass, translationRow, shouldInvalidateOnException); |
| } |
| } |
| |
| /** |
| * INTERNAL: |
| * Lock the entire cache if the cache isolation requires. |
| * By default concurrent reads and writes are allowed. |
| * By write, unit of work merge is meant. |
| */ |
| @Override |
| public void releaseWriteLock() { |
| //release in the opposite order of the acquire |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().releaseWriteLock(); |
| getIdentityMapManager().releaseWriteLock(); |
| } |
| |
| /** |
| * ADVANCED: |
| * Remove the object from the object cache. |
| */ |
| @Override |
| public Object removeFromIdentityMap(Object key, Class<?> theClass, ClassDescriptor descriptor, Object object) { |
| Object removedObject = null; |
| if (descriptor.isIsolated() || descriptor.isProtectedIsolation()) { |
| removedObject = getIdentityMapManager().removeFromIdentityMap(key, theClass, descriptor, object); |
| } |
| if (!descriptor.isIsolated()){ |
| removedObject = ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().removeFromIdentityMap(key, theClass, descriptor, object); |
| } |
| return removedObject; |
| } |
| |
| /** |
| * INTERNAL: |
| * Set the IdentityMapManager for this IdentityMapAccessor |
| */ |
| @Override |
| public void setIdentityMapManager(IdentityMapManager identityMapManager) { |
| this.identityMapManager = identityMapManager; |
| } |
| |
| /** |
| * INTERNAL: |
| * Update the wrapper object the cache key associated with the given primary key, |
| * this is used for EJB. |
| */ |
| @Override |
| public void setWrapper(Object primaryKey, Class<?> theClass, Object wrapper) { |
| if (!getSession().getDescriptor(theClass).getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().setWrapper(primaryKey, theClass, wrapper); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().setWrapper(primaryKey, theClass, wrapper); |
| } |
| } |
| |
| /** |
| * ADVANCED: |
| * Update the write lock value in the identity map. |
| */ |
| @Override |
| public void updateWriteLockValue(Object primaryKey, Class<?> theClass, Object writeLockValue) { |
| if (!getSession().getDescriptor(theClass).getCachePolicy().isSharedIsolation()) { |
| getIdentityMapManager().setWriteLockValue(primaryKey, theClass, writeLockValue); |
| } else { |
| ((IsolatedClientSession)session).getParent().getIdentityMapAccessorInstance().updateWriteLockValue(primaryKey, theClass, writeLockValue); |
| } |
| } |
| } |