/**
* Copyright 2013 Sven Diedrichsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package de.jollyday.util;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* Cache implementation which handles concurrent access to cached values.
*
* @param <VALUE>
* the type of cached values
*/
public class Cache<VALUE> {
/**
* Map for caching
*/
private final Map<String, VALUE> cachingMap = new HashMap<>();
/**
* Lock for accessing the map
*/
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
/**
* Returns the value defined by the {@link ValueHandler}
*
* @param valueHandler
* which creates the key and the value if necessary
* @return the eventually cached value
*/
public VALUE get(ValueHandler<VALUE> valueHandler) {
String key = valueHandler.getKey();
try {
readLock();
if (!containsKey(key)) {
readUnlockWriteLock();
if (!containsKey(key)) {
putValue(key, valueHandler.createValue());
}
readLockWriteUnlock();
}
return getValue(key);
} finally {
unlockBoth();
}
}
private void unlockBoth() {
writeUnlock();
readUnlock();
}
private void readLockWriteUnlock() {
readLock();
writeUnlock();
}
private void readUnlockWriteLock() {
readUnlock();
writeLock();
}
private VALUE getValue(String key) {
return cachingMap.get(key);
}
private void putValue(String key, VALUE value) {
cachingMap.put(key, value);
}
private boolean containsKey(String key) {
return cachingMap.containsKey(key);
}
private void writeUnlock() {
if (lock.isWriteLockedByCurrentThread()) {
lock.writeLock().unlock();
}
}
private void writeLock() {
lock.writeLock().lock();
}
private void readLock() {
lock.readLock().lock();
}
private void readUnlock() {
if(lock.getReadHoldCount() > 0){
lock.readLock().unlock();
}
}
/**
* Clears the cache.
*/
public void clear() {
writeLock();
cachingMap.clear();
writeUnlock();
}
public interface ValueHandler<VALUE> {
String getKey();
VALUE createValue();
}
}