package jadex.base.service.remote;
import jadex.base.service.remote.commands.RemoteDGCAddReferenceCommand;
import jadex.base.service.remote.commands.RemoteDGCRemoveReferenceCommand;
import jadex.base.service.remote.replacements.DefaultEqualsMethodReplacement;
import jadex.base.service.remote.replacements.DefaultHashcodeMethodReplacement;
import jadex.bridge.IComponentIdentifier;
import jadex.bridge.IComponentManagementService;
import jadex.bridge.IComponentStep;
import jadex.bridge.IExternalAccess;
import jadex.bridge.IInternalAccess;
import jadex.commons.Future;
import jadex.commons.IFuture;
import jadex.commons.SReflect;
import jadex.commons.SUtil;
import jadex.commons.collection.LRU;
import jadex.commons.concurrent.IResultListener;
import jadex.commons.service.IService;
import jadex.commons.service.IServiceIdentifier;
import jadex.commons.service.SServiceProvider;
import jadex.commons.service.clock.IClockService;
import jadex.commons.service.library.ILibraryService;
import jadex.micro.ExternalAccess;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* This class implements the rmi handling. It mainly supports:
* - remote reference management
* - creation of proxy references for transferring IProxyable objects
* - creation of proxies on the remote side of a target object
* - distributed garbage collection for target (remote) objects using reference counting
* - management of interfaceproperties for metadata such as exclusion or replacement of methods
*/
public class RemoteReferenceModule
{
/** Debug flag. */
public static boolean DEBUG = false;
/** The default lease time. */
public static long DEFAULT_LEASETIME = 15000;
/** leasetime*factor is used to determine when an entry should be removed. */
public static double WAITFACTOR = 2.2;
//-------- attributes --------
/** The remote interface properties. */
protected Map interfaceproperties;
/** The remote management service. */
protected RemoteServiceManagementService rsms;
/** The cache of proxy infos (class -> proxy info). */
protected Map proxyinfos;
/** The map of target objects (rr -> target object). */
protected Map targetobjects;
/** The inverse map of target object to remote references (target objects -> rr). */
protected Map remoterefs;
/** The id counter. */
protected long idcnt;
/** The proxycount count map. (rr -> number of proxies created for rr). */
protected Map proxycount;
protected Map proxydates;
/** The remote reference holders of a object (rr -> holder (rms cid)). */
protected Map holders;
/** The clock. */
protected IClockService clock;
/** The library service. */
protected ILibraryService libservice;
/** The renew behaviour id. */
protected long renewid;
/** The remove behaviour id. */
protected long removeid;
//-------- constructors --------
/**
* Create a new remote reference module.
*/
public RemoteReferenceModule(RemoteServiceManagementService rsms, IClockService clock, ILibraryService libservice)
{
this.rsms = rsms;
this.clock = clock;
this.libservice = libservice;
this.interfaceproperties = new HashMap();
this.proxyinfos = new LRU(200);
this.targetobjects = new HashMap();
this.remoterefs = new HashMap();
this.proxycount = new HashMap();
this.proxydates = new TreeMap();
this.holders = new HashMap();
}
//-------- methods --------
/**
* Get a remote reference for a component for transport.
* (Called during marshalling from writer).
*/
public ProxyReference getProxyReference(Object target, Class[] remoteinterfaces, IComponentIdentifier tmpholder)
{
checkThread();
// todo: should all ids of remote objects be saved in table?
// Note: currently agents use model information e.g. componentviewer.viewerclass
// to add specific properties, so that proxies are cached per agent model type due
// to cached method call getPropertyMap().
RemoteReference rr = getRemoteReference(target);
// Remember that this rr is send to some other process (until the addRef message arrives).
if(rr.isObjectReference())
addTemporaryRemoteReference(rr, tmpholder);
// This construct ensures
// a) fast access to existing proxyinfos in the map
// b) creation is performed only once by ordering threads
// via synchronized block and rechecking if proxy was already created.
// -> not necessary due to only single threaded access via agent thread
Object tcid = target instanceof IExternalAccess? (Object)((IExternalAccess)target).getModel().getFullName(): target.getClass();
ProxyInfo pi = (ProxyInfo)proxyinfos.get(tcid);
if(pi==null)
{
pi = createProxyInfo(target, remoteinterfaces);
proxyinfos.put(tcid, pi);
// System.out.println("add: "+tcid+" "+pi);
}
ProxyReference ret = new ProxyReference(pi, rr);
// Check interface methods and possibly cache constant calls.
for(int i=0; i<remoteinterfaces.length; i++)
{
Method[] methods = remoteinterfaces[i].getMethods();
for(int j=0; j<methods.length; j++)
{
addCachedMethodValue(ret, pi, methods[j], target);
}
}
// Check object methods and possibly cache constant calls.
Method[] methods = Object.class.getMethods();
for(int i=0; i<methods.length; i++)
{
addCachedMethodValue(ret, pi, methods[i], target);
}
return ret;
}
/**
* Create a proxy info for a service.
*/
protected ProxyInfo createProxyInfo(Object target, Class[] remoteinterfaces)
{
checkThread();
// todo: dgc, i.e. remember that target is a remote object (for which a proxyinfo is sent away).
ProxyInfo ret = new ProxyInfo(remoteinterfaces);
Map properties = null;
// Hack! as long as registry is not there
if(target instanceof IExternalAccess)
{
properties = ((IExternalAccess)target).getModel().getProperties();
}
else if(properties==null && target instanceof IService)
{
properties = ((IService)target).getPropertyMap();
}
for(int i=0; i<remoteinterfaces.length+1; i++)
{
if(i>0)
properties = (Map)interfaceproperties.get(remoteinterfaces[i-1]);
Class targetclass = target.getClass();
// Check for excluded and synchronous methods.
if(properties!=null)
{
Object ex = properties.get(RemoteServiceManagementService.REMOTE_EXCLUDED);
if(ex!=null)
{
for(Iterator it = SReflect.getIterator(ex); it.hasNext(); )
{
MethodInfo[] mis = getMethodInfo(it.next(), targetclass, false);
for(int j=0; j<mis.length; j++)
{
ret.addExcludedMethod(mis[j]);
}
}
}
Object syn = properties.get(RemoteServiceManagementService.REMOTE_SYNCHRONOUS);
if(syn!=null)
{
for(Iterator it = SReflect.getIterator(syn); it.hasNext(); )
{
MethodInfo[] mis = getMethodInfo(it.next(), targetclass, false);
for(int j=0; j<mis.length; j++)
{
ret.addSynchronousMethod(mis[j]);
}
}
}
Object un = properties.get(RemoteServiceManagementService.REMOTE_UNCACHED);
if(un!=null)
{
for(Iterator it = SReflect.getIterator(un); it.hasNext(); )
{
MethodInfo[] mis = getMethodInfo(it.next(), targetclass, false);
for(int j=0; j<mis.length; j++)
{
ret.addUncachedMethod(mis[j]);
}
}
}
Object mr = properties.get(RemoteServiceManagementService.REMOTE_METHODREPLACEMENT);
if(mr!=null)
{
for(Iterator it = SReflect.getIterator(mr); it.hasNext(); )
{
Object[] tmp = (Object[])it.next();
MethodInfo[] mis = getMethodInfo(tmp[0], targetclass, false);
for(int j=0; j<mis.length; j++)
{
ret.addMethodReplacement(mis[j], (IMethodReplacement)tmp[1]);
}
}
}
}
}
// Add default replacement for equals() and hashCode().
Class targetclass = target.getClass();
Method equals = SReflect.getMethod(Object.class, "equals", new Class[]{Object.class});
if(ret.getMethodReplacement(equals)==null)
{
MethodInfo[] mis = getMethodInfo(equals, targetclass, false);
for(int i=0; i<mis.length; i++)
{
ret.addMethodReplacement(mis[i], new DefaultEqualsMethodReplacement());
}
}
Method hashcode = SReflect.getMethod(Object.class, "hashCode", new Class[0]);
if(ret.getMethodReplacement(hashcode)==null)
{
MethodInfo[] mis = getMethodInfo(hashcode, targetclass, true);
for(int i=0; i<mis.length; i++)
{
ret.addMethodReplacement(mis[i], new DefaultHashcodeMethodReplacement());
}
}
// Add getClass as excluded. Otherwise the target class must be present on
// the computer which only uses the proxy.
Method getclass = SReflect.getMethod(Object.class, "getClass", new Class[0]);
if(ret.getMethodReplacement(getclass)==null)
{
ret.addExcludedMethod(new MethodInfo(getclass));
}
return ret;
}
/**
* Add a cached method value to the proxy info.
*/
public static void addCachedMethodValue(ProxyReference pr, ProxyInfo pi, Method m, Object target)
{
// only cache when not excluded, not cached and not replaced
if(!pi.isUncached(m) && !pi.isExcluded(m) && !pi.isReplaced(m))
{
Class rt = m.getReturnType();
Class[] ar = m.getParameterTypes();
if(void.class.equals(rt))
{
// System.out.println("Warning, void method call will be executed asynchronously: "+type+" "+methods[i].getName());
}
else if(!(rt.isAssignableFrom(IFuture.class)))
{
if(ar.length>0)
{
// System.out.println("Warning, service method is blocking: "+type+" "+methods[i].getName());
}
else
{
// Invoke method to get constant return value.
try
{
// System.out.println("Calling for caching: "+m);
Object val = m.invoke(target, new Object[0]);
pr.putCache(m.getName(), val);
}
catch(Exception e)
{
System.err.println("Warning, constant service method threw exception: "+m);
e.printStackTrace();
}
}
}
}
}
/**
* Get method info.
*/
public static MethodInfo[] getMethodInfo(Object iden, Class targetclass, boolean noargs)
{
MethodInfo[] ret;
if(iden instanceof String)
{
if(noargs)
{
Method method = SReflect.getMethod(targetclass, (String)iden, new Class[0]);
if(method==null)
method = SReflect.getMethod(Object.class, (String)iden, new Class[0]);
if(method!=null)
{
ret = new MethodInfo[]{new MethodInfo(method)};
}
else
{
throw new RuntimeException("Method not found: "+iden);
}
}
else
{
Method[] ms = SReflect.getMethods(targetclass, (String)iden);
if(ms.length==0)
{
ms = SReflect.getMethods(Object.class, (String)iden);
}
if(ms.length==1)
{
ret = new MethodInfo[]{new MethodInfo(ms[0])};
}
else if(ms.length>1)
{
// Exclude all if more than one fits?!
ret = new MethodInfo[ms.length];
for(int i=0; i<ret.length; i++)
ret[i] = new MethodInfo(ms[i]);
// Check if the methods are equal = same signature (e.g. defined in different interfaces)
// boolean eq = true;
// Method m0 = ms[0];
// for(int i=1; i<ms.length && eq; i++)
// {
// if(!hasEqualSignature(m0, ms[i]))
// eq = false;
// }
// if(!eq)
// throw new RuntimeException("More than one method with the name availble: "+tmp);
// else
// ret = new MethodInfo(m0);
}
else
{
throw new RuntimeException("Method not found: "+iden);
}
}
}
else
{
ret = new MethodInfo[]{new MethodInfo((Method)iden)};
}
return ret;
}
/**
* Get a remote reference.
* @param target The (local) remote object.
*/
protected RemoteReference getRemoteReference(Object target)
{
checkThread();
RemoteReference ret = (RemoteReference)remoterefs.get(target);
// Create a remote reference if not yet available.
if(ret==null)
{
if(target instanceof IExternalAccess)
{
ret = new RemoteReference(rsms.getRMSComponentIdentifier(), ((IExternalAccess)target).getComponentIdentifier());
}
else if(target instanceof IService)
{
ret = new RemoteReference(rsms.getRMSComponentIdentifier(), ((IService)target).getServiceIdentifier());
}
else
{
ret = generateRemoteReference();
// System.out.println("Adding rr: "+ret+" "+target);
remoterefs.put(target, ret);
targetobjects.put(ret, target);
}
}
return ret;
}
/**
* Delete a remote reference.
* @param rr The remote reference.
*/
protected void deleteRemoteReference(RemoteReference rr)
{
checkThread();
Object target = targetobjects.remove(rr);
remoterefs.remove(target);
// System.out.println("Removing rr: "+rr+" "+target);
}
/**
* Shutdown the module.
* Sends notifications to all
*/
protected void shutdown()
{
checkThread();
RemoteReference[] rrs = (RemoteReference[])proxycount.keySet().toArray(new RemoteReference[0]);
for(int i=0; i<rrs.length; i++)
{
sendRemoveRemoteReference(rrs[i]);
}
}
/**
* Get a target object per remote reference.
* @param rr The remote reference.
* @return The target object.
*/
public IFuture getTargetObject(RemoteReference rr)
{
checkThread();
final Future ret = new Future();
if(rr.getTargetIdentifier() instanceof IServiceIdentifier)
{
IServiceIdentifier sid = (IServiceIdentifier)rr.getTargetIdentifier();
// fetch service via its id
SServiceProvider.getService(rsms.getComponent().getServiceProvider(), sid)
.addResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
ret.setResult(result);
}
public void exceptionOccurred(Object source, Exception exception)
{
ret.setException(exception);
}
});
}
else if(rr.getTargetIdentifier() instanceof IComponentIdentifier)
{
final IComponentIdentifier cid = (IComponentIdentifier)rr.getTargetIdentifier();
// fetch component via target component id
SServiceProvider.getServiceUpwards(rsms.getComponent().getServiceProvider(), IComponentManagementService.class)
.addResultListener(new IResultListener()
// .addResultListener(component.createResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
IComponentManagementService cms = (IComponentManagementService)result;
// fetch target component via component identifier.
cms.getExternalAccess(cid).addResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
ret.setResult(result);
}
public void exceptionOccurred(Object source, Exception exception)
{
ret.setException(exception);
}
});
}
public void exceptionOccurred(Object source, Exception exception)
{
ret.setException(exception);
}
});
}
else //(rr.getTargetIdentifier() instanceof String)
{
Object o = targetobjects.get(rr);
if(o!=null)
{
ret.setResult(o);
}
else
{
ret.setException(new RuntimeException("Remote object not found: "+rr));
}
}
return ret;
}
/**
* Remove a target object.
* @param rr The remote reference.
* @return The target object.
*/
protected Object removeTargetObject(RemoteReference rr)
{
checkThread();
return targetobjects.remove(rr);
}
/**
* Generate a remote reference.
* @return The remote reference.
*/
protected RemoteReference generateRemoteReference()
{
checkThread();
return new RemoteReference(rsms.getRMSComponentIdentifier(), ""+idcnt++);
}
//-------- management of proxies --------
/**
* Get a proxy for a proxy reference.
* @param pr The proxy reference.
*/
public Object getProxy(ProxyReference pr)
{
checkThread();
Object ret;
// RemoteReference rr = pi.getRemoteReference();
// Is is local return local target object.
if(pr.getRemoteReference().getRemoteManagementServiceIdentifier().equals(rsms.getRMSComponentIdentifier()))
{
ret = targetobjects.get(pr.getRemoteReference());
}
// Else return new or old proxy.
else
{
// System.out.println("interfaces of proxy: "+SUtil.arrayToString(pi.getTargetInterfaces()));
Class[] tmp = pr.getProxyInfo().getTargetInterfaces();
Class[] interfaces = new Class[tmp.length+1];
System.arraycopy(tmp, 0, interfaces, 0, tmp.length);
interfaces[tmp.length] = IFinalize.class;
ret = Proxy.newProxyInstance(libservice.getClassLoader(),
interfaces, new RemoteMethodInvocationHandler(rsms, pr));
incProxyCount(pr.getRemoteReference());
// ret = proxies.get(rr);
// if(ret==null)
// {
// synchronized(this)
// {
// ret = proxies.get(rr);
// if(ret==null)
// {
// ret = Proxy.newProxyInstance(rsms.getComponent().getModel().getClassLoader(),
// pi.getTargetInterfaces(), new RemoteMethodInvocationHandler(rsms, pi));
// proxies.put(rr, ret);
//
// sendAddRemoteReference(rr);
// }
// }
// }
}
return ret;
}
//-------- dgc --------
/**
* Increment the proxy count for a remote reference.
* @param rr The remote reference for the proxy.
*/
protected void incProxyCount(RemoteReference rr)
{
if(DEBUG)
{
if(proxycount.size()!=proxydates.size())
System.out.println("ipc start");
}
checkThread();
// Only keep track of proxies for java objects.
// Components and services are not subject of gc.
if(rr.isObjectReference())
{
boolean notify = false;
// RemoteReference origrr = (RemoteReference)proxycountkeys.get(rr);
Integer cnt = (Integer)proxycount.remove(rr);
if(cnt==null)
{
// proxycountkeys.put(rr, rr);
proxycount.put(rr, new Integer(1));
notify = true;
// todo: transfer lease time interval?!
// rr.setExpiryDate(clock.getTime()+DEFAULT_LEASETIME);
proxydates.put(new Long(clock.getTime()+DEFAULT_LEASETIME), rr);
// Initiate check procedure.
startRenewalBehaviour();
}
else
{
proxycount.put(rr, new Integer(cnt.intValue()+1));
}
// System.out.println("Add proxy: "+rr+" "+cnt);
if(notify)
sendAddRemoteReference(rr);
if(DEBUG)
{
if(proxycount.size()!=proxydates.size())
System.out.println("ipc end");
}
}
}
/**
* Decrease the proxy count for a remote reference.
* @param rr The remote reference for the proxy.
*/
protected void decProxyCount(RemoteReference rr)
{
if(DEBUG)
{
if(proxycount.size()!=proxydates.size())
System.out.println("dpc start");
}
checkThread();
// Only keep track of proxies for java objects.
// Components and services are not subject of gc.
if(rr.isObjectReference())
{
boolean notify = false;
// RemoteReference origrr = (RemoteReference)proxycountkeys.remove(rr);
Integer cnt = (Integer)proxycount.remove(rr);
int nv = cnt.intValue()-1;
if(nv==0)
{
notify = true;
proxydates.values().remove(rr);
// System.out.println("Remove proxy: "+rr+" "+nv);
}
else
{
// proxycountkeys.put(rr, rr);
proxycount.put(rr, new Integer(nv));
}
// System.out.println("Remove proxy: "+rr+" "+nv+" "+proxycount);
if(notify)
sendRemoveRemoteReference(rr);
}
if(DEBUG)
{
if(proxycount.size()!=proxydates.size())
System.out.println("dpc end");
}
}
/**
* Start the removal behavior.
*/
protected void startRenewalBehaviour()
{
final long renewid = ++this.renewid;
rsms.getComponent().scheduleStep(new IComponentStep()
{
public Object execute(IInternalAccess ia)
{
if(renewid == RemoteReferenceModule.this.renewid)
{
final RemoteServiceManagementAgent agent = (RemoteServiceManagementAgent)ia;
if(DEBUG)
{
if(proxycount.size()!=proxydates.size())
System.out.println("srb start");
}
// System.out.println("Starting renewal behavior: "+removeid);
// if(proxydates.size()>0)
// {
// System.out.println("Checking proxies: "+proxydates.size()+" "+proxycount.size());
// for(Iterator it=proxydates.keySet().iterator(); it.hasNext(); )
// {
// Long key = (Long)it.next();
// System.out.println("\t "+key+" "+" "+System.currentTimeMillis()+" "+proxydates.get(key));
// }
// }
long diff = 0;
Long[] dates = (Long[])proxydates.keySet().toArray(new Long[proxydates.size()]);
for(int i=0; i<dates.length; i++)
{
diff = dates[i].longValue()-clock.getTime();
if(diff<=0)
{
final RemoteReference rr = (RemoteReference)proxydates.remove(dates[i]);
// System.out.println("renewal sent for: "+rr);
IResultListener lis = agent.createResultListener(new IResultListener()
{
public void resultAvailable(Object source, Object result)
{
if(DEBUG)
System.out.println("Renewed successfully lease for: "+rr);
}
public void exceptionOccurred(Object source, Exception exception)
{
if(DEBUG)
System.out.println("Failed to renew lease for: "+rr);
}
});
sendAddRemoteReference(rr).addResultListener(lis);
long expirydate = clock.getTime()+DEFAULT_LEASETIME;
proxydates.put(new Long(expirydate), rr);
diff = DEFAULT_LEASETIME;
}
else
{
break;
}
}
// System.out.println("prxy: "+proxycount);
if(DEBUG)
{
if(proxycount.size()!=proxydates.size())
System.out.println("srb end");
}
if(proxycount.size()>0 && diff>0)
{
// System.out.println("renewal behaviour waiting: "+diff);
rsms.getComponent().waitFor(diff, this);
}
}
// System.out.println("renewal behaviour exit");
return null;
}
});
}
/**
* Start removal behavior for expired holders.
*/
protected void startRemovalBehaviour()
{
final long removeid = ++this.removeid;
rsms.getComponent().scheduleStep(new IComponentStep()
{
public Object execute(IInternalAccess ia)
{
if(removeid == RemoteReferenceModule.this.removeid)
{
// System.out.println("Starting removal behavior: "+removeid);
// if(holders.size()>0)
// {
// System.out.println("Checking holders: ");
// for(Iterator it=holders.keySet().iterator(); it.hasNext(); )
// {
// Object key = it.next();
// System.out.println("\t "+key+" "+((Map)holders.get(key)).keySet());
// }
// }
for(Iterator it=holders.keySet().iterator(); it.hasNext(); )
{
RemoteReference rr = (RemoteReference)it.next();
Map hds = (Map)holders.get(rr);
for(Iterator it2=hds.keySet().iterator(); it2.hasNext(); )
{
RemoteReferenceHolder rrh = (RemoteReferenceHolder)it2.next();
if(clock.getTime() > rrh.getExpiryDate()+DEFAULT_LEASETIME*WAITFACTOR)
{
if(DEBUG)
System.out.println("Removing expired holder: "+rr+" "+rrh+" "+rrh.getExpiryDate()+" "+System.currentTimeMillis());
it2.remove();
// hds.remove(rrh);
if(hds.size()==0)
{
it.remove();
// holders.remove(rr);
deleteRemoteReference(rr);
}
}
}
}
if(holders.size()>0)
rsms.getComponent().waitFor(5000, this);
}
return null;
}
});
}
/**
* Send addRef to the origin process of the remote reference.
* @param rr The remote reference.
*/
protected Future sendAddRemoteReference(final RemoteReference rr)
{
checkThread();
// DGC: notify rr origin that a new proxy of target object exists
// todo: handle failures!
Future future = new Future();
// System.out.println("send add: "+rr);
final String callid = SUtil.createUniqueId(rsms.getRMSComponentIdentifier().getLocalName());
RemoteDGCAddReferenceCommand com = new RemoteDGCAddReferenceCommand(rr, rsms.getRMSComponentIdentifier(), callid);
rsms.sendMessage(rr.getRemoteManagementServiceIdentifier(), com, callid, -1, future);
return future;
}
/**
* Send removeRef to the origin process of the remote reference.
* @param rr The remote reference.
*/
public Future sendRemoveRemoteReference(RemoteReference rr)
{
checkThread();
// DGC: notify rr origin that a new proxy of target object exists
// todo: handle failures!
Future future = new Future();
// System.out.println("send rem: "+rr);
final String callid = SUtil.createUniqueId(rsms.getRMSComponentIdentifier().getLocalName());
RemoteDGCRemoveReferenceCommand com = new RemoteDGCRemoveReferenceCommand(rr, rsms.getRMSComponentIdentifier(), callid);
rsms.sendMessage(rr.getRemoteManagementServiceIdentifier(), com, callid, -1, future);
return future;
}
/**
* Add a new temporary holder to a remote object.
* @param rr The remote reference.
* @param holder The cid of the holding rms.
*/
protected void addTemporaryRemoteReference(final RemoteReference rr, final IComponentIdentifier holder)
{
checkThread();
Map hds = (Map)holders.get(rr);
if(hds==null)
{
hds = new HashMap();
holders.put(rr, hds);
startRemovalBehaviour();
}
long expirydate = clock.getTime()+DEFAULT_LEASETIME;
TemporaryRemoteReferenceHolder newth = new TemporaryRemoteReferenceHolder(holder, expirydate);
TemporaryRemoteReferenceHolder oldth = (TemporaryRemoteReferenceHolder)hds.get(newth);
if(oldth==null)
{
hds.put(newth, newth);
}
else
{
// Update existing holder.
oldth.setNumber(oldth.getNumber()+1);
oldth.setExpiryDate(expirydate);
}
// System.out.println("Holders for (temp add): "+rr+" add: "+holder+" "+hds.keySet());
}
/**
* Add a new holder to a remote object.
* @param rr The remote reference.
* @param holder The cid of the holding rms.
*/
public void addRemoteReference(final RemoteReference rr, final IComponentIdentifier holder)
{
checkThread();
Map hds = (Map)holders.get(rr);
if(hds==null)
{
hds = new HashMap();
holders.put(rr, hds);
startRemovalBehaviour();
}
long expirydate = clock.getTime()+DEFAULT_LEASETIME;
RemoteReferenceHolder newh = new RemoteReferenceHolder(holder, expirydate);
RemoteReferenceHolder oldh = (RemoteReferenceHolder)hds.get(newh);
if(oldh==null)
{
// throw new RuntimeException("Holder already contained: "+holder);
hds.put(newh, newh);
}
else
{
// Renew expiry date of existing holder.
oldh.setExpiryDate(expirydate);
if(DEBUG)
System.out.println("renewed lease for: "+rr+" "+oldh);
}
// Decrement number (and possibly remove) temporary holder.
TemporaryRemoteReferenceHolder th = (TemporaryRemoteReferenceHolder)hds.get(
new TemporaryRemoteReferenceHolder(holder, 0));
if(th!=null)
{
th.setNumber(th.getNumber()-1);
if(th.getNumber()==0)
{
hds.remove(th); // hds.size() != 0
}
}
// System.out.println("Holders for (add): "+rr+" add: "+holder+" "+hds.keySet());
}
/**
* Remove a new holder from a remote object.
* @param rr The remote reference.
* @param holder The cid of the holding rms.
*/
public void removeRemoteReference(final RemoteReference rr, final IComponentIdentifier holder)
{
checkThread();
Map hds = (Map)holders.get(rr);
// if(hds==null || !hds.contains(holder))
// throw new RuntimeException("Holder not contained: "+holder);
// System.out.println("Holders for (rem): "+result+" rem: "+holder+" "+hds);
if(hds!=null)
{
hds.remove(new RemoteReferenceHolder(holder, 0));
if(hds.size()==0)
{
holders.remove(rr);
deleteRemoteReference(rr);
}
}
}
/**
* Check if correct thread access.
*/
protected void checkThread()
{
if(DEBUG)
{
if(((ExternalAccess)rsms.getComponent()).getInterpreter().isExternalThread())
{
System.out.println("wrong thread: "+Thread.currentThread());
Thread.dumpStack();
}
}
}
// Code for retrying a command
// rsms.getComponent().scheduleStep(new ICommand()
// {
// public void execute(Object args)
// {
// final RemoteServiceManagementAgent agent = (RemoteServiceManagementAgent)args;
// final int[] retrycnt = new int[1];
// IResultListener lis = agent.createResultListener(new IResultListener()
// {
// public void resultAvailable(Object source, Object result)
// {
// long expirydate = clock.getTime()+DEFAULT_LEASETIME;
// proxydates.put(new Long(expirydate), rr);
// }
//
// public void exceptionOccurred(Object source, Exception exception)
// {
// // retry 2 times
// if(retrycnt[0]<2)
// sendAddRemoteReference(rr).addResultListener(this);
// else
// System.out.println("Failed to renew lease for: "+rr);
// retrycnt[0]++;
// }
// });
// }
// });
}