![JAR search and dependency download from the Maven repository](/logo.png)
net.sf.ehcache.store.cachingtier.CountBasedBackEnd 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.
/**
* 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.cachingtier;
import net.sf.ehcache.Element;
import net.sf.ehcache.store.LruPolicy;
import net.sf.ehcache.store.Policy;
import net.sf.ehcache.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
*
* A backend to a OnHeapCachingTier that will be cap'ed based on the amount of entries
*
* @param the key type
* @param the value type
*
* @author Alex Snaps
*/
public class CountBasedBackEnd extends ConcurrentHashMap implements HeapCacheBackEnd {
private static final Logger LOG = LoggerFactory.getLogger(CountBasedBackEnd.class.getName());
private static final int MAX_EVICTIONS = 5;
private static final int SAMPLING_SIZE = 30;
private volatile long maxEntriesLocalHeap;
private volatile Policy policy;
private volatile RemovalCallback callback;
/**
* Constructs a cap'ed backend
* @param maxEntriesLocalHeap amount of mappings this should hold before it starts evicting
*/
public CountBasedBackEnd(final long maxEntriesLocalHeap) {
this(maxEntriesLocalHeap, new LruPolicy());
}
/**
* Constructs a cap'ed backend
* @param maxEntriesLocalHeap amount of mappings this should hold before it starts evicting
* @param policy the policy it'll use to decide what to evict
*/
public CountBasedBackEnd(final long maxEntriesLocalHeap, final Policy policy) {
this.maxEntriesLocalHeap = maxEntriesLocalHeap;
setPolicy(policy);
}
/**
* Dynamic property to switch the policy out
* @param policy the new eviction Policy to use
*/
public void setPolicy(final Policy policy) {
if (policy == null) {
throw new NullPointerException("We need a Policy passed in here, null won't cut it!");
}
this.policy = policy;
}
/**
* {@inheritDoc}
*/
@Override
public V putIfAbsent(final K key, final V value) {
final V v = super.putIfAbsent(key, value);
if (v == null) {
try {
evictIfRequired(key, value);
} catch (Throwable e) {
LOG.warn("Caught throwable while evicting", e);
}
}
return v;
}
/**
* {@inheritDoc}
*/
@Override
public void registerEvictionCallback(final EvictionCallback evictionCallback) {
this.callback = evictionCallback == null ? null : new RemovalCallback() {
@Override
public void removed(final Object key, final Object value) {
evictionCallback.evicted((K)key, (V)value);
}
};
}
@Override
public void recalculateSize(final K key) {
// NO OP!
}
@Override
public V remove(final Object key) {
return super.removeAndNotify(key, callback);
}
@Override
public Policy getPolicy() {
return policy;
}
@Override
public void clear(final boolean notify) {
if (notify) {
for (Map.Entry entry : entrySet()) {
if (entry.getValue() instanceof Element) {
removeAndNotify(entry.getKey(), entry.getValue(), callback);
}
}
} else {
super.clear();
}
}
@Override
public boolean hasSpace() {
return maxEntriesLocalHeap == 0 || maxEntriesLocalHeap > mappingCount();
}
private void evictIfRequired(final K key, final V value) {
if (maxEntriesLocalHeap == 0) {
return;
}
int evictions = MAX_EVICTIONS;
while (maxEntriesLocalHeap < mappingCount() && evictions-- > 0) {
final Element evictionCandidate = findEvictionCandidate(key, value);
if (evictionCandidate != null) {
remove(evictionCandidate.getObjectKey(), evictionCandidate, callback);
}
}
}
private Element findEvictionCandidate(final K key, final V value) {
List values = getRandomValues(SAMPLING_SIZE);
// this can return null. Let the cache get bigger by one.
List elements = new ArrayList(values.size() * 2);
for (V v : values) {
if (v instanceof Element && !((Element)v).getObjectKey().equals(key)) {
elements.add((Element)v);
}
}
return policy.selectedBasedOnPolicy(elements.toArray(new Element[elements.size()]),
value instanceof Element ? (Element)value : null);
}
/**
* Sets the capacity limit
* @param maxEntriesLocalHeap the new limit
*/
public void setMaxEntriesLocalHeap(final long maxEntriesLocalHeap) {
this.maxEntriesLocalHeap = maxEntriesLocalHeap;
}
/**
* Reads the current capacity limit
* @return the capacity limit
*/
public long getMaxEntriesLocalHeap() {
return maxEntriesLocalHeap;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy