blob: 56303ea4d5bb3dcfab8a06c0bd9cf0f3baf41bee [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.helper.linkedlist.*;
import org.eclipse.persistence.internal.sessions.AbstractSession;
/**
* <p><b>Purpose</b>: A HardCacheWeakIdentityMap is identical to the weak identity map, however the weak reference
* can be a performance problem for some types of apps because it can cause too much garbage collection
* of objects read causing them to be re-read and re-built (this defeats the purpose of the cache).
* The hard weak cache solves this by also holding a fixed number of objects in memory to improve caching.<br>
* This class makes use of an exposed node linked list to maintain the objects by storing the link nodes in the cache key.
* <p><b>Responsibilities</b>:<ul>
* <li> Guarantees identity
* <li> Allows garbage collection
* <li> Increases performance by maintaining a fixed size cache of LRU objects when memory is available
* <li> The default size of the reference cache is half the max size
* </ul>
* @since TOPLink/Java 1.2
*/
public class HardCacheWeakIdentityMap extends WeakIdentityMap {
/** A subset of cache entries have hard references maintained in this list to reduce garbage collection frequency */
protected ExposedNodeLinkedList referenceCache;
public HardCacheWeakIdentityMap(int size, ClassDescriptor descriptor, AbstractSession session, boolean isIsolated) {
super(size, descriptor, session, isIsolated);
this.referenceCache = new ExposedNodeLinkedList();
}
/**
* Use a ReferenceCacheKey that also stores the linked list node to manage
* the LRU sub-cache of references.
*/
@Override
public CacheKey createCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime) {
return new ReferenceCacheKey(primaryKey, object, writeLockValue, readTime, isIsolated);
}
/**
* Return the linked reference cache.
*/
public ExposedNodeLinkedList getReferenceCache() {
return referenceCache;
}
/**
* Allows subclass to create a SoftReference to the object.
* @param object is the domain object to cache.
*/
public Object buildReference(Object object) {
return object;
}
/**
* Checks if the object is null, or reference's object is null.
* @param the object for hard or the reference for soft.
*/
public boolean hasReference(Object reference) {
return reference != null;
}
/**
* Remove the cache key from the map and the sub-cache list.
*/
public Object remove(CacheKey cacheKey) {
if (cacheKey == null) {
return null;
}
LinkedNode node = ((ReferenceCacheKey)cacheKey).getReferenceCacheNode();
// Node is initially null while object is being built.
if (node != null) {
synchronized (this.referenceCache) {
this.referenceCache.remove(node);
}
}
return super.remove(cacheKey);
}
/**
* Store the object in the cache at its primary key, and add to sub-cache list.
*/
@Override
public CacheKey put(Object primaryKey, Object object, Object writeLockValue, long readTime) {
CacheKey cacheKey = super.put(primaryKey, object, writeLockValue, readTime);
cacheKey.updateAccess();
return cacheKey;
}
/**
* This method will be used to update the max cache size.
*/
public synchronized void updateMaxSize(int maxSize) {
setMaxSize(maxSize);
synchronized (this.referenceCache) {
// Remove the LRU items if max size exceeded.
while (this.referenceCache.size() > this.maxSize) {
this.referenceCache.removeLast();
}
}
}
/**
* Inner class to define the specialized weak cache key.
* Keeps track of the linked list node to allow quick repositioning.
*/
public class ReferenceCacheKey extends WeakCacheKey {
protected LinkedNode referenceNode;
public ReferenceCacheKey(Object primaryKey, Object object, Object writeLockValue, long readTime, boolean isIsolated) {
super(primaryKey, object, writeLockValue, readTime, isIsolated);
}
public LinkedNode getReferenceCacheNode() {
return referenceNode;
}
public void setReferenceCacheNode(LinkedNode referenceNode) {
this.referenceNode = referenceNode;
}
public ExposedNodeLinkedList getReferenceCache() {
return referenceCache;
}
/**
* Notifies that cache key that it has been accessed.
* Allows the LRU sub-cache to be maintained,
* the cache node must be moved to the front of the list.
*/
public void updateAccess() {
// Check if the node's contents is null (was removed),
// or ref value may have garbage collected so reset it.
if ((this.referenceNode != null) && (!hasReference(this.referenceNode.getContents()))) {
this.referenceNode.setContents(buildReference(getObject()));
}
// PERF: Synchronize on the linked list.
synchronized (referenceCache) {
// If reference node is null, add to start (new cache key).
if (this.referenceNode == null) {
this.referenceNode = referenceCache.addFirst(buildReference(getObject()));
} else {
// This is a fast constant time operations because of the linked list usage.
referenceCache.moveFirst(getReferenceCacheNode());
}
// Remove the old LRU items if max size exceeded (if was removed).
while (referenceCache.size() > maxSize) {
referenceCache.removeLast();
}
}
}
}
}