package org.jboss.cache.invocation;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.*;
import org.jboss.cache.buddyreplication.BuddyManager;
import org.jboss.cache.buddyreplication.GravitateResult;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.Option;
import org.jboss.cache.factories.InterceptorChainFactory;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.interceptors.Interceptor;
import org.jboss.cache.loader.CacheLoaderManager;
import org.jboss.cache.marshall.Marshaller;
import org.jboss.cache.marshall.MethodCall;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jboss.cache.notifications.Notifier;
import org.jboss.cache.statetransfer.StateTransferManager;
import org.jboss.cache.transaction.GlobalTransaction;
import org.jboss.cache.transaction.TransactionTable;
import org.jgroups.Address;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The delegate that users (and interceptor authors) interact with when they create a cache by using a cache factory.
* This wrapper delegates calls down the interceptor chain.
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @since 2.1.0
*/
@SuppressWarnings("unchecked")
public class CacheInvocationDelegate<K, V> extends AbstractInvocationDelegate implements CacheSPI<K, V>
{
// this stuff is needed since the SPI has methods to retrieve these.
private StateTransferManager stateTransferManager;
private CacheLoaderManager cacheLoaderManager;
private Notifier notifier;
private TransactionManager transactionManager;
private BuddyManager buddyManager;
private TransactionTable transactionTable;
private RPCManager rpcManager;
private RegionManager regionManager;
private Marshaller marshaller;
public CacheInvocationDelegate()
{
log = LogFactory.getLog(CacheInvocationDelegate.class);
}
@Inject
private void injectDependencies(StateTransferManager stateTransferManager, CacheLoaderManager cacheLoaderManager, Notifier notifier,
TransactionManager transactionManager, BuddyManager buddyManager, TransactionTable transactionTable,
RPCManager rpcManager, RegionManager regionManager, Marshaller marshaller)
{
this.stateTransferManager = stateTransferManager;
this.cacheLoaderManager = cacheLoaderManager;
this.notifier = notifier;
this.transactionManager = transactionManager;
this.buddyManager = buddyManager;
this.transactionTable = transactionTable;
this.rpcManager = rpcManager;
this.regionManager = regionManager;
this.marshaller = marshaller;
}
private void reset()
{
this.stateTransferManager = null;
this.cacheLoaderManager = null;
this.transactionManager = null;
this.buddyManager = null;
this.transactionTable = null;
this.rpcManager = null;
this.marshaller = null;
}
@Override
public String toString()
{
return cache == null ? super.toString() : cache.toString();
}
public Configuration getConfiguration()
{
return configuration;
}
public NodeSPI<K, V> getRoot()
{
return (NodeSPI<K, V>) cache.getRoot();
}
public TransactionManager getTransactionManager()
{
return transactionManager;
}
public List<Interceptor> getInterceptorChain()
{
List interceptors = InterceptorChainFactory.asList(interceptorChain);
return interceptors == null ? Collections.emptyList() : Collections.unmodifiableList(interceptors);
}
public void addInterceptor(Interceptor i, int position)
{
cache.addInterceptor(i, position);
}
public void addInterceptor(Interceptor i, Class<? extends Interceptor> afterInterceptor)
{
cache.addInterceptor(i, afterInterceptor);
}
public void removeInterceptor(int position)
{
cache.removeInterceptor(position);
}
public void removeInterceptor(Class<? extends Interceptor> interceptorType)
{
cache.removeInterceptor(interceptorType);
}
public CacheLoaderManager getCacheLoaderManager()
{
return cacheLoaderManager;
}
public BuddyManager getBuddyManager()
{
return buddyManager;
}
public TransactionTable getTransactionTable()
{
return transactionTable;
}
public RPCManager getRPCManager()
{
return rpcManager;
}
public StateTransferManager getStateTransferManager()
{
return stateTransferManager;
}
public String getClusterName()
{
return cache.getClusterName();
}
public int getNumberOfAttributes()
{
return cache.getNumberOfAttributes();
}
public int getNumberOfNodes()
{
return cache.getNumberOfNodes();
}
public RegionManager getRegionManager()
{
return regionManager;
}
public GlobalTransaction getCurrentTransaction(Transaction tx, boolean createIfNotExists)
{
return cache.getCurrentTransaction(tx, createIfNotExists);
}
public GlobalTransaction getCurrentTransaction()
{
return cache.getCurrentTransaction();
}
public Set<Fqn> getInternalFqns()
{
return cache.getInternalFqns();
}
public int getNumberOfLocksHeld()
{
return cache.getNumberOfLocksHeld();
}
public boolean exists(String fqn)
{
return exists(Fqn.fromString(fqn));
}
public boolean exists(Fqn<?> fqn)
{
return peek(fqn, false, false) != null;
}
public Notifier getNotifier()
{
return notifier;
}
public Marshaller getMarshaller()
{
return marshaller;
}
public GravitateResult gravitateData(Fqn fqn, boolean searchBuddyBackupSubtrees)
{
return cache.gravitateData(fqn, searchBuddyBackupSubtrees);
}
public NodeSPI<K, V> peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes)
{
return (NodeSPI<K, V>) cache.peek(fqn, includeDeletedNodes, includeInvalidNodes);
}
public NodeSPI<K, V> peek(Fqn fqn, boolean includeDeletedNodes)
{
return (NodeSPI<K, V>) cache.peek(fqn, includeDeletedNodes);
}
public void addCacheListener(Object listener)
{
notifier.addCacheListener(listener);
}
public void removeCacheListener(Object listener)
{
notifier.removeCacheListener(listener);
}
public Set<Object> getCacheListeners()
{
return notifier.getCacheListeners();
}
public void create() throws CacheException
{
cache.create();
}
public void start() throws CacheException
{
cache.start();
}
public void stop()
{
cache.stop();
}
public void destroy()
{
reset();
cache.destroy();
}
public CacheStatus getCacheStatus()
{
return cache == null ? null : cache.getCacheStatus();
}
public InvocationContext getInvocationContext()
{
assertIsConstructed();
return invocationContextContainer.get();
}
public void setInvocationContext(InvocationContext ctx)
{
assertIsConstructed();
// assume a null ctx is meant to "un-set" the context?
if (ctx == null) invocationContextContainer.remove();
else invocationContextContainer.set(ctx);
}
public Address getLocalAddress()
{
if (rpcManager == null) return null;
return rpcManager.getLocalAddress();
}
public List<Address> getMembers()
{
if (rpcManager == null) return null;
return rpcManager.getMembers();
}
public String getVersion()
{
return Version.printVersion();
}
public void move(Fqn<?> nodeToMove, Fqn<?> newParent) throws NodeNotExistsException
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.moveMethodLocal_id, nodeToMove, newParent);
invoke(m);
}
public void move(String nodeToMove, String newParent) throws NodeNotExistsException
{
move(Fqn.fromString(nodeToMove), Fqn.fromString(newParent));
}
public boolean removeRegion(Fqn fqn)
{
return regionManager.removeRegion(fqn);
}
public Region getRegion(Fqn fqn, boolean createIfAbsent)
{
return regionManager.getRegion(fqn, createIfAbsent);
}
public void evict(Fqn<?> fqn, boolean recursive)
{
if (!getCacheStatus().allowInvocations()) throw new IllegalStateException("Cache is not in STARTED state");
//this method should be called by eviction thread only, so no transaction - expected (sec param is false)
Node<K, V> node = peek(fqn, false);
if (node != null && node.isResident())
{
return;
}
if (recursive)
{
if (node != null)
{
evictChildren((NodeSPI<K, V>) node);
}
}
else
{
evict(fqn);
}
}
private void evictChildren(NodeSPI<K, V> n)
{
for (NodeSPI<K, V> child : n.getChildrenDirect())
{
evictChildren(child);
}
evict(n.getFqn());
}
public void evict(Fqn<?> fqn)
{
Node<K, V> node = peek(fqn, false);
if (node != null && node.isResident())
{
return;
}
if (fqn.isRoot())
{
// special treatment for root eviction
// we need to preserve options
InvocationContext ctx = getInvocationContext();
Option o = ctx.getOptionOverrides();
for (Object childName : cache.peek(fqn, false, false).getChildrenNames())
{
ctx.setOptionOverrides(o);
evict(new Fqn<Object>(fqn, childName));
}
}
else
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.evictNodeMethodLocal_id, fqn);
invoke(m);
}
}
public V get(Fqn<?> fqn, K key)
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.getKeyValueMethodLocal_id, fqn, key, true);
return (V) invoke(m);
}
public V get(String fqn, K key)
{
return get(Fqn.fromString(fqn), key);
}
public boolean removeNode(Fqn<?> fqn)
{
GlobalTransaction tx = cache.getCurrentTransaction();
// special case if we are removing the root. Remove all children instead.
if (fqn.isRoot())
{
boolean result = true;
// we need to preserve options
InvocationContext ctx = getInvocationContext();
Option o = ctx.getOptionOverrides();
Set<Fqn> internalFqns = getInternalFqns();
for (Object childName : peek(fqn, false, false).getChildrenNames())
{
if (!internalFqns.contains(new Fqn(childName)))
{
ctx.setOptionOverrides(o);
result = removeNode(new Fqn<Object>((Fqn<Object>) fqn, childName)) && result;
}
}
return result;
}
else
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.removeNodeMethodLocal_id, tx, fqn, true, false);
Object retval = invoke(m);
return retval != null && (Boolean) retval;
}
}
public boolean removeNode(String fqn)
{
return removeNode(Fqn.fromString(fqn));
}
public NodeSPI<K, V> getNode(Fqn<?> fqn)
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.getNodeMethodLocal_id, fqn);
return (NodeSPI) invoke(m);
}
public NodeSPI<K, V> getNode(String fqn)
{
return getNode(Fqn.fromString(fqn));
}
public V remove(Fqn<?> fqn, K key) throws CacheException
{
GlobalTransaction tx = cache.getCurrentTransaction();
MethodCall m = MethodCallFactory.create(MethodDeclarations.removeKeyMethodLocal_id, tx, fqn, key, true);
return (V) invoke(m);
}
public V remove(String fqn, K key)
{
return remove(Fqn.fromString(fqn), key);
}
public void put(Fqn<?> fqn, Map<K, V> data)
{
GlobalTransaction tx = cache.getCurrentTransaction();
MethodCall m = MethodCallFactory.create(MethodDeclarations.putDataMethodLocal_id, tx, fqn, data, true);
invoke(m);
}
public void put(String fqn, Map<K, V> data)
{
put(Fqn.fromString(fqn), data);
}
public void putForExternalRead(Fqn<?> fqn, K key, V value)
{
// if the node exists then this should be a no-op.
if (peek(fqn, false, false) == null)
{
getInvocationContext().getOptionOverrides().setFailSilently(true);
getInvocationContext().getOptionOverrides().setForceAsynchronous(true);
//GlobalTransaction tx = cache.getCurrentTransaction();
MethodCall m = MethodCallFactory.create(MethodDeclarations.putForExternalReadMethodLocal_id, null, fqn, key, value);
invoke(m);
}
else
{
if (log.isDebugEnabled())
log.debug("putForExternalRead() called with Fqn " + fqn + " and this node already exists. This method is hence a no op.");
}
}
public V put(Fqn<?> fqn, K key, V value)
{
GlobalTransaction tx = cache.getCurrentTransaction();
MethodCall m = MethodCallFactory.create(MethodDeclarations.putKeyValMethodLocal_id, tx, fqn, key, value, true);
return (V) invoke(m);
}
public V put(String fqn, K key, V value)
{
return put(Fqn.fromString(fqn), key, value);
}
public Set<Object> getCacheListeners(Fqn region)
{
throw new UnsupportedOperationException("Not implemented in this release");
}
public void removeCacheListener(Fqn region, Object listener)
{
throw new UnsupportedOperationException("Not implemented in this release");
}
public void addCacheListener(Fqn region, Object listener)
{
throw new UnsupportedOperationException("Not implemented in this release");
}
/**
* Retrieves a defensively copied data map of the underlying node.
*
* @param fqn
* @return map of data, or an empty map
* @throws CacheException
*/
public Map<K, V> getData(Fqn<?> fqn)
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.getDataMapMethodLocal_id, fqn);
return (Map<K, V>) invoke(m);
}
/**
* Returns a set of attribute keys for the Fqn.
* Returns null if the node is not found, otherwise a Set.
* The set is a copy of the actual keys for this node.
*
* @param fqn name of the node
*/
public Set getKeys(String fqn)
{
return getKeys(Fqn.fromString(fqn));
}
/**
* Returns a set of attribute keys for the Fqn.
* Returns null if the node is not found, otherwise a Set.
* The set is a copy of the actual keys for this node.
*
* @param fqn name of the node
*/
public Set<K> getKeys(Fqn<?> fqn)
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.getKeysMethodLocal_id, fqn);
return (Set<K>) invoke(m);
}
/**
* Removes the keys and properties from a node.
*/
public void clearData(String fqn) throws CacheException
{
clearData(Fqn.fromString(fqn));
}
/**
* Removes the keys and properties from a named node.
*/
public void clearData(Fqn fqn)
{
GlobalTransaction tx = getCurrentTransaction();
MethodCall m = MethodCallFactory.create(MethodDeclarations.removeDataMethodLocal_id, tx, fqn, true);
invoke(m);
}
/**
* Returns all children of a given node. Returns an empty set if there are no children.
* The set is unmodifiable.
*
* @param fqn The fully qualified name of the node
* @return Set an unmodifiable set of children names, Object.
*/
public <E> Set<E> getChildrenNames(Fqn<E> fqn)
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.getChildrenNamesMethodLocal_id, fqn);
Set<E> retval = null;
retval = (Set<E>) invoke(m);
if (retval != null)
retval = Collections.unmodifiableSet(new HashSet<E>(retval));
else
retval = Collections.emptySet();
return retval;
}
public Set getChildrenNames(String fqn)
{
return getChildrenNames(Fqn.fromString(fqn));
}
public CacheImpl getDelegationTarget()
{
return cache;
}
}