package org.jboss.cache.invocation;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.buddyreplication.BuddyGroup;
import org.jboss.cache.buddyreplication.BuddyManager;
import org.jboss.cache.buddyreplication.BuddyNotInitException;
import org.jboss.cache.factories.annotations.Inject;
import org.jboss.cache.marshall.MethodCall;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
import org.jgroups.Address;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* A sublcass of CacheInvocationDelegate, used by remote instances to invoke methods on current cache.
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @since 2.1.0
*/
public class RemoteCacheInvocationDelegate extends CacheInvocationDelegate
{
private BuddyManager buddyManager;
public RemoteCacheInvocationDelegate()
{
originLocal = false;
log = LogFactory.getLog(RemoteCacheInvocationDelegate.class);
}
@Inject
private void injectBuddyManager(BuddyManager buddyManager)
{
this.buddyManager = buddyManager;
}
// ------------- start: buddy replication specific 'lifecycle' method calls
public void assignToBuddyGroup(BuddyGroup group, Map<Fqn, byte[]> state) throws Exception
{
if (buddyManager != null)
buddyManager.handleAssignToBuddyGroup(group, state);
else if (log.isWarnEnabled())
log.warn("Received assignToBuddyGroup call from group owner [" + group.getDataOwner() + "] but buddy replication is not enabled on this node!");
}
public void removeFromBuddyGroup(String groupName) throws BuddyNotInitException
{
if (buddyManager != null)
buddyManager.handleRemoveFromBuddyGroup(groupName);
else if (log.isWarnEnabled())
log.warn("Received removeFromBuddyGroup call for group name [" + groupName + "] but buddy replication is not enabled on this node!");
}
public void announceBuddyPoolName(Address address, String buddyPoolName)
{
if (buddyManager != null)
buddyManager.handlePoolNameBroadcast(address, buddyPoolName);
else if (log.isWarnEnabled())
log.warn("Received annouceBuddyPoolName call from [" + address + "] but buddy replication is not enabled on this node!");
}
public void dataGravitationCleanup(Fqn primary, Fqn backup) throws Exception
{
// MethodCall primaryDataCleanup, backupDataCleanup;
if (buddyManager.isDataGravitationRemoveOnFind())
{
if (log.isTraceEnabled())
log.trace("DataGravitationCleanup: Removing primary (" + primary + ") and backup (" + backup + ")");
getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
if (!removeNode(primary))
{
// only attempt to clean up the backup if the primary did not exist - a waste of a call otherwise.
getInvocationContext().getOptionOverrides().setCacheModeLocal(true);
removeNode(backup);
}
}
else
{
if (log.isTraceEnabled())
log.trace("DataGravitationCleanup: Evicting primary (" + primary + ") and backup (" + backup + ")");
//primaryDataCleanup = MethodCallFactory.create(MethodDeclarations.evictNodeMethodLocal, primary);
evict(primary, true);
//backupDataCleanup = MethodCallFactory.create(MethodDeclarations.evictNodeMethodLocal, backup);
evict(backup, true);
}
}
// ------------- end: buddy replication specific 'lifecycle' method calls
public Object _replicate(MethodCall methodCall) throws Throwable
{
try
{
Object retVal = invoke(methodCall);
// we only need to return values for a set of remote calls; not every call.
if (MethodDeclarations.returnValueForRemoteCall(methodCall.getMethodId()))
{
return retVal;
}
else
{
return null;
}
}
catch (Throwable ex)
{
if (methodCall.getMethodId() != MethodDeclarations.putForExternalReadMethodLocal_id
|| methodCall.getMethodId() != MethodDeclarations.putForExternalReadVersionedMethodLocal_id)
{
if (!MethodDeclarations.isBuddyGroupOrganisationMethod(methodCall.getMethodId()) && log.isWarnEnabled())
log.warn("replication failure with methodCall " + methodCall + " exception", ex);
throw ex;
}
else return null;
}
}
/**
* Replicates a list of method calls.
*/
public void _replicate(List<MethodCall> methodCalls) throws Throwable
{
for (MethodCall methodCall : methodCalls) _replicate((MethodCall) methodCall.getArgs()[0]);
}
public void block()
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.blockChannelMethodLocal_id);
invoke(m, true);
}
public void unblock()
{
MethodCall m = MethodCallFactory.create(MethodDeclarations.unblockChannelMethodLocal_id);
invoke(m, true);
}
/**
* A 'clustered get' call, called from a remote ClusteredCacheLoader.
*
* @return a List containing 2 elements: (true or false) and a value (Object). If buddy replication
* is used one further element is added - an Fqn of the backup subtree in which this node may be found.
*/
public List clusteredGet(MethodCall methodCall, Boolean searchBackupSubtrees)
{
if (log.isTraceEnabled())
log.trace("Clustered Get called with params: " + methodCall + ", " + searchBackupSubtrees);
Method m = methodCall.getMethod();
Object[] args = methodCall.getArgs();
Object callResults = null;
try
{
Fqn fqn = (Fqn) args[0];
if (log.isTraceEnabled()) log.trace("Clustered get: invoking call " + m + " with Fqn " + fqn);
callResults = m.invoke(cache, args);
boolean found = validResult(callResults, methodCall, fqn);
if (log.isTraceEnabled()) log.trace("Got result " + callResults + ", found=" + found);
if (found && callResults == null) callResults = createEmptyResults(methodCall);
}
catch (Exception e)
{
log.warn("Problems processing clusteredGet call", e);
}
List<Object> results = new ArrayList<Object>(2);
if (callResults != null)
{
results.add(true);
results.add(callResults);
}
else
{
results.add(false);
results.add(null);
}
return results;
}
/**
* Returns true if the call results returned a valid result.
*/
private boolean validResult(Object callResults, MethodCall mc, Fqn fqn)
{
switch (mc.getMethodId())
{
case MethodDeclarations.getDataMapMethodLocal_id:
case MethodDeclarations.getChildrenNamesMethodLocal_id:
return callResults != null || exists(fqn);
case MethodDeclarations.existsMethod_id:
return (Boolean) callResults;
default:
return false;
}
}
/**
* Creates an empty Collection class based on the return type of the method called.
*/
private Object createEmptyResults(MethodCall mc)
{
switch (mc.getMethodId())
{
case MethodDeclarations.getDataMapMethodLocal_id:
case MethodDeclarations.getChildrenNamesMethodLocal_id:
return Collections.emptyMap();
default:
return null;
}
}
}