/*
* Copyright (C) 2011 eXo Platform SAS.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.exoplatform.services.cache.impl.infinispan.distributed;
import org.exoplatform.commons.utils.SecurityHelper;
import org.exoplatform.container.ExoContainer;
import org.exoplatform.container.ExoContainerContext;
import org.exoplatform.management.annotations.Managed;
import org.exoplatform.management.annotations.ManagedDescription;
import org.exoplatform.management.annotations.ManagedName;
import org.exoplatform.services.cache.CacheInfo;
import org.exoplatform.services.cache.CacheListener;
import org.exoplatform.services.cache.CacheListenerContext;
import org.exoplatform.services.cache.CachedObjectSelector;
import org.exoplatform.services.cache.ExoCache;
import org.exoplatform.services.cache.ExoCacheConfig;
import org.exoplatform.services.cache.ObjectCacheInfo;
import org.exoplatform.services.ispn.AbstractMapper;
import org.exoplatform.services.ispn.DistributedCacheManager;
import org.exoplatform.services.log.ExoLogger;
import org.exoplatform.services.log.Log;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.context.Flag;
import org.infinispan.distexec.mapreduce.Collector;
import org.infinispan.distexec.mapreduce.MapReduceTask;
import org.infinispan.distexec.mapreduce.Reducer;
import org.infinispan.notifications.Listener;
import org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
import org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author <a href="mailto:nfilotto@exoplatform.com">Nicolas Filotto</a>
* @version $Id$
*
*/
public class DistributedExoCache<K extends Serializable, V> implements ExoCache<K, V>
{
/**
* Logger.
*/
private static final Log LOG = ExoLogger//NOSONAR
.getLogger("exo.kernel.component.ext.cache.impl.infinispan.v5.DistributedExoCache");//NOSONAR
public static final String CACHE_NAME = "eXoCache";
private final AtomicInteger hits = new AtomicInteger(0);
private final AtomicInteger misses = new AtomicInteger(0);
private String label;
private String name;
private final String fullName;
private boolean distributed;
private boolean replicated;
private boolean logEnabled;
@SuppressWarnings("rawtypes")
private static final ConcurrentMap<Cache, ConcurrentMap<String, List<ListenerContext>>> ALL_LISTENERS =
new ConcurrentHashMap<Cache, ConcurrentMap<String, List<ListenerContext>>>();
protected final AdvancedCache<CacheKey<K>, V> cache;
@SuppressWarnings("unchecked")
public DistributedExoCache(ExoContainerContext ctx, ExoCacheConfig config, Cache<K, V> cache)
{
this.fullName = ctx.getName() + "-" + config.getName();
this.cache = (AdvancedCache<CacheKey<K>, V>)cache.getAdvancedCache();
setDistributed(config.isDistributed());
setLabel(config.getLabel());
setName(config.getName());
setLogEnabled(config.isLogEnabled());
setReplicated(config.isRepicated());
}
AdvancedCache<CacheKey<K>, V> getCache()
{
return cache;
}
/**
* @return the fullName
*/
String getFullName()
{
return fullName;
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("rawtypes")
public void addCacheListener(CacheListener<? super K, ? super V> listener)
{
if (listener == null)
{
throw new IllegalArgumentException("The listener cannot be null");
}
List<ListenerContext> lListeners = getListeners(fullName);
if (lListeners == null)
{
lListeners = new CopyOnWriteArrayList<ListenerContext>();
boolean alreadyAdded = false;
ConcurrentMap<String, List<ListenerContext>> listeners = getOrCreateListeners();
if (listeners.isEmpty())
{
synchronized (listeners)
{
if (listeners.isEmpty())
{
// Ensure that the listener is added only once
cache.addListener(new CacheEventListener());
listeners.put(fullName, lListeners);
alreadyAdded = true;
}
}
}
if (!alreadyAdded)
{
List<ListenerContext> oldValue = listeners.putIfAbsent(fullName, lListeners);
if (oldValue != null)
{
lListeners = oldValue;
}
}
}
lListeners.add(new ListenerContext<K, V>(listener, this));
}
@SuppressWarnings("rawtypes")
private ConcurrentMap<String, List<ListenerContext>> getOrCreateListeners()
{
ConcurrentMap<String, List<ListenerContext>> listeners = ALL_LISTENERS.get(cache);
if (listeners == null)
{
listeners = new ConcurrentHashMap<String, List<ListenerContext>>();
ConcurrentMap<String, List<ListenerContext>> oldValue = ALL_LISTENERS.putIfAbsent(cache, listeners);
if (oldValue != null)
{
listeners = oldValue;
}
}
return listeners;
}
@SuppressWarnings("rawtypes")
private List<ListenerContext> getListeners(String fullName)
{
ConcurrentMap<String, List<ListenerContext>> listeners = ALL_LISTENERS.get(cache);
return listeners == null ? null : listeners.get(fullName);
}
/**
* {@inheritDoc}
*/
public void clearCache()
{
SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
{
@Override
public Void run()
{
MapReduceTask<CacheKey<K>, V, Void, Void> task = new MapReduceTask<CacheKey<K>, V, Void, Void>(cache);
task.mappedWith(new ClearCacheMapper<K, V>(fullName)).reducedWith(new ClearCacheReducer());
task.execute();
return null;
}
});
onClearCache();
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public V get(Serializable name)
{
if (name == null)
{
return null;
}
@SuppressWarnings("rawtypes")
final CacheKey key = new CacheKey<Serializable>(fullName, name);
final V result = SecurityHelper.doPrivilegedAction(new PrivilegedAction<V>()
{
@Override
public V run()
{
return cache.get(key);
}
});
if (result == null)
{
misses.incrementAndGet();
}
else
{
hits.incrementAndGet();
}
onGet(key, result);
return result;
}
/**
* {@inheritDoc}
*/
public int getCacheHit()
{
return hits.get();
}
/**
* {@inheritDoc}
*/
public int getCacheMiss()
{
return misses.get();
}
/**
* {@inheritDoc}
*/
public int getCacheSize()
{
Map<String, Integer> map = SecurityHelper.doPrivilegedAction(new PrivilegedAction<Map<String, Integer>>()
{
@Override
public Map<String, Integer> run()
{
MapReduceTask<CacheKey<K>, V, String, Integer> task =
new MapReduceTask<CacheKey<K>, V, String, Integer>(cache);
task.mappedWith(new GetSizeMapper<K, V>(fullName)).reducedWith(new GetSizeReducer<String>());
return task.execute();
}
});
int sum = 0;
for (Integer i : map.values())
{
sum += i;
}
return sum;
}
/**
* {@inheritDoc}
*/
public List<V> getCachedObjects()
{
Map<String, List<V>> map = SecurityHelper.doPrivilegedAction(new PrivilegedAction<Map<String, List<V>>>()
{
@Override
public Map<String, List<V>> run()
{
MapReduceTask<CacheKey<K>, V, String, List<V>> task =
new MapReduceTask<CacheKey<K>, V, String, List<V>>(cache);
task.mappedWith(new GetCachedObjectsMapper<K, V>(fullName)).reducedWith(
new GetCachedObjectsReducer<String, V>());
return task.execute();
}
});
List<V> result = new ArrayList<V>();
for (List<V> vals : map.values())
{
result.addAll(vals);
}
return result;
}
/**
* {@inheritDoc}
*/
public String getLabel()
{
return label;
}
/**
* {@inheritDoc}
*/
public String getName()
{
return name;
}
/**
* {@inheritDoc}
*/
public boolean isDistributed()
{
return distributed;
}
/**
* {@inheritDoc}
*/
public boolean isLogEnabled()
{
return logEnabled;
}
/**
* {@inheritDoc}
*/
public boolean isReplicated()
{
return replicated;
}
/**
* {@inheritDoc}
*/
public void put(final K key, final V value) throws IllegalArgumentException
{
if (key == null)
{
throw new IllegalArgumentException("No null cache key accepted");
}
else if (value == null)
{
// ignore null values
return;
}
SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
{
@Override
public Void run()
{
putOnly(key, value);
return null;
}
});
onPut(key, value);
}
/**
* Only puts the data into the cache nothing more
*/
protected void putOnly(K key, V value)
{
cache.withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.IGNORE_RETURN_VALUES).put(new CacheKey<K>(fullName, key), value);
}
/**
* {@inheritDoc}
*/
public void putMap(final Map<? extends K, ? extends V> objs) throws IllegalArgumentException
{
if (objs == null)
{
throw new IllegalArgumentException("No null map accepted");
}
for (Serializable name : objs.keySet())
{
if (name == null)
{
throw new IllegalArgumentException("No null cache key accepted");
}
}
SecurityHelper.doPrivilegedAction(new PrivilegedAction<Void>()
{
@Override
public Void run()
{
// Start transaction
cache.startBatch();
try
{
// Wrap the key into a CacheKey and make sure that the key and the value
// are valid
Map<CacheKey<K>, V> map = new LinkedHashMap<CacheKey<K>, V>();
for (Map.Entry<? extends K, ? extends V> entry : objs.entrySet())
{
map.put(new CacheKey<K>(fullName, entry.getKey()), entry.getValue());
}
cache.putAll(map);
cache.endBatch(true);
// End transaction
for (Map.Entry<? extends K, ? extends V> entry : objs.entrySet())
{
onPut(entry.getKey(), entry.getValue());
}
}
catch (Exception e)//NOSONAR
{
cache.endBatch(false);
LOG.warn("An error occurs while executing the putMap method", e);
}
return null;
}
});
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
public V remove(Serializable name) throws IllegalArgumentException
{
if (name == null)
{
throw new IllegalArgumentException("No null cache key accepted");
}
@SuppressWarnings("rawtypes")
final CacheKey key = new CacheKey<Serializable>(fullName, name);
V result = SecurityHelper.doPrivilegedAction(new PrivilegedAction<V>()
{
@Override
public V run()
{
return cache.remove(key);
}
});
onRemove(key, result);
return result;
}
/**
* {@inheritDoc}
*/
public List<V> removeCachedObjects()
{
final List<V> list = getCachedObjects();
clearCache();
return list;
}
/**
* {@inheritDoc}
*/
public void select(CachedObjectSelector<? super K, ? super V> selector) throws Exception
{
if (selector == null)
{
throw new IllegalArgumentException("No null selector");
}
Map<K, V> map = SecurityHelper.doPrivilegedAction(new PrivilegedAction<Map<K, V>>()
{
@Override
public Map<K, V> run()
{
MapReduceTask<CacheKey<K>, V, K, V> task = new MapReduceTask<CacheKey<K>, V, K, V>(cache);
task.mappedWith(new GetEntriesMapper<K, V>(fullName)).reducedWith(new GetEntriesReducer<K, V>());
return task.execute();
}
});
for (K key : map.keySet())
{
if (key == null)
{
continue;
}
final V value = map.get(key);
ObjectCacheInfo<V> info = new ObjectCacheInfo<V>()
{
public V get()
{
return value;
}
public long getExpireTime()
{
// Cannot know: The expire time is managed by Infinispan itself
return -1;
}
};
if (selector.select(key, info))
{
selector.onSelect(this, key, info);
}
}
}
/**
* {@inheritDoc}
*/
public void setDistributed(boolean distributed)
{
this.distributed = distributed;
}
/**
* {@inheritDoc}
*/
public void setLabel(String label)
{
this.label = label;
}
/**
* {@inheritDoc}
*/
public void setLogEnabled(boolean logEnabled)
{
this.logEnabled = logEnabled;
}
/**
* {@inheritDoc}
*/
public void setName(String name)
{
this.name = name;
}
/**
* {@inheritDoc}
*/
public void setReplicated(boolean replicated)
{
this.replicated = replicated;
}
@SuppressWarnings({"rawtypes", "unchecked"})
void onExpire(CacheKey<K> key, V obj)
{
List<ListenerContext> listeners = getListeners(key.getFullName());
if (listeners == null || listeners.isEmpty())
{
return;
}
for (ListenerContext context : listeners)
{
try
{
context.onExpire(key.getKey(), obj);
}
catch (Exception e)//NOSONAR
{
if (LOG.isWarnEnabled())
LOG.warn("Cannot execute the CacheListener properly", e);
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
void onRemove(CacheKey<K> key, V obj)
{
List<ListenerContext> listeners = getListeners(key.getFullName());
if (listeners == null || listeners.isEmpty())
{
return;
}
for (ListenerContext context : listeners)
{
try
{
context.onRemove(key, obj);
}
catch (Exception e)//NOSONAR
{
if (LOG.isWarnEnabled())
LOG.warn("Cannot execute the CacheListener properly", e);
}
}
}
void onPut(CacheKey<K> key, V obj)
{
onPut(key.getFullName(), key.getKey(), obj);
}
void onPut(K key, V obj)
{
onPut(fullName, key, obj);
}
@SuppressWarnings({"rawtypes", "unchecked"})
void onPut(String fullName, K key, V obj)
{
List<ListenerContext> listeners = getListeners(fullName);
if (listeners == null || listeners.isEmpty())
{
return;
}
for (ListenerContext context : listeners)
{
try
{
context.onPut(key, obj);
}
catch (Exception e)//NOSONAR
{
if (LOG.isWarnEnabled())
LOG.warn("Cannot execute the CacheListener properly", e);
}
}
}
@SuppressWarnings({"rawtypes", "unchecked"})
void onGet(CacheKey<K> key, V obj)
{
List<ListenerContext> listeners = getListeners(key.getFullName());
if (listeners == null || listeners.isEmpty())
{
return;
}
for (ListenerContext context : listeners)
{
try
{
context.onGet(key, obj);
}
catch (Exception e)//NOSONAR
{
if (LOG.isWarnEnabled())
LOG.warn("Cannot execute the CacheListener properly", e);
}
}
}
@SuppressWarnings("rawtypes")
void onClearCache()
{
List<ListenerContext> listeners = getListeners(fullName);
if (listeners == null || listeners.isEmpty())
{
return;
}
for (ListenerContext context : listeners)
{
try
{
context.onClearCache();
}
catch (Exception e)//NOSONAR
{
if (LOG.isWarnEnabled())
LOG.warn("Cannot execute the CacheListener properly", e);
}
}
}
@Listener
public class CacheEventListener
{
/**
* Warning Infinispan triggers a <code>CacheEntryEvictedEvent</code> only at explicit eviction
* that is done lazily which is not exactly what we expect, we still use it to be
* able to use it with <code>avoidValueReplication</code> set to <code>true</code>.
*/
@CacheEntriesEvicted
public void cacheEntryEvicted(CacheEntriesEvictedEvent<CacheKey<K>, V> evt)
{
if (evt.isPre())
{
for (Map.Entry<CacheKey<K>, V> entry : evt.getEntries().entrySet())
{
onExpire(entry.getKey(), entry.getValue());
}
}
}
@CacheEntryRemoved
public void cacheEntryRemoved(CacheEntryRemovedEvent<CacheKey<K>, V> evt)
{
if (evt.isPre() && !evt.isOriginLocal())
{
final CacheKey<K> key = evt.getKey();
final V value = evt.getValue();
onRemove(key, value);
}
}
@CacheEntryModified
public void cacheEntryModified(CacheEntryModifiedEvent<CacheKey<K>, V> evt)
{
if (!evt.isOriginLocal() && !evt.isPre())
{
final CacheKey<K> key = evt.getKey();
final V value = evt.getValue();
onPut(key, value);
}
}
}
private static class ListenerContext<K extends Serializable, V> implements CacheListenerContext, CacheInfo
{
/** . */
private final ExoCache<K, V> cache;
/** . */
final CacheListener<? super K, ? super V> listener;
public ListenerContext(CacheListener<? super K, ? super V> listener, ExoCache<K, V> cache)
{
this.listener = listener;
this.cache = cache;
}
public CacheInfo getCacheInfo()
{
return this;
}
public String getName()
{
return cache.getName();
}
public int getMaxSize()
{
return cache.getMaxSize();
}
public long getLiveTime()
{
return cache.getLiveTime();
}
public int getSize()
{
return cache.getCacheSize();
}
void onExpire(K key, V obj) throws Exception
{
listener.onExpire(this, key, obj);
}
void onRemove(K key, V obj) throws Exception
{
listener.onRemove(this, key, obj);
}
void onPut(K key, V obj) throws Exception
{
listener.onPut(this, key, obj);
}
void onGet(K key, V obj) throws Exception
{
listener.onGet(this, key, obj);
}
void onClearCache() throws Exception
{
listener.onClearCache(this);
}
}
public void setMaxSize(int max)
{
throw new UnsupportedOperationException("The configuration of the cache cannot not be modified");
}
public void setLiveTime(long period)
{
throw new UnsupportedOperationException("The configuration of the cache cannot not be modified");
}
@ManagedName("MaxEntries")
@ManagedDescription("Maximum number of entries in a cache instance. -1 means no limit.")
public int getMaxSize()
{
return cache.getCacheConfiguration().eviction().maxEntries();
}
@ManagedName("Lifespan")
@ManagedDescription("Maximum lifespan of a cache entry, after which the entry is expired cluster-wide."
+ " -1 means the entries never expire.")
public long getLiveTime()
{
return cache.getCacheConfiguration().expiration().lifespan();
}
@Managed
@ManagedName("MaxIdle")
@ManagedDescription("Maximum idle time a cache entry will be maintained in the cache. "
+ "If the idle time is exceeded, the entry will be expired cluster-wide. -1 means the entries never expire.")
public long getMaxIdle()
{
return cache.getCacheConfiguration().expiration().maxIdle();
}
@Managed
@ManagedName("WakeUpInterval")
@ManagedDescription("Interval between subsequent eviction runs. If you wish to disable the periodic eviction "
+ "process altogether, set wakeupInterval to -1.")
public long getWakeUpInterval()
{
return cache.getCacheConfiguration().expiration().wakeUpInterval();
}
public static class CacheKey<K> implements Externalizable
{
private K key;
private String fullName;
public CacheKey()
{
}
public CacheKey(String fullName, K key)
{
this.fullName = fullName;
this.key = key;
}
/**
* @return the nested key
*/
K getKey()
{
return key;
}
/**
* @return the fullName
*/
String getFullName()
{
return fullName;
}
/**
* @see java.lang.Object#hashCode()
*/
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + ((fullName == null) ? 0 : fullName.hashCode());
result = prime * result + ((key == null) ? 0 : key.hashCode());
return result;
}
/**
* @see java.lang.Object#equals(java.lang.Object)
*/
@Override
public boolean equals(Object obj)
{
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("rawtypes")
CacheKey other = (CacheKey)obj;
if (fullName == null)
{
if (other.fullName != null)
return false;
}
else if (!fullName.equals(other.fullName))
return false;
if (key == null)
{
if (other.key != null)
return false;
}
else if (!key.equals(other.key))
return false;
return true;
}
/**
* @see java.lang.Object#toString()
*/
@Override
public String toString()
{
return "CacheKey [fullName=" + fullName + ", key=" + key + "]";
}
/**
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException
{
byte[] buf = fullName.getBytes("UTF-8");
out.writeInt(buf.length);
out.write(buf);
out.writeObject(key);
}
/**
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
@SuppressWarnings("unchecked")
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
byte[] buf = new byte[in.readInt()];
in.readFully(buf);
fullName = new String(buf, "UTF-8");
key = (K)in.readObject();
}
}
private abstract static class AbstractExoCacheMapper<K, V, KOut, VOut> extends
AbstractMapper<CacheKey<K>, V, KOut, VOut> implements Externalizable
{
/**
* The full name of the cache instance
*/
private String fullName;
public AbstractExoCacheMapper()
{
}
public AbstractExoCacheMapper(String fullName)
{
this.fullName = fullName;
}
/**
* The serial version UID
*/
private static final long serialVersionUID = 7962676854308932222L;
/**
* @see org.exoplatform.services.ispn.AbstractMapper#isValid(java.lang.Object)
*/
@Override
protected boolean isValid(CacheKey<K> key)
{
return fullName.equals(key.getFullName());
}
/**
* @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
*/
public void writeExternal(ObjectOutput out) throws IOException
{
byte[] buf = fullName.getBytes("UTF-8");
out.writeInt(buf.length);
out.write(buf);
}
/**
* @see java.io.Externalizable#readExternal(java.io.ObjectInput)
*/
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
{
byte[] buf = new byte[in.readInt()];
in.readFully(buf);
fullName = new String(buf, "UTF-8");
}
}
public static class GetSizeMapper<K, V> extends AbstractExoCacheMapper<K, V, String, Integer>
{
public GetSizeMapper()
{
}
public GetSizeMapper(String fullName)
{
super(fullName);
}
/**
* {@inheritDoc}
*/
@Override
protected void _map(CacheKey<K> key, V value, Collector<String, Integer> collector)
{
collector.emit("total", Integer.valueOf(1));
}
}
public static class GetSizeReducer<K> implements Reducer<K, Integer>
{
/**
* The serial version UID
*/
private static final long serialVersionUID = -5264142863835473112L;
/**
* @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator)
*/
@Override
public Integer reduce(K reducedKey, Iterator<Integer> iter)
{
int sum = 0;
while (iter.hasNext())
{
Integer i = iter.next();
sum += i;
}
return sum;
}
}
public static class GetCachedObjectsMapper<K, V> extends AbstractExoCacheMapper<K, V, String, List<V>>
{
public GetCachedObjectsMapper()
{
}
public GetCachedObjectsMapper(String fullName)
{
super(fullName);
}
/**
* {@inheritDoc}
*/
@Override
protected void _map(CacheKey<K> key, V value, Collector<String, List<V>> collector)
{
collector.emit("values", Collections.singletonList(value));
}
}
public static class GetCachedObjectsReducer<K, V> implements Reducer<K, List<V>>
{
/**
* The serial version UID
*/
private static final long serialVersionUID = 8069024420056440405L;
/**
* @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator)
*/
@Override
public List<V> reduce(K reducedKey, Iterator<List<V>> iter)
{
List<V> values = new ArrayList<V>();
while (iter.hasNext())
{
List<V> vals = iter.next();
values.addAll(vals);
}
return values;
}
}
public static class ClearCacheMapper<K, V> extends AbstractExoCacheMapper<K, V, Void, Void>
{
public ClearCacheMapper()
{
}
public ClearCacheMapper(String fullName)
{
super(fullName);
}
/**
* {@inheritDoc}
*/
@Override
protected void _map(CacheKey<K> key, V value, Collector<Void, Void> collector)
{
ExoContainer container = ExoContainerContext.getTopContainer();
if (container == null)
{
LOG.error("The top container could not be found");
return;
}
DistributedCacheManager dcm =
(DistributedCacheManager)container.getComponentInstanceOfType(DistributedCacheManager.class);
if (dcm == null)
{
LOG.error("The DistributedCacheManager could not be found at top container level, please configure it.");
return;
}
Cache<CacheKey<K>, V> cache = dcm.getCache(CACHE_NAME);
cache.getAdvancedCache().withFlags(Flag.SKIP_REMOTE_LOOKUP, Flag.FAIL_SILENTLY).remove(key);
}
}
public static class ClearCacheReducer implements Reducer<Void, Void>
{
/**
* The serial version UID
*/
private static final long serialVersionUID = -8111087186325793256L;
/**
* @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator)
*/
@Override
public Void reduce(Void reducedKey, Iterator<Void> iter)
{
return null;
}
}
public static class GetEntriesMapper<K, V> extends AbstractExoCacheMapper<K, V, K, V>
{
public GetEntriesMapper()
{
}
public GetEntriesMapper(String fullName)
{
super(fullName);
}
/**
* {@inheritDoc}
*/
@Override
protected void _map(CacheKey<K> key, V value, Collector<K, V> collector)
{
collector.emit(key.getKey(), value);
}
}
public static class GetEntriesReducer<K, V> implements Reducer<K, V>
{
/**
* The serial version UID
*/
private static final long serialVersionUID = 5153826700048219537L;
/**
* @see org.infinispan.distexec.mapreduce.Reducer#reduce(java.lang.Object, java.util.Iterator)
*/
@Override
public V reduce(K reducedKey, Iterator<V> iter)
{
return iter == null || !iter.hasNext() ? null : iter.next();
}
}
}