blob: e7a5e0de23253c6f1f7e7742b7e8b1a07a000b76 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 1998, 2013 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 v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* 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;
/**
* <p><b>Purpose</b>: A fixed size LRU cache<p>
* 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).
* <p><b>Responsibilities</b>:<ul>
* <li> Guarantees identity through primary key values
* <li> Keeps the LRU linked list updated.
* </ul>
* @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).
* @return the added LinkedCacheKey
*/
protected LinkedCacheKey insertLink(LinkedCacheKey key) {
if (key == null){
return key;
}
// No sence on locking the entire cache, just lock on the list.
synchronized (this.first){
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.
*/
protected CacheKey putCacheKeyIfAbsent(CacheKey searchKey) {
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.
*/
public Object remove(CacheKey key) {
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.
* @return the removed LinkedCacheKey.
*/
protected LinkedCacheKey removeLink(LinkedCacheKey key) {
if (key == null){
return key;
}
synchronized (this.first) {
if (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.
*/
public synchronized void updateMaxSize(int maxSize) {
setMaxSize(maxSize);
ensureFixedSize();
}
}