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.
net.sf.ehcache.store.FrontEndCacheTier Maven / Gradle / Ivy
/**
* Copyright Terracotta, Inc.
*
* 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 net.sf.ehcache.store;
import java.io.IOException;
import java.io.Serializable;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import net.sf.ehcache.concurrent.LockType;
import net.sf.ehcache.concurrent.ReadWriteLockSync;
import net.sf.ehcache.concurrent.StripedReadWriteLock;
import net.sf.ehcache.concurrent.StripedReadWriteLockSync;
import net.sf.ehcache.search.impl.SearchManager;
import net.sf.ehcache.store.compound.ReadWriteCopyStrategy;
import net.sf.ehcache.util.SetAsList;
import net.sf.ehcache.writer.CacheWriterManager;
/**
* Abstract class for stores which combine two other stores, one caching the other (aka authority)'s elements.
*
* @param the cache tier store type
* @param the authority tier store type
* @author Chris Dennis
* @author Ludovic Orban
*/
public abstract class FrontEndCacheTier extends AbstractStore {
private static final int DEFAULT_LOCK_STRIPE_COUNT = 128;
/**
* The cache tier store
*/
protected final T cache;
/**
* The authority tier store
*/
protected final U authority;
private final StripedReadWriteLock masterLocks;
private final boolean copyOnRead;
private final boolean copyOnWrite;
private final ReadWriteCopyStrategy copyStrategy;
private final ConcurrentMap faults = new ConcurrentHashMap();
/**
* Constructor for FrontEndCacheTier
*
* @param cache the caching tier
* @param authority the authority tier
* @param copyStrategy the copyStrategy to use
* @param searchManager the search manager to use
* @param copyOnWrite whether to copy on writes, false otherwise
* @param copyOnRead whether to copy on reads, false otherwise
*/
public FrontEndCacheTier(T cache, U authority, ReadWriteCopyStrategy copyStrategy, SearchManager searchManager,
boolean copyOnWrite, boolean copyOnRead) {
super(searchManager);
this.cache = cache;
this.authority = authority;
this.copyStrategy = copyStrategy;
this.copyOnWrite = copyOnWrite;
this.copyOnRead = copyOnRead;
if (authority instanceof StripedReadWriteLockProvider) {
masterLocks = ((StripedReadWriteLockProvider) authority).createStripedReadWriteLock();
} else {
masterLocks = new StripedReadWriteLockSync(DEFAULT_LOCK_STRIPE_COUNT);
}
}
/**
* {@inheritDoc}
*/
public void unpinAll() {
writeLock();
try {
// unpin in both authority first then cache later
authority.unpinAll();
cache.unpinAll();
} finally {
writeUnlock();
}
}
/**
* {@inheritDoc}
*/
public boolean isPinned(Object key) {
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
// checking in authority first then cache later
return authority.isPinned(key) || cache.isPinned(key);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public void setPinned(Object key, boolean pinned) {
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
// setting pinning in both authority first then cache later
authority.setPinned(key, pinned);
cache.setPinned(key, pinned);
} finally {
lock.unlock();
}
}
/**
* Perform copy on read on an element if configured
*
* @param element the element to copy for read
* @return a copy of the element with the reconstructed original value
*/
protected Element copyElementForReadIfNeeded(Element element) {
if (copyOnRead && copyOnWrite) {
return copyStrategy.copyForRead(element);
} else if (copyOnRead) {
return copyStrategy.copyForRead(copyStrategy.copyForWrite(element));
} else {
return element;
}
}
/**
* Perform copy on write on an element if configured
*
* @param element the element to copy for write
* @return a copy of the element with a storage-ready value
*/
protected Element copyElementForWriteIfNeeded(Element element) {
if (copyOnRead && copyOnWrite) {
return copyStrategy.copyForWrite(element);
} else if (copyOnWrite) {
return copyStrategy.copyForRead(copyStrategy.copyForWrite(element));
} else {
return element;
}
}
/**
* Perform copy for the element If both copy on read and copy on write are set to true
*
* @param element the element to copy for removal
* @return a copy of the element with a storage-ready value
*/
private Element copyElementForRemovalIfNeeded(Element element) {
if (copyOnRead && copyOnWrite) {
return copyStrategy.copyForWrite(element);
} else {
return element;
}
}
/**
* {@inheritDoc}
*/
public Element get(Object key) {
if (key == null) {
return null;
}
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
Element e = cache.get(key);
if (e == null) {
Fault fault = faults.putIfAbsent(key, new Fault());
if (fault == null) {
try {
e = cache.get(key);
if (e != null) {
return copyElementForReadIfNeeded(e);
}
e = authority.get(key);
if (e != null) {
cache.put(e);
}
return copyElementForReadIfNeeded(e);
} finally {
faults.remove(key).complete(e);
}
} else {
return copyElementForReadIfNeeded(fault.get());
}
} else {
return copyElementForReadIfNeeded(e);
}
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public Element getQuiet(Object key) {
if (key == null) {
return null;
}
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
Element e = cache.getQuiet(key);
if (e == null) {
Fault fault = faults.putIfAbsent(key, new Fault());
if (fault == null) {
try {
e = authority.getQuiet(key);
if (e != null) {
cache.put(e);
}
} finally {
faults.remove(key).complete(e);
}
} else {
e = fault.get();
}
}
return copyElementForReadIfNeeded(e);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean put(Element e) {
if (e == null) {
return true;
}
Object key = e.getObjectKey();
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
Element copy = copyElementForWriteIfNeeded(e);
final boolean put = authority.put(copy);
try {
cache.fill(copy);
} catch (OutOfMemoryError oome) {
authority.remove(e.getKey());
throw oome;
}
return put;
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean putWithWriter(Element e, CacheWriterManager writer) {
if (e == null) {
return true;
}
Object key = e.getObjectKey();
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
Element copy = copyElementForWriteIfNeeded(e);
final boolean put = authority.putWithWriter(copy, writer);
try {
cache.fill(copy);
} catch (OutOfMemoryError oome) {
authority.remove(e.getKey());
throw oome;
}
return put;
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public Element remove(Object key) {
if (key == null) {
return null;
}
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
Element e = cache.remove(key);
if (e == null) {
return copyElementForReadIfNeeded(authority.remove(key));
} else {
authority.removeNoReturn(key);
return copyElementForReadIfNeeded(e);
}
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public Element removeWithWriter(Object key, CacheWriterManager writerManager) throws CacheException {
if (key == null) {
return null;
}
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
cache.remove(key);
return copyElementForReadIfNeeded(authority.removeWithWriter(key, writerManager));
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public Element putIfAbsent(Element e) throws NullPointerException {
Object key = e.getObjectKey();
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
Element copy = copyElementForWriteIfNeeded(e);
Element old = authority.putIfAbsent(copy);
if (old == null) {
try {
cache.fill(copy);
} catch (OutOfMemoryError oome) {
authority.remove(copy.getKey());
throw oome;
}
}
return copyElementForReadIfNeeded(old);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public Element removeElement(Element e, ElementValueComparator comparator) throws NullPointerException {
Object key = e.getObjectKey();
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
cache.remove(e.getObjectKey());
return copyElementForReadIfNeeded(authority.removeElement(copyElementForRemovalIfNeeded(e), comparator));
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean replace(Element old, Element e, ElementValueComparator comparator) throws NullPointerException, IllegalArgumentException {
Object key = old.getObjectKey();
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
Element copy = copyElementForWriteIfNeeded(e);
cache.remove(old.getObjectKey());
return authority.replace(copyElementForRemovalIfNeeded(old), copy, comparator);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public Element replace(Element e) throws NullPointerException {
Object key = e.getObjectKey();
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
Element copy = copyElementForWriteIfNeeded(e);
cache.remove(e.getObjectKey());
return copyElementForReadIfNeeded(authority.replace(copy));
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean containsKey(Object key) {
if (key == null) {
return false;
}
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
return cache.containsKey(key) || authority.containsKey(key);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean containsKeyOnDisk(Object key) {
if (key == null) {
return false;
}
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
return cache.containsKeyOnDisk(key) || authority.containsKeyOnDisk(key);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean containsKeyOffHeap(Object key) {
if (key == null) {
return false;
}
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
return cache.containsKeyOffHeap(key) || authority.containsKeyOffHeap(key);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public boolean containsKeyInMemory(Object key) {
if (key == null) {
return false;
}
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
return cache.containsKeyInMemory(key) || authority.containsKeyInMemory(key);
} finally {
lock.unlock();
}
}
/**
* {@inheritDoc}
*/
public List> getKeys() {
readLock();
try {
if (cache.isTierPinned() && !authority.isPersistent()) {
return cache.getKeys();
} else {
return new SetAsList(
new CacheKeySet(
authority.getKeys(), cache.isTierPinned() ? cache.getKeys() : cache.getPresentPinnedKeys()));
}
} finally {
readUnlock();
}
}
/**
* {@inheritDoc}
*/
public void removeAll() throws CacheException {
writeLock();
try {
cache.removeAll();
authority.removeAll();
} finally {
writeUnlock();
}
}
/**
* {@inheritDoc}
*/
public void dispose() {
cache.dispose();
authority.dispose();
}
/**
* {@inheritDoc}
*/
public int getSize() {
readLock();
try {
return Math.max(cache.getSize(), authority.getSize());
} finally {
readUnlock();
}
}
/**
* {@inheritDoc}
*/
public int getInMemorySize() {
readLock();
try {
return authority.getInMemorySize() + cache.getInMemorySize();
} finally {
readUnlock();
}
}
/**
* {@inheritDoc}
*/
public int getOffHeapSize() {
readLock();
try {
return authority.getOffHeapSize() + cache.getOffHeapSize();
} finally {
readUnlock();
}
}
/**
* {@inheritDoc}
*/
public int getOnDiskSize() {
readLock();
try {
return authority.getOnDiskSize() + cache.getOnDiskSize();
} finally {
readUnlock();
}
}
/**
* {@inheritDoc}
*/
public int getTerracottaClusteredSize() {
readLock();
try {
return authority.getTerracottaClusteredSize() + cache.getTerracottaClusteredSize();
} finally {
readUnlock();
}
}
/**
* {@inheritDoc}
*/
public long getInMemorySizeInBytes() {
return authority.getInMemorySizeInBytes() + cache.getInMemorySizeInBytes();
}
/**
* {@inheritDoc}
*/
public long getOffHeapSizeInBytes() {
return authority.getOffHeapSizeInBytes() + cache.getOffHeapSizeInBytes();
}
/**
* {@inheritDoc}
*/
public long getOnDiskSizeInBytes() {
return authority.getOnDiskSizeInBytes() + cache.getOnDiskSizeInBytes();
}
/**
* {@inheritDoc}
*/
public void expireElements() {
writeLock();
try {
authority.expireElements();
cache.expireElements();
} finally {
writeUnlock();
}
}
/**
* {@inheritDoc}
*/
//TODO : is this correct?
public void flush() throws IOException {
cache.flush();
authority.flush();
}
/**
* {@inheritDoc}
*/
public boolean bufferFull() {
return cache.bufferFull() || authority.bufferFull();
}
private void readLock() {
for (ReadWriteLockSync lock : getAllLocks()) {
lock.lock(LockType.READ);
}
}
private void readUnlock() {
for (ReadWriteLockSync lock : getAllLocks()) {
lock.unlock(LockType.READ);
}
}
private void writeLock() {
for (ReadWriteLockSync lock : getAllLocks()) {
lock.lock(LockType.WRITE);
}
}
private void writeUnlock() {
for (ReadWriteLockSync lock : getAllLocks()) {
lock.unlock(LockType.WRITE);
}
}
/**
* Returns the ReadWriteLock guarding this key.
*
* @param key key of interest
* @return lock for the supplied key
*/
public ReadWriteLock getLockFor(Object key) {
return masterLocks.getLockForKey(key);
}
/**
* Returns all ReadWriteLockSyncs guarding this store.
*/
protected List getAllLocks() {
return masterLocks.getAllSyncs();
}
/**
* {@inheritDoc}
*/
public Status getStatus() {
//TODO this might be wrong...
return authority.getStatus();
}
/**
* {@inheritDoc}
*/
public Policy getInMemoryEvictionPolicy() {
return cache.getInMemoryEvictionPolicy();
}
/**
* {@inheritDoc}
*/
public void setInMemoryEvictionPolicy(Policy policy) {
cache.setInMemoryEvictionPolicy(policy);
}
/**
* {@inheritDoc}
*/
public final Object getInternalContext() {
return masterLocks;
}
/**
* Checks whether the element can be safely evicted.
* It is done by checking whether we can lock the master lock for that Element's key and, if we could, remove that key from all other tiers,
* but the lowest.
* Failing to obey this, might result in firing an Element Evicted Event, while it is still present in higher tiers
* @param e The element we want to evict
* @return true, if it can be evicted, false otherwise
*/
public boolean isEvictionCandidate(final Element e) {
Object key = e.getObjectKey();
Lock lockForKey = masterLocks.getLockForKey(key).writeLock();
if (lockForKey.tryLock()) {
try {
cache.removeIfNotPinned(key);
return true;
} finally {
lockForKey.unlock();
}
} else {
return false;
}
}
/**
* Checks whether the key is held in the fronting cache
* @param key the key to check for
* @return true if cached, false otherwise
*/
public boolean isCached(final Object key) {
Lock lock = getLockFor(key).readLock();
lock.lock();
try {
return cache.containsKey(key);
} finally {
lock.unlock();
}
}
/**
* Whether evicting this from the cache should fire when evicting from upper tiers
*
* @param key the key to the element
* @return true if we should fire, otherwise false
*/
public boolean notifyEvictionFromCache(final Serializable key) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean hasAbortedSizeOf() {
return cache.hasAbortedSizeOf() || authority.hasAbortedSizeOf();
}
@Override
public void recalculateSize(Object key) {
Lock lock = getLockFor(key).writeLock();
lock.lock();
try {
authority.recalculateSize(key);
cache.recalculateSize(key);
} finally {
lock.unlock();
}
}
/**
* Type on which multiple concurrent authority fetches will wait.
*/
private static final class Fault {
private Element result;
private boolean complete;
public Element get() {
boolean interrupted = false;
try {
synchronized (this) {
while (!complete) {
try {
wait();
} catch (InterruptedException ex) {
interrupted = true;
}
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
return result;
}
public void complete(Element e) {
synchronized (this) {
result = e;
complete = true;
notifyAll();
}
}
}
}