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

org.eclipse.persistence.sessions.interceptors.CacheInterceptor 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:
//     Gordon Yorke - Initial Feature development
//     12/14/2017-3.0 Tomas Kraus
//       - 522635: ConcurrentModificationException when triggering lazy load from conforming query
package org.eclipse.persistence.sessions.interceptors;

import java.util.Collection;
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.indirection.ValueHolderInterface;
import org.eclipse.persistence.internal.helper.Helper;
import org.eclipse.persistence.internal.identitymaps.CacheKey;
import org.eclipse.persistence.internal.identitymaps.IdentityMap;
import org.eclipse.persistence.internal.sessions.AbstractSession;
import org.eclipse.persistence.mappings.ForeignReferenceMapping;


/**
 * 

* Purpose: Define a class through which Cache access can be * intercepted. *

* Description: EclipseLink makes extensive use of caching * functionality. This class provides the mechanism for intercepting the cache * access. Once intercepted applications can allow the process to continue by * calling the method on the class attribute targetIdentityMap as described in * the API javadocs or redirect the process entirely. * As with IdentityMaps an * entire class inheritance hierarchy will share the same interceptor. *

* To implement a CacheInterceptor users should subclass this class implementing * those methods they wish to intercept. *

* Configuration: Interceptors may be added to a session by using a * Session or Descriptor customizer to set the class name of the interceptor on * the target ClassDescriptor through the method * setCacheInterceptorClass(Class). */ public abstract class CacheInterceptor implements IdentityMap { /** * This attribute stores the actual IdentityMap as configured by the user. */ protected IdentityMap targetIdentityMap; protected AbstractSession interceptedSession; public CacheInterceptor(IdentityMap targetIdentityMap, AbstractSession interceptedSession){ this.targetIdentityMap = targetIdentityMap; this.interceptedSession = interceptedSession; } /** * 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){ return createCacheKeyInterceptor(this.targetIdentityMap.acquireDeferredLock(primaryKey, isCacheCheckComplete)); } /** * 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){ return createCacheKeyInterceptor(this.targetIdentityMap.acquireLock(primaryKey, forMerge, isCacheCheckComplete)); } /** * 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 cacheKeyToBeWrapped = this.targetIdentityMap.acquireLockNoWait(primaryKey, forMerge); if (cacheKeyToBeWrapped != null){ return createCacheKeyInterceptor(cacheKeyToBeWrapped); } return null; } /** * 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 cacheKeyToBeWrapped = this.targetIdentityMap.acquireLockWithWait(primaryKey, forMerge, wait); if (cacheKeyToBeWrapped != null) { return createCacheKeyInterceptor(cacheKeyToBeWrapped); } return null; } /** * 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) { return createCacheKeyInterceptor(this.targetIdentityMap.acquireReadLockOnCacheKey(primaryKey)); } /** * 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 cacheKeyToBeWrapped = this.targetIdentityMap.acquireReadLockOnCacheKeyNoWait(primaryKey); if (cacheKeyToBeWrapped != null){ return createCacheKeyInterceptor(cacheKeyToBeWrapped); } return null; } /** * Add all locked CacheKeys to the map grouped by thread. * Used to print all the locks in the identity map. */ public void collectLocks(HashMap threadList) { this.targetIdentityMap.collectLocks(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 abstract Object clone(); /** * 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 this.targetIdentityMap.containsKey(primaryKey); } protected abstract CacheKeyInterceptor createCacheKeyInterceptor(CacheKey wrappedCacheKey); /** * Allow for the cache to be iterated on. This method is only used during debugging when * validateCache() has been called to print out the contents of the cache. */ public Enumeration elements() { return this.targetIdentityMap.elements(); } /** * Return the object cached in the identity map or null if it could not be found. * User API. */ public Object get(Object primaryKey){ return this.targetIdentityMap.get(primaryKey); } /** * 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 */ public abstract Map getAllFromIdentityMapWithEntityPK(Object[] pkList, ClassDescriptor descriptor, AbstractSession session); /** * 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 */ public abstract Map getAllCacheKeysFromIdentityMapWithEntityPK(Object[] pkList, ClassDescriptor descriptor, AbstractSession session); /** * Get the cache key (with object) for the primary key. */ public CacheKey getCacheKey(Object primaryKey, boolean forMerge) { return this.targetIdentityMap.getCacheKey(primaryKey, forMerge); } /** * Get the cache key (with object) for the primary key. */ public CacheKey getCacheKeyForLock(Object primaryKey) { return this.targetIdentityMap.getCacheKeyForLock(primaryKey); } /** * Return the descriptor that this is the map for. */ public ClassDescriptor getDescriptor() { return this.targetIdentityMap.getDescriptor(); } /** * Return the class that this is the map for. */ public Class getDescriptorClass() { return this.targetIdentityMap.getDescriptorClass(); } /** * @return The maxSize for the IdentityMap (NOTE: some subclasses may use this differently). */ public int getMaxSize() { return this.targetIdentityMap.getMaxSize(); } /** * Return the number of CacheKeys in the IdentityMap. * This may contain weak referenced objects that have been garbage collected. */ public int getSize() { return this.targetIdentityMap.getSize(); } /** * Return the number of actual objects of type myClass in the IdentityMap. * Recurse = true will include subclasses of myClass in the count. */ public int getSize(Class myClass, boolean recurse) { return this.targetIdentityMap.getSize(myClass, recurse); } /** * Return the instance of the IdentityMap that this intercpetor is wrapping. Any call that * is to be passed on to the underlying EclipseLink cache should be passed to this IdentityMap. */ public IdentityMap getTargetIdenttyMap() { return this.targetIdentityMap; } /** * Get the wrapper object from the cache key associated with the given primary key, * this is used for EJB2. */ public Object getWrapper(Object primaryKey) { return this.targetIdentityMap.getWrapper(primaryKey); } /** * Get the write lock value from the cache key associated to the primarykey. * User API. */ public Object getWriteLockValue(Object primaryKey) { return this.targetIdentityMap.getWriteLockValue(primaryKey); } /** * Allow for the {@link CacheKey} elements to be iterated. * {@link CacheKey} {@link Collection} is reused so this iteration may not be thread safe. * * @return {@link Enumeration} of {@link CacheKey} instances. */ public Enumeration keys() { return this.targetIdentityMap.keys(); } /** * Allow for the {@link CacheKey} elements to be iterated. * Clone of {@link CacheKey} {@link Collection} is returned so this iteration should * be thread safe. * * @return {@link Enumeration} with clone of the {@link CacheKey} {@link Collection} */ public Enumeration cloneKeys() { return this.targetIdentityMap.cloneKeys(); } /** * Allow for the {@link CacheKey} elements to be iterated. * {@link CacheKey} {@link Collection} is reused so this iteration may not be thread safe. * * @param checkReadLocks value of {@code true} if read lock on the {@link CacheKey} * instances should be checked or {@code false} otherwise * @return {@link Enumeration} of {@link CacheKey} instances. */ public Enumeration keys(boolean checkReadLocks) { return this.targetIdentityMap.keys(checkReadLocks); } /** * Notify the cache that a lazy relationship has been triggered in the object * and the cache may need to be updated */ public void lazyRelationshipLoaded(Object rootEntity, ValueHolderInterface valueHolder, ForeignReferenceMapping mapping){ this.targetIdentityMap.lazyRelationshipLoaded(rootEntity, valueHolder, mapping); } /** * 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 CacheKey put(Object primaryKey, Object object, Object writeLockValue, long readTime) { return this.targetIdentityMap.put(primaryKey, object, writeLockValue, readTime); } /** * 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) { return this.targetIdentityMap.remove(primaryKey, object); } /** * Remove the CacheKey from the map. */ public Object remove(CacheKey cacheKey) { if (cacheKey.isWrapper()){ return this.targetIdentityMap.remove(cacheKey.getWrappedCacheKey()); } return this.targetIdentityMap.remove(cacheKey); } /** * 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){ this.targetIdentityMap.updateMaxSize(maxSize); } /** * Set the descriptor that this is the map for. */ public void setDescriptor(ClassDescriptor descriptor) { this.targetIdentityMap.setDescriptor(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) { this.targetIdentityMap.setWrapper(primaryKey, 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) { this.targetIdentityMap.setWriteLockValue(primaryKey, writeLockValue); } public String toString() { return Helper.getShortClassName("Intercepted " + this.targetIdentityMap.getClass()) + "[" + getSize() + "]"; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy