org.glassfish.hk2.utilities.general.internal.WeakHashLRUImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ehcache Show documentation
Show all versions of ehcache Show documentation
Ehcache is an open source, standards-based cache used to boost performance,
offload the database and simplify scalability. Ehcache is robust, proven and full-featured and
this has made it the most widely-used Java-based cache.
The newest version!
/*
* Copyright (c) 2015, 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.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the
* Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
* version 2 with the GNU Classpath Exception, which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
*/
package org.glassfish.hk2.utilities.general.internal;
import java.lang.ref.ReferenceQueue;
import java.util.LinkedList;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
import org.glassfish.hk2.utilities.cache.CacheKeyFilter;
import org.glassfish.hk2.utilities.general.WeakHashLRU;
/**
* An implementation of the WeakHashLRU as needed by the CAR algorithm
*
* @author jwells
*
*/
public class WeakHashLRUImpl implements WeakHashLRU {
private final static Object VALUE = new Object();
private final boolean isWeak;
private final WeakHashMap> byKey;
private final ConcurrentHashMap> byKeyNotWeak;
private final ReferenceQueue myQueue = new ReferenceQueue();
private DoubleNode mru;
private DoubleNode lru;
public WeakHashLRUImpl(boolean isWeak) {
this.isWeak = isWeak;
if (isWeak) {
byKey = new WeakHashMap>();
byKeyNotWeak = null;
}
else {
byKey = null;
byKeyNotWeak = new ConcurrentHashMap>();
}
}
private DoubleNode addToHead(K key) {
DoubleNode added = new DoubleNode(key, VALUE, myQueue);
if (mru == null) {
mru = added;
lru = added;
return added;
}
added.setNext(mru);
mru.setPrevious(added);
mru = added;
return added;
}
private K remove(DoubleNode removeMe) {
K retVal = removeMe.getWeakKey().get();
if (removeMe.getNext() != null) {
removeMe.getNext().setPrevious(removeMe.getPrevious());
}
if (removeMe.getPrevious() != null) {
removeMe.getPrevious().setNext(removeMe.getNext());
}
if (removeMe == mru) {
mru = removeMe.getNext();
}
if (removeMe == lru) {
lru = removeMe.getPrevious();
}
removeMe.setNext(null);
removeMe.setPrevious(null);
return retVal;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#add(java.lang.Object)
*/
@Override
public synchronized void add(K key) {
if (key == null) {
throw new IllegalArgumentException("key may not be null");
}
DoubleNode existing;
if (isWeak) {
clearStale();
existing = byKey.get(key);
}
else {
existing = byKeyNotWeak.get(key);
}
if (existing != null) {
remove(existing);
}
DoubleNode added = addToHead(key);
if (isWeak) {
byKey.put(key, added);
}
else {
byKeyNotWeak.put(key, added);
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#contains(java.lang.Object)
*/
@Override
public boolean contains(K key) {
if (isWeak) {
synchronized (this) {
clearStale();
return byKey.containsKey(key);
}
}
return byKeyNotWeak.containsKey(key);
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#remove(java.lang.Object)
*/
@Override
public synchronized boolean remove(K key) {
if (isWeak) {
clearStale();
}
return removeNoClear(key);
}
private boolean removeNoClear(K key) {
if (key == null) return false;
DoubleNode removeMe;
if (isWeak) {
removeMe = byKey.remove(key);
}
else {
removeMe = byKeyNotWeak.remove(key);
}
if (removeMe == null) return false;
remove(removeMe);
return true;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#size()
*/
@Override
public int size() {
if (isWeak) {
synchronized (this) {
clearStale();
return byKey.size();
}
}
return byKeyNotWeak.size();
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#remove()
*/
@Override
public synchronized K remove() {
try {
if (lru == null) return null;
DoubleNode current = lru;
while (current != null) {
DoubleNode previous = current.getPrevious();
K retVal = current.getWeakKey().get();
if (retVal != null) {
removeNoClear(retVal);
return retVal;
}
else {
remove(current);
}
current = previous;
}
return null;
}
finally {
clearStale();
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#releaseMatching(org.glassfish.hk2.utilities.cache.CacheKeyFilter)
*/
@Override
public synchronized void releaseMatching(CacheKeyFilter filter) {
if (filter == null) return;
if (isWeak) {
clearStale();
}
LinkedList removeMe = new LinkedList();
DoubleNode current = mru;
while (current != null) {
K key = current.getWeakKey().get();
if (key != null && filter.matches(key)) {
removeMe.add(key);
}
current = current.getNext();
}
for (K removeKey : removeMe) {
removeNoClear(removeKey);
}
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#clear()
*/
@Override
public synchronized void clear() {
if (isWeak) {
clearStale();
byKey.clear();
}
else {
byKeyNotWeak.clear();
}
mru = null;
lru = null;
}
/* (non-Javadoc)
* @see org.glassfish.hk2.utilities.general.WeakHashLRU#clearStaleReferences()
*/
@Override
public synchronized void clearStaleReferences() {
clearStale();
}
private void clearStale() {
boolean goOn = false;
while (myQueue.poll() != null) {
goOn = true;
}
if (!goOn) return;
DoubleNode current;
current = mru;
while (current != null) {
DoubleNode next = current.getNext();
if (current.getWeakKey().get() == null) {
remove(current);
}
current = next;
}
}
@Override
public synchronized String toString() {
StringBuffer sb = new StringBuffer("WeakHashLRUImpl({");
boolean first = true;
DoubleNode current = mru;
while (current != null) {
K key = current.getWeakKey().get();
String keyString = (key == null) ? "null" : key.toString();
if (first) {
first = false;
sb.append(keyString);
}
else {
sb.append("," + keyString);
}
current = current.getNext();
}
sb.append("}," + System.identityHashCode(this) + ")");
return sb.toString();
}
}