Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
javax.cache.implementation.RICache Maven / Gradle / Ivy
/**
* Copyright 2011 Terracotta, Inc.
* Copyright 2011 Oracle America Incorporated
*
* 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 javax.cache.implementation;
import javax.cache.CacheConfiguration;
import javax.cache.CacheLoader;
import javax.cache.mbeans.CacheMXBean;
import javax.cache.CacheStatistics;
import javax.cache.CacheWriter;
import javax.cache.Status;
import javax.cache.event.CacheEntryListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.ReentrantLock;
/**
* The reference implementation for JSR107.
*
* This is meant to act as a proof of concept for the API. It is not threadsafe or high performance and does limit
* the size of caches or provide eviction. It therefore is not suitable for use in production. Please use a
* production implementation of the API.
*
* This implementation implements all optional parts of JSR107 except for the Transactions chapter. Transactions support
* simply uses the JTA API. The JSR107 specification details how JTA should be applied to caches.
*
* @param the type of keys maintained by this map
* @param the type of mapped values*
* @author Greg Luck
* @author Yannis Cosmadopoulos
* @since 1.0
*/
public final class RICache extends AbstractCache {
private final RISimpleCache store;
private final Set> cacheEntryListeners =
new CopyOnWriteArraySet>();
private volatile Status status;
private final RICacheStatistics statistics;
private final CacheMXBean mBean;
private final LockManager lockManager = new LockManager();
/**
* Constructs a cache.
*
* @param cacheName the cache name
* @param cacheManagerName the cache manager name
* @param classLoader the class loader
* @param configuration the configuration
* @param cacheLoader the cache loader
* @param cacheWriter the cache writer
* @param listeners the cache listeners
*/
private RICache(String cacheName, String cacheManagerName,
ClassLoader classLoader,
CacheConfiguration configuration,
CacheLoader cacheLoader, CacheWriter super K, ? super V> cacheWriter,
Set> listeners) {
super(cacheName, cacheManagerName, classLoader, configuration, cacheLoader, cacheWriter);
status = Status.UNINITIALISED;
store = configuration.isStoreByValue() ?
new RIByValueSimpleCache(new RISerializer(classLoader),
new RISerializer(classLoader)) :
new RIByReferenceSimpleCache();
statistics = new RICacheStatistics(this);
mBean = new DelegatingCacheMXBean(this);
for (CacheEntryListener listener : listeners) {
registerCacheEntryListener(listener);
}
}
/**
* {@inheritDoc}
*/
@Override
public V get(K key) {
checkStatusStarted();
if (key == null) {
throw new NullPointerException();
}
return getInternal(key);
}
/**
* {@inheritDoc}
*/
@Override
public Map getAll(Set extends K> keys) {
checkStatusStarted();
if (keys.contains(null)) {
throw new NullPointerException("key");
}
// will throw NPE if keys=null
HashMap map = new HashMap(keys.size());
for (K key : keys) {
V value = getInternal(key);
if (value != null) {
map.put(key, value);
}
}
return map;
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsKey(K key) {
checkStatusStarted();
if (key == null) {
throw new NullPointerException();
}
lockManager.lock(key);
try {
return store.containsKey(key);
} finally {
lockManager.unLock(key);
}
}
/**
* {@inheritDoc}
*/
@Override
public Future load(K key) {
checkStatusStarted();
if (key == null) {
throw new NullPointerException("key");
}
if (getCacheLoader() == null) {
return null;
}
if (containsKey(key)) {
return null;
}
FutureTask task = new FutureTask(new RICacheLoaderLoadCallable(this, getCacheLoader(), key));
submit(task);
return task;
}
/**
* {@inheritDoc}
*/
@Override
@SuppressWarnings("unchecked")
public Future> loadAll(Set extends K> keys) {
checkStatusStarted();
if (keys == null) {
throw new NullPointerException("keys");
}
if (getCacheLoader() == null) {
return null;
}
if (keys.contains(null)) {
throw new NullPointerException("key");
}
Callable> callable = new RICacheLoaderLoadAllCallable(this, getCacheLoader(), keys);
FutureTask> task = new FutureTask>(callable);
submit(task);
return task;
}
/**
* {@inheritDoc}
*/
@Override
public CacheStatistics getStatistics() {
checkStatusStarted();
if (statisticsEnabled()) {
return statistics;
} else {
return null;
}
}
/**
* {@inheritDoc}
*/
@Override
public void put(K key, V value) {
checkStatusStarted();
long start = statisticsEnabled() ? System.nanoTime() : 0;
lockManager.lock(key);
try {
store.put(key, value);
} finally {
lockManager.unLock(key);
}
if (statisticsEnabled()) {
statistics.increaseCachePuts(1);
statistics.addPutTimeNano(System.nanoTime() - start);
}
}
@Override
public V getAndPut(K key, V value) {
checkStatusStarted();
long start = statisticsEnabled() ? System.nanoTime() : 0;
V result;
lockManager.lock(key);
try {
result = store.getAndPut(key, value);
} finally {
lockManager.unLock(key);
}
if (statisticsEnabled()) {
statistics.increaseCachePuts(1);
statistics.addPutTimeNano(System.nanoTime() - start);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void putAll(Map extends K, ? extends V> map) {
checkStatusStarted();
long start = statisticsEnabled() ? System.nanoTime() : 0;
if (map.containsKey(null)) {
throw new NullPointerException("key");
}
//store.putAll(map);
for (Map.Entry extends K, ? extends V> entry : map.entrySet()) {
K key = entry.getKey();
lockManager.lock(key);
try {
store.put(key, entry.getValue());
} finally {
lockManager.unLock(key);
}
}
if (statisticsEnabled()) {
statistics.increaseCachePuts(map.size());
statistics.addPutTimeNano(System.nanoTime() - start);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean putIfAbsent(K key, V value) {
checkStatusStarted();
long start = statisticsEnabled() ? System.nanoTime() : 0;
boolean result;
lockManager.lock(key);
try {
result = store.putIfAbsent(key, value);
} finally {
lockManager.unLock(key);
}
if (result && statisticsEnabled()) {
statistics.increaseCachePuts(1);
statistics.addPutTimeNano(System.nanoTime() - start);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(K key) {
checkStatusStarted();
long start = statisticsEnabled() ? System.nanoTime() : 0;
boolean result;
lockManager.lock(key);
try {
result = store.remove(key);
} finally {
lockManager.unLock(key);
}
if (result && statisticsEnabled()) {
statistics.increaseCacheRemovals(1);
statistics.addRemoveTimeNano(System.nanoTime() - start);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(K key, V oldValue) {
checkStatusStarted();
long start = statisticsEnabled() ? System.nanoTime() : 0;
boolean result;
lockManager.lock(key);
try {
result = store.remove(key, oldValue);
} finally {
lockManager.unLock(key);
}
if (result && statisticsEnabled()) {
statistics.increaseCacheRemovals(1);
statistics.addRemoveTimeNano(System.nanoTime() - start);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public V getAndRemove(K key) {
checkStatusStarted();
V result;
lockManager.lock(key);
try {
result = store.getAndRemove(key);
} finally {
lockManager.unLock(key);
}
if (statisticsEnabled()) {
if (result != null) {
statistics.increaseCacheHits(1);
statistics.increaseCacheRemovals(1);
} else {
statistics.increaseCacheMisses(1);
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean replace(K key, V oldValue, V newValue) {
checkStatusStarted();
boolean result;
lockManager.lock(key);
try {
result = store.replace(key, oldValue, newValue);
} finally {
lockManager.unLock(key);
}
if (result && statisticsEnabled()) {
statistics.increaseCachePuts(1);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public boolean replace(K key, V value) {
checkStatusStarted();
boolean result;
lockManager.lock(key);
try {
result = store.replace(key, value);
} finally {
lockManager.unLock(key);
}
if (result && statisticsEnabled()) {
statistics.increaseCachePuts(1);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public V getAndReplace(K key, V value) {
checkStatusStarted();
V result;
lockManager.lock(key);
try {
result = store.getAndReplace(key, value);
} finally {
lockManager.unLock(key);
}
if (statisticsEnabled()) {
if (result != null) {
statistics.increaseCacheHits(1);
statistics.increaseCachePuts(1);
} else {
statistics.increaseCacheMisses(1);
}
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void removeAll(Set extends K> keys) {
checkStatusStarted();
for (K key : keys) {
lockManager.lock(key);
try {
store.remove(key);
} finally {
lockManager.unLock(key);
}
}
if (statisticsEnabled()) {
statistics.increaseCacheRemovals(keys.size());
}
}
/**
* {@inheritDoc}
*/
@Override
public void removeAll() {
checkStatusStarted();
int size = (statisticsEnabled()) ? store.size() : 0;
//store.removeAll();
Iterator> iterator = store.iterator();
while (iterator.hasNext()) {
Map.Entry entry = iterator.next();
K key = entry.getKey();
lockManager.lock(key);
try {
iterator.remove();
} finally {
lockManager.unLock(key);
}
}
//possible race here but it is only stats
if (statisticsEnabled()) {
statistics.increaseCacheRemovals(size);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean registerCacheEntryListener(CacheEntryListener super K, ? super V> cacheEntryListener) {
return cacheEntryListeners.add(cacheEntryListener);
}
/**
* {@inheritDoc}
*/
@Override
public boolean unregisterCacheEntryListener(CacheEntryListener, ?> cacheEntryListener) {
return cacheEntryListeners.remove(cacheEntryListener);
}
/**
* {@inheritDoc}
*/
@Override
public Object invokeEntryProcessor(K key, EntryProcessor entryProcessor) {
checkStatusStarted();
if (key == null) {
throw new NullPointerException();
}
if (key == entryProcessor) {
throw new NullPointerException();
}
Object result = null;
lockManager.lock(key);
try {
RIMutableEntry entry = new RIMutableEntry(key, store);
result = entryProcessor.process(entry);
entry.commit();
} finally {
lockManager.unLock(key);
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public Iterator> iterator() {
checkStatusStarted();
return new RIEntryIterator(store.iterator(), lockManager);
}
@Override
public CacheMXBean getMBean() {
return mBean;
}
/**
* {@inheritDoc}
*/
@Override
public void start() {
status = Status.STARTED;
}
/**
* {@inheritDoc}
*/
@Override
public void stop() {
super.stop();
store.removeAll();
status = Status.STOPPED;
}
private void checkStatusStarted() {
if (!status.equals(Status.STARTED)) {
throw new IllegalStateException("The cache status is not STARTED");
}
}
/**
* {@inheritDoc}
*/
@Override
public Status getStatus() {
return status;
}
@Override
public T unwrap(java.lang.Class cls) {
if (cls.isAssignableFrom(this.getClass())) {
return cls.cast(this);
}
throw new IllegalArgumentException("Unwrapping to " + cls + " is not a supported by this implementation");
}
private boolean statisticsEnabled() {
return getConfiguration().isStatisticsEnabled();
}
private V getInternal(K key) {
long start = statisticsEnabled() ? System.nanoTime() : 0;
V value = null;
lockManager.lock(key);
try {
value = store.get(key);
} finally {
lockManager.unLock(key);
}
if (statisticsEnabled()) {
statistics.addGetTimeNano(System.nanoTime() - start);
}
if (value == null) {
if (statisticsEnabled()) {
statistics.increaseCacheMisses(1);
}
if (getCacheLoader() != null) {
return getFromLoader(key);
} else {
return null;
}
} else {
if (statisticsEnabled()) {
statistics.increaseCacheHits(1);
}
return value;
}
}
private V getFromLoader(K key) {
Entry entry = getCacheLoader().load(key);
if (entry != null) {
store.put(entry.getKey(), entry.getValue());
return entry.getValue();
} else {
return null;
}
}
/**
* Returns the size of the cache.
*
* @return the size in entries of the cache
*/
long getSize() {
return store.size();
}
/**
* {@inheritDoc}
*
* @author Yannis Cosmadopoulos
*/
private static class RIEntry implements Entry {
private final K key;
private final V value;
public RIEntry(K key, V value) {
if (key == null) {
throw new NullPointerException("key");
}
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RIEntry, ?> e2 = (RIEntry, ?>) o;
return this.getKey().equals(e2.getKey()) &&
this.getValue().equals(e2.getValue());
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return getKey().hashCode() ^ getValue().hashCode();
}
}
/**
* {@inheritDoc}
* TODO: not obvious how iterator should behave with locking.
* TODO: in the impl below, by the time we get the lock, value may be stale
*
* @author Yannis Cosmadopoulos
*/
private static final class RIEntryIterator implements Iterator> {
private final Iterator> mapIterator;
private final LockManager lockManager;
private RIEntryIterator(Iterator> mapIterator, LockManager lockManager) {
this.mapIterator = mapIterator;
this.lockManager = lockManager;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasNext() {
return mapIterator.hasNext();
}
/**
* {@inheritDoc}
*/
@Override
public Entry next() {
Map.Entry mapEntry = mapIterator.next();
K key = mapEntry.getKey();
lockManager.lock(key);
try {
return new RIEntry(key, mapEntry.getValue());
} finally {
lockManager.unLock(key);
}
}
/**
* {@inheritDoc}
*/
@Override
public void remove() {
mapIterator.remove();
}
}
/**
* Callable used for cache loader.
*
* @param the type of the key
* @param the type of the value
* @author Yannis Cosmadopoulos
*/
private static class RICacheLoaderLoadCallable implements Callable {
private final RICache cache;
private final CacheLoader cacheLoader;
private final K key;
RICacheLoaderLoadCallable(RICache cache, CacheLoader cacheLoader, K key) {
this.cache = cache;
this.cacheLoader = cacheLoader;
this.key = key;
}
@Override
public V call() throws Exception {
Entry entry = cacheLoader.load(key);
cache.put(entry.getKey(), entry.getValue());
return entry.getValue();
}
}
/**
* Callable used for cache loader.
*
* @param the type of the key
* @param the type of the value
* @author Yannis Cosmadopoulos
*/
private static class RICacheLoaderLoadAllCallable implements Callable> {
private final RICache cache;
private final CacheLoader cacheLoader;
private final Collection extends K> keys;
RICacheLoaderLoadAllCallable(RICache cache, CacheLoader cacheLoader, Collection extends K> keys) {
this.cache = cache;
this.cacheLoader = cacheLoader;
this.keys = keys;
}
@Override
public Map call() throws Exception {
ArrayList keysNotInStore = new ArrayList();
for (K key : keys) {
if (!cache.containsKey(key)) {
keysNotInStore.add(key);
}
}
Map value = cacheLoader.loadAll(keysNotInStore);
cache.putAll(value);
return value;
}
}
/**
* A Builder for RICache.
*
* @param
* @param
* @author Yannis Cosmadopoulos
*/
public static class Builder extends AbstractCache.Builder {
private final Set> listeners = new CopyOnWriteArraySet>();
/**
* Construct a builder.
*
* @param cacheName the name of the cache to be built
* @param cacheManagerName the name of the cache manager
* @param classLoader the class loader
*/
public Builder(String cacheName, String cacheManagerName, ClassLoader classLoader) {
this(cacheName, cacheManagerName, classLoader, new RICacheConfiguration.Builder());
}
private Builder(String cacheName, String cacheManagerName, ClassLoader classLoader,
RICacheConfiguration.Builder configurationBuilder) {
super(cacheName, cacheManagerName, classLoader, configurationBuilder);
}
/**
* Builds the cache
*
* @return a constructed cache.
*/
@Override
public RICache build() {
CacheConfiguration configuration = createCacheConfiguration();
RICache riCache = new RICache(cacheName, cacheManagerName,
classLoader, configuration,
cacheLoader, cacheWriter, listeners);
((RICacheConfiguration) configuration).setRiCache(riCache);
return riCache;
}
@Override
public Builder registerCacheEntryListener(CacheEntryListener listener) {
listeners.add(listener);
return this;
}
}
/**
* Simple lock management
* @param the type of the object to be locked
* @author Yannis Cosmadopoulos
* @since 1.0
*/
private static final class LockManager {
private final ConcurrentHashMap locks = new ConcurrentHashMap();
private final LockFactory lockFactory = new LockFactory();
private LockManager() {
}
/**
* Lock the object
* @param key the key
*/
private void lock(K key) {
ReentrantLock lock = lockFactory.getLock();
while (true) {
ReentrantLock oldLock = locks.putIfAbsent(key, lock);
if (oldLock == null) {
return;
}
// there was a lock
oldLock.lock();
// now we have it. Because of possibility that someone had it for remove,
// we don't re-use directly
lockFactory.release(oldLock);
}
}
/**
* Unlock the object
* @param key the object
*/
private void unLock(K key) {
ReentrantLock lock = locks.remove(key);
lockFactory.release(lock);
}
/**
* Factory/pool
* @author Yannis Cosmadopoulos
* @since 1.0
*/
private static final class LockFactory {
private static final int CAPACITY = 100;
private static final ArrayList LOCKS = new ArrayList(CAPACITY);
private LockFactory() {
}
private ReentrantLock getLock() {
ReentrantLock qLock = null;
synchronized (LOCKS) {
if (!LOCKS.isEmpty()) {
qLock = LOCKS.remove(0);
}
}
ReentrantLock lock = qLock != null ? qLock : new ReentrantLock();
lock.lock();
return lock;
}
private void release(ReentrantLock lock) {
lock.unlock();
synchronized (LOCKS) {
if (LOCKS.size() <= CAPACITY) {
LOCKS.add(lock);
}
}
}
}
}
/**
* A mutable entry
* @param
* @param
* @author Yannis Cosmadopoulos
* @since 1.0
*/
private static class RIMutableEntry implements MutableEntry {
private final K key;
private V value;
private final RISimpleCache store;
private boolean exists;
private boolean remove;
RIMutableEntry(K key, RISimpleCache store) {
this.key = key;
this.store = store;
exists = store.containsKey(key);
}
private void commit() {
if (remove) {
store.remove(key);
} else if (value != null) {
store.put(key, value);
}
}
@Override
public boolean exists() {
return exists;
}
@Override
public void remove() {
remove = true;
exists = false;
value = null;
}
@Override
public void setValue(V value) {
if (value == null) {
throw new NullPointerException();
}
exists = true;
remove = false;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value != null ? value : store.get(key);
}
}
}