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.
/**
* 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 net.sf.ehcache.CacheEntry;
import net.sf.ehcache.CacheException;
import net.sf.ehcache.Ehcache;
import net.sf.ehcache.Element;
import net.sf.ehcache.Status;
import net.sf.ehcache.concurrent.CacheLockProvider;
import net.sf.ehcache.concurrent.ReadWriteLockSync;
import net.sf.ehcache.concurrent.Sync;
import net.sf.ehcache.config.CacheConfiguration;
import net.sf.ehcache.config.CacheConfigurationListener;
import net.sf.ehcache.config.PinningConfiguration;
import net.sf.ehcache.config.SizeOfPolicyConfiguration;
import net.sf.ehcache.event.RegisteredEventListeners;
import net.sf.ehcache.pool.Pool;
import net.sf.ehcache.pool.PoolAccessor;
import net.sf.ehcache.pool.PoolableStore;
import net.sf.ehcache.pool.Size;
import net.sf.ehcache.pool.impl.DefaultSizeOfEngine;
import net.sf.ehcache.store.chm.SelectableConcurrentHashMap;
import net.sf.ehcache.store.disk.StoreUpdateException;
import net.sf.ehcache.util.ratestatistics.AtomicRateStatistic;
import net.sf.ehcache.util.ratestatistics.RateStatistic;
import net.sf.ehcache.writer.CacheWriterManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A Store implementation suitable for fast, concurrent in memory stores. The policy is determined by that
* configured in the cache.
*
* @author Surya Suravarapu
* @version $Id: MemoryStore.java 9093 2014-06-26 19:18:31Z alexsnaps $
*/
public class MemoryStore extends AbstractStore implements TierableStore, PoolableStore, CacheConfigurationListener {
/**
* This is the default from {@link java.util.concurrent.ConcurrentHashMap}. It should never be used, because we size
* the map to the max size of the store.
*/
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/**
* Set optimisation for 100 concurrent threads.
*/
private static final int CONCURRENCY_LEVEL = 100;
private static final int MAX_EVICTION_RATIO = 5;
private static final Logger LOG = LoggerFactory.getLogger(MemoryStore.class.getName());
private final boolean alwaysPutOnHeap;
/**
* The cache this store is associated with.
*/
private final Ehcache cache;
/**
* Map where items are stored by key.
*/
private final SelectableConcurrentHashMap map;
private final PoolAccessor poolAccessor;
private final RateStatistic hitRate = new AtomicRateStatistic(1000, TimeUnit.MILLISECONDS);
private final RateStatistic missRate = new AtomicRateStatistic(1000, TimeUnit.MILLISECONDS);
private final boolean storePinned;
private final boolean elementPinningEnabled;
/**
* The maximum size of the store (0 == no limit)
*/
private volatile int maximumSize;
/**
* status.
*/
private volatile Status status;
/**
* The eviction policy to use
*/
private volatile Policy policy;
/**
* The pool accessor
*/
private volatile CacheLockProvider lockProvider;
/**
* Constructs things that all MemoryStores have in common.
*
* @param cache the cache
* @param pool the pool tracking the on-heap usage
* @param notify whether to notify the Cache's EventNotificationService on eviction and expiry
*/
protected MemoryStore(Ehcache cache, Pool pool, boolean notify, BackingFactory factory) {
status = Status.STATUS_UNINITIALISED;
this.cache = cache;
this.maximumSize = (int) cache.getCacheConfiguration().getMaxEntriesLocalHeap();
this.policy = determineEvictionPolicy(cache);
this.poolAccessor = pool.createPoolAccessor(this,
SizeOfPolicyConfiguration.resolveMaxDepth(cache),
SizeOfPolicyConfiguration.resolveBehavior(cache).equals(SizeOfPolicyConfiguration.MaxDepthExceededBehavior.ABORT));
this.alwaysPutOnHeap = getAdvancedBooleanConfigProperty("alwaysPutOnHeap", cache.getCacheConfiguration().getName(), false);
this.storePinned = determineStorePinned(cache.getCacheConfiguration());
this.elementPinningEnabled = !cache.getCacheConfiguration().isOverflowToOffHeap();
int maximumCapacity = isClockEviction() && !storePinned ? maximumSize : 0;
RegisteredEventListeners eventListener = notify ? cache.getCacheEventNotificationService() : null;
if (Boolean.getBoolean(MemoryStore.class.getName() + ".presize")) {
// create the CHM with initialCapacity sufficient to hold maximumSize
final float loadFactor = maximumSize == 1 ? 1 : DEFAULT_LOAD_FACTOR;
int initialCapacity = getInitialCapacityForLoadFactor(maximumSize, loadFactor);
this.map = factory.newBackingMap(poolAccessor, elementPinningEnabled, initialCapacity,
loadFactor, CONCURRENCY_LEVEL, maximumCapacity, eventListener);
} else {
this.map = factory.newBackingMap(poolAccessor, elementPinningEnabled, CONCURRENCY_LEVEL, maximumCapacity, eventListener);
}
this.status = Status.STATUS_ALIVE;
if (LOG.isDebugEnabled()) {
LOG.debug("Initialized " + this.getClass().getName() + " for " + cache.getName());
}
}
private boolean determineStorePinned(CacheConfiguration cacheConfiguration) {
PinningConfiguration pinningConfiguration = cacheConfiguration.getPinningConfiguration();
if (pinningConfiguration == null) {
return false;
}
switch (pinningConfiguration.getStore()) {
case LOCALHEAP:
return true;
case LOCALMEMORY:
return !cacheConfiguration.isOverflowToOffHeap();
case INCACHE:
return !cacheConfiguration.isOverflowToOffHeap() && !cacheConfiguration.isOverflowToDisk();
default:
throw new IllegalArgumentException();
}
}
/**
* Calculates the initialCapacity for a desired maximumSize goal and loadFactor.
*
* @param maximumSizeGoal the desired maximum size goal
* @param loadFactor the load factor
* @return the calculated initialCapacity. Returns 0 if the parameter maximumSizeGoal is less than or equal
* to 0
*/
protected static int getInitialCapacityForLoadFactor(int maximumSizeGoal, float loadFactor) {
double actualMaximum = Math.ceil(maximumSizeGoal / loadFactor);
return Math.max(0, actualMaximum >= Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) actualMaximum);
}
/**
* A factory method to create a MemoryStore.
*
* @param cache the cache
* @param pool the pool tracking the on-heap usage
* @return an instance of a MemoryStore, configured with the appropriate eviction policy
*/
public static MemoryStore create(final Ehcache cache, Pool pool) {
MemoryStore memoryStore = new MemoryStore(cache, pool, false, new BasicBackingFactory());
cache.getCacheConfiguration().addConfigurationListener(memoryStore);
return memoryStore;
}
/**
* {@inheritDoc}
*/
public void unpinAll() {
if (elementPinningEnabled) {
map.unpinAll();
}
}
/**
* {@inheritDoc}
*/
public void setPinned(Object key, boolean pinned) {
if (elementPinningEnabled) {
map.setPinned(key, pinned);
}
}
/**
* {@inheritDoc}
*/
public boolean isPinned(Object key) {
return elementPinningEnabled && map.isPinned(key);
}
private boolean isPinningEnabled(Element element) {
return storePinned || isPinned(element.getObjectKey());
}
/**
* {@inheritDoc}
*/
public void fill(Element element) {
if (alwaysPutOnHeap || isPinningEnabled(element) || remove(element.getObjectKey()) != null || canPutWithoutEvicting(element)) {
put(element);
}
}
/**
* {@inheritDoc}
*/
public boolean removeIfNotPinned(final Object key) {
return !storePinned && !isPinned(key) && remove(key) != null;
}
/**
* Puts an item in the store. Note that this automatically results in an eviction if the store is full.
*
* @param element the element to add
*/
public boolean put(final Element element) throws CacheException {
if (element == null) {
return false;
}
long delta = poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element));
if (delta > -1) {
Element old = map.put(element.getObjectKey(), element, delta);
checkCapacity(element);
return old == null;
} else {
notifyDirectEviction(element);
return true;
}
}
/**
* {@inheritDoc}
*/
public boolean putWithWriter(Element element, CacheWriterManager writerManager) throws CacheException {
long delta = poolAccessor.add(element.getObjectKey(), element.getObjectValue(), map.storedObject(element), isPinningEnabled(element));
if (delta > -1) {
Element old = map.put(element.getObjectKey(), element, delta);
if (writerManager != null) {
try {
writerManager.put(element);
} catch (RuntimeException e) {
throw new StoreUpdateException(e, old != null);
}
}
checkCapacity(element);
return old == null;
} else {
notifyDirectEviction(element);
return true;
}
}
/**
* Gets an item from the cache.
*
* The last access time in {@link net.sf.ehcache.Element} is updated.
*
* @param key the key of the Element
* @return the element, or null if there was no match for the key
*/
public final Element get(final Object key) {
if (key == null) {
return null;
} else {
final Element e = map.get(key);
if (e == null) {
missRate.event();
} else {
hitRate.event();
}
return e;
}
}
/**
* Gets an item from the cache, without updating statistics.
*
* @param key the cache key
* @return the element, or null if there was no match for the key
*/
public final Element getQuiet(Object key) {
return get(key);
}
/**
* Removes an Element from the store.
*
* @param key the key of the Element, usually a String
* @return the Element if one was found, else null
*/
public Element remove(final Object key) {
if (key == null) {
return null;
}
return map.remove(key);
}
/**
* {@inheritDoc}
*/
public void removeNoReturn(final Object key) {
remove(key);
}
/**
* {@inheritDoc}
*/
public boolean isTierPinned() {
return storePinned;
}
/**
* {@inheritDoc}
*/
public Set getPresentPinnedKeys() {
return map.pinnedKeySet();
}
/**
* {@inheritDoc}
*/
public boolean isPersistent() {
return false;
}
/**
* {@inheritDoc}
*/
public final Element removeWithWriter(Object key, CacheWriterManager writerManager) throws CacheException {
if (key == null) {
return null;
}
// remove single item.
Element element = map.remove(key);
if (writerManager != null) {
writerManager.remove(new CacheEntry(key, element));
}
if (element == null && LOG.isDebugEnabled()) {
LOG.debug(cache.getName() + "Cache: Cannot remove entry as key " + key + " was not found");
}
return element;
}
/**
* Memory stores are never backed up and always return false
*/
public final boolean bufferFull() {
return false;
}
/**
* Expire all elements.
*
* This is a default implementation which does nothing. Expiration on demand is only implemented for disk stores.
*/
public void expireElements() {
for (Object key : map.keySet()) {
expireElement(key);
}
}
/**
* Evicts the element for the given key, if it exists and is expired
* @param key the key
* @return the evicted element, if any. Otherwise null
*/
protected Element expireElement(final Object key) {
Element value = get(key);
return value != null && value.isExpired() && map.remove(key, value) ? value : null;
}
/**
* Chooses the Policy from the cache configuration
* @param cache the cache
* @return the chosen eviction policy
*/
private static Policy determineEvictionPolicy(Ehcache cache) {
MemoryStoreEvictionPolicy policySelection = cache.getCacheConfiguration().getMemoryStoreEvictionPolicy();
if (policySelection.equals(MemoryStoreEvictionPolicy.LRU)) {
return new LruPolicy();
} else if (policySelection.equals(MemoryStoreEvictionPolicy.FIFO)) {
return new FifoPolicy();
} else if (policySelection.equals(MemoryStoreEvictionPolicy.LFU)) {
return new LfuPolicy();
} else if (policySelection.equals(MemoryStoreEvictionPolicy.CLOCK)) {
return null;
}
throw new IllegalArgumentException(policySelection + " isn't a valid eviction policy");
}
/**
* Remove all of the elements from the store.
*/
public final void removeAll() throws CacheException {
for (Object key : map.keySet()) {
remove(key);
}
}
/**
* Prepares for shutdown.
*/
public synchronized void dispose() {
if (status.equals(Status.STATUS_SHUTDOWN)) {
return;
}
status = Status.STATUS_SHUTDOWN;
flush();
poolAccessor.unlink();
}
/**
* Flush to disk only if the cache is diskPersistent.
*/
public void flush() {
if (cache.getCacheConfiguration().isClearOnFlush()) {
removeAll();
}
}
/**
* Gets an Array of the keys for all elements in the memory cache.
*
* Does not check for expired entries
*
* @return An List
*/
public final List getKeys() {
return new ArrayList