package org.jboss.cache.invocation;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.CacheStatus;
import org.jboss.cache.DataContainer;
import org.jboss.cache.Fqn;
import org.jboss.cache.InvocationContext;
import org.jboss.cache.NodeNotExistsException;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.RPCManager;
import org.jboss.cache.Region;
import org.jboss.cache.RegionManager;
import org.jboss.cache.Version;
import org.jboss.cache.buddyreplication.BuddyManager;
import org.jboss.cache.buddyreplication.GravitateResult;
import org.jboss.cache.commands.read.GetChildrenNamesCommand;
import org.jboss.cache.commands.read.GetDataMapCommand;
import org.jboss.cache.commands.read.GetKeyValueCommand;
import org.jboss.cache.commands.read.GetKeysCommand;
import org.jboss.cache.commands.read.GetNodeCommand;
import org.jboss.cache.commands.read.GravitateDataCommand;
import org.jboss.cache.commands.write.EvictCommand;
import org.jboss.cache.commands.write.MoveCommand;
import org.jboss.cache.commands.write.PutDataMapCommand;
import org.jboss.cache.commands.write.PutForExternalReadCommand;
import org.jboss.cache.commands.write.PutKeyValueCommand;
import org.jboss.cache.commands.write.RemoveKeyCommand;
import org.jboss.cache.commands.write.RemoveNodeCommand;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.config.Option;
import org.jboss.cache.factories.CommandsFactory;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.factories.annotations.NonVolatile;
import org.jboss.cache.interceptors.base.CommandInterceptor;
import org.jboss.cache.loader.CacheLoaderManager;
import org.jboss.cache.marshall.Marshaller;
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 ChainedInterceptor 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")
@NonVolatile
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;
private DataContainer dataContainer;
private CommandsFactory commandsFactory;
@Inject
public void initialize(StateTransferManager stateTransferManager, CacheLoaderManager cacheLoaderManager, Notifier notifier,
TransactionManager transactionManager, BuddyManager buddyManager, TransactionTable transactionTable,
RPCManager rpcManager, RegionManager regionManager, Marshaller marshaller,
CommandsFactory commandsFactory, DataContainer dataContainer)
{
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;
this.dataContainer = dataContainer;
this.commandsFactory = commandsFactory;
}
private void reset()
{
this.stateTransferManager = null;
this.cacheLoaderManager = null;
this.transactionManager = null;
this.buddyManager = null;
this.transactionTable = null;
this.rpcManager = null;
this.marshaller = null;
this.dataContainer = null;
this.commandsFactory = null;
}
@Override
public String toString()
{
return dataContainer == null ? super.toString() : dataContainer.toString();
}
public Configuration getConfiguration()
{
return configuration;
}
public NodeSPI<K, V> getRoot()
{
return (NodeSPI<K, V>) dataContainer.getRoot();
}
public TransactionManager getTransactionManager()
{
return transactionManager;
}
public void addInterceptor(CommandInterceptor i, int position)
{
invoker.addInterceptor(i, position);
}
public void addInterceptor(CommandInterceptor i, Class<? extends CommandInterceptor> afterInterceptor)
{
invoker.addInterceptor(i, afterInterceptor);
}
public List<CommandInterceptor> getInterceptorChain()
{
return invoker.asList();
}
public void removeInterceptor(int position)
{
invoker.removeInterceptor(position);
}
public void removeInterceptor(Class<? extends CommandInterceptor> interceptorType)
{
invoker.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 configuration.getClusterName();
}
public int getNumberOfAttributes()
{
return dataContainer.getNumberOfAttributes();
}
public int getNumberOfNodes()
{
return dataContainer.getNumberOfNodes();
}
public RegionManager getRegionManager()
{
return regionManager;
}
public GlobalTransaction getCurrentTransaction(Transaction tx, boolean createIfNotExists)
{
return transactionTable.getCurrentTransaction(tx, createIfNotExists);
}
public GlobalTransaction getCurrentTransaction()
{
return transactionTable.getCurrentTransaction();
}
public Set<Fqn> getInternalFqns()
{
return dataContainer.getInternalFqns();
}
public int getNumberOfLocksHeld()
{
return dataContainer.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, InvocationContext ctx)
{
cacheStatusCheck(ctx);
GravitateDataCommand command = commandsFactory.buildGravitateDataCommand(fqn, searchBuddyBackupSubtrees);
return (GravitateResult) invoker.invoke(ctx, command);
// return (GravitateResult) command.perform(null);
}
public NodeSPI<K, V> peek(Fqn fqn, boolean includeDeletedNodes, boolean includeInvalidNodes)
{
return (NodeSPI<K, V>) dataContainer.peek(fqn, includeDeletedNodes, includeInvalidNodes);
}
public NodeSPI<K, V> peek(Fqn fqn, boolean includeDeletedNodes)
{
return (NodeSPI<K, V>) dataContainer.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
{
componentRegistry.create();
}
public void start() throws CacheException
{
componentRegistry.start();
}
public void stop()
{
componentRegistry.stop();
}
public void destroy()
{
reset();
componentRegistry.destroy();
}
public CacheStatus getCacheStatus()
{
return componentRegistry.getState();
}
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
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
MoveCommand command = commandsFactory.buildMoveCommand(nodeToMove, newParent);
invoker.invoke(ctx, command);
}
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)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
EvictCommand c = commandsFactory.buildEvictFqnCommand(fqn);
c.setRecursive(recursive);
invoker.invoke(ctx, c);
}
public void evict(Fqn<?> fqn)
{
evict(fqn, false);
}
public V get(Fqn<?> fqn, K key)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GetKeyValueCommand command = commandsFactory.buildGetKeyValueCommand(fqn, key, true);
return (V) invoker.invoke(ctx, command);
}
public V get(String fqn, K key)
{
return get(Fqn.fromString(fqn), key);
}
public boolean removeNode(Fqn<?> fqn)
{
// 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(Fqn.fromElements(childName)))
{
ctx.setOptionOverrides(o);
result = removeNode(Fqn.fromRelativeElements(fqn, childName)) && result;
}
}
return result;
}
else
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GlobalTransaction tx = transactionTable.getCurrentTransaction();
RemoveNodeCommand command = commandsFactory.buildRemoveNodeCommand(tx, fqn);
Object retval = invoker.invoke(ctx, command);
return retval != null && (Boolean) retval;
}
}
public boolean removeNode(String fqn)
{
return removeNode(Fqn.fromString(fqn));
}
public NodeSPI<K, V> getNode(Fqn<?> fqn)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GetNodeCommand command = commandsFactory.buildGetNodeCommand(fqn);
return (NodeSPI) invoker.invoke(ctx, command);
}
public NodeSPI<K, V> getNode(String fqn)
{
return getNode(Fqn.fromString(fqn));
}
public V remove(Fqn<?> fqn, K key) throws CacheException
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GlobalTransaction tx = transactionTable.getCurrentTransaction();
RemoveKeyCommand command = commandsFactory.buildRemoveKeyCommand(tx, fqn, key);
return (V) invoker.invoke(ctx, command);
}
public V remove(String fqn, K key)
{
return remove(Fqn.fromString(fqn), key);
}
public void put(Fqn<?> fqn, Map<K, V> data)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
PutDataMapCommand command = commandsFactory.buildPutDataMapCommand(null, fqn, data);
invoker.invoke(ctx, command);
}
public void put(String fqn, Map<K, V> data)
{
put(Fqn.fromString(fqn), data);
}
public void putForExternalRead(Fqn<?> fqn, K key, V value)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
// 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);
PutForExternalReadCommand command = commandsFactory.buildPutForExternalReadCommand(null, fqn, key, value);
invoker.invoke(ctx, command);
}
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)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GlobalTransaction tx = transactionTable.getCurrentTransaction();
PutKeyValueCommand command = commandsFactory.buildPutKeyValueCommand(tx, fqn, key, value);
return (V) invoker.invoke(ctx, command);
}
public V put(String fqn, K key, V value)
{
return put(Fqn.fromString(fqn), key, value);
}
/**
* 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)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GetDataMapCommand command = commandsFactory.buildGetDataMapCommand(fqn);
return (Map<K, V>) invoker.invoke(ctx, command);
}
/**
* 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)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GetKeysCommand command = commandsFactory.buildGetKeysCommand(fqn);
return (Set<K>) invoker.invoke(ctx, command);
}
/**
* 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)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GlobalTransaction tx = getCurrentTransaction();
invoker.invoke(ctx, commandsFactory.buildClearDataCommand(tx, fqn));
}
/**
* 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)
{
InvocationContext ctx = invocationContextContainer.get();
cacheStatusCheck(ctx);
GetChildrenNamesCommand command = commandsFactory.buildGetChildrenNamesCommand(fqn);
Set<E> retval = (Set<E>) invoker.invoke(ctx, command);
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 DataContainer getDataContainer()
{
return dataContainer;
}
protected void cacheStatusCheck(InvocationContext ctx)
{
assertIsConstructed();
if (!ctx.getOptionOverrides().isSkipCacheStatusCheck() && !componentRegistry.invocationsAllowed(true))
{
throw new IllegalStateException("Cache not in STARTED state!");
}
}
}