org.eclipse.persistence.internal.identitymaps.CacheIdentityMap Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2020 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 org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.sessions.AbstractSession;
/**
* Purpose: A fixed size LRU cache
* Using a linked list as well as the map from the superclass a LRU cache is maintained.
* When a get is executed the LRU list is updated and when a new object is inserted the object
* at the start of the list is deleted (provided the maxSize has been reached).
*
Responsibilities:
* - Guarantees identity through primary key values
*
- Keeps the LRU linked list updated.
*
* @since TOPLink/Java 1.0
*/
public class CacheIdentityMap extends FullIdentityMap {
/** Provide handles on the linked list */
protected LinkedCacheKey first;
/** Provide handles on the linked list */
protected LinkedCacheKey last;
public CacheIdentityMap(int size, ClassDescriptor descriptor, AbstractSession session, boolean isolated) {
super(size, descriptor, session, isolated);
this.first = new LinkedCacheKey(CacheId.EMPTY, null, null, 0, isIsolated);
this.last = new LinkedCacheKey(CacheId.EMPTY, null, null, 0, isIsolated);
this.first.setNext(this.last);
this.last.setPrevious(this.first);
}
@Override
public CacheKey createCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime) {
return new LinkedCacheKey(primaryKey, object, writeLockValue, readTime, isIsolated);
}
/**
* Reduces the size of the receiver down to the maxSize removing objects from the
* start of the linked list.
*/
protected void ensureFixedSize() {
// protect the case where someone attempts to break the cache by
// setting max size to 0.
synchronized(this.first) {
while (getMaxSize() > 0 && getSize() > getMaxSize()) {
remove(last.getPrevious());
}
}
}
/**
* Access the object within the table for the given primaryKey.
* Move the accessed key to the top of the order keys linked list to maintain LRU.
* @param primaryKeys is the primary key for the object to search for.
* @return the LinkedCacheKey or null if none found for primaryKey
*/
@Override
public CacheKey getCacheKey(Object primaryKeys, boolean forMerge) {
LinkedCacheKey cacheKey = (LinkedCacheKey)super.getCacheKey(primaryKeys, forMerge);
if (cacheKey != null) {
synchronized (this.first) {
removeLink(cacheKey);
insertLink(cacheKey);
}
}
return cacheKey;
}
/**
* Insert a new element into the linked list of LinkedCacheKeys.
* New elements (Recently Used) are added at the end (last).
* Callers of this method must synchronize on the start of the list (this.first).
* @return the added LinkedCacheKey
*/
protected LinkedCacheKey insertLink(LinkedCacheKey key) {
if (key == null){
return key;
}
this.first.getNext().setPrevious(key);
key.setNext(this.first.getNext());
key.setPrevious(this.first);
this.first.setNext(key);
return key;
}
/**
* Also insert the link if the cacheKey is put.
*/
@Override
protected CacheKey putCacheKeyIfAbsent(CacheKey searchKey) {
synchronized(this.first) {
CacheKey cacheKey = super.putCacheKeyIfAbsent(searchKey);
if (cacheKey == null) {
insertLink((LinkedCacheKey)searchKey);
ensureFixedSize();
}
return cacheKey;
}
}
/**
* Remove the LinkedCacheKey from the cache as well as from the linked list.
* @return the LinkedCacheKey to be removed.
*/
@Override
public Object remove(CacheKey key) {
synchronized (this.first) {
super.remove(key);
// The key may be null if was missing, just null should be returned in this case.
if (key == null) {
return null;
}
return removeLink((LinkedCacheKey)key).getObject();
}
}
/**
* Remove the LinkedCacheKey from the linked list.
* Callers of this method must synchronize on the start of the list (this.first).
* @return the removed LinkedCacheKey.
*/
protected LinkedCacheKey removeLink(LinkedCacheKey key) {
// callers of this method must be synchronized on this.first
if (key == null || key.getPrevious() == null || key.getNext() == null){
//already removed by a competing thread, just return
return key;
}
key.getPrevious().setNext(key.getNext());
key.getNext().setPrevious(key.getPrevious());
key.setNext(null);
key.setPrevious(null);
return key;
}
/**
* INTERNAL:
* 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.
*/
@Override
public synchronized void updateMaxSize(int maxSize) {
setMaxSize(maxSize);
ensureFixedSize();
}
}