package jadex.base.service.remote; import jadex.base.fipa.SFipa; import jadex.base.service.remote.commands.RemoteGetExternalAccessCommand; import jadex.base.service.remote.commands.RemoteSearchCommand; import jadex.base.service.remote.xml.RMIPostProcessor; import jadex.base.service.remote.xml.RMIPreProcessor; import jadex.bridge.IComponentIdentifier; import jadex.bridge.IComponentManagementService; import jadex.bridge.IComponentStep; import jadex.bridge.IInternalAccess; import jadex.bridge.IMessageService; import jadex.bridge.IRemoteServiceManagementService; import jadex.commons.Future; import jadex.commons.IFuture; import jadex.commons.IRemotable; import jadex.commons.SUtil; import jadex.commons.concurrent.DefaultResultListener; import jadex.commons.concurrent.DelegationResultListener; import jadex.commons.concurrent.IResultListener; import jadex.commons.service.AnyResultSelector; import jadex.commons.service.BasicService; import jadex.commons.service.IResultSelector; import jadex.commons.service.ISearchManager; import jadex.commons.service.IVisitDecider; import jadex.commons.service.SServiceProvider; import jadex.commons.service.TypeResultSelector; import jadex.commons.service.clock.IClockService; import jadex.commons.service.clock.ITimer; import jadex.commons.service.library.ILibraryService; import jadex.micro.IMicroExternalAccess; import jadex.micro.MicroAgent; import jadex.xml.ObjectInfo; import jadex.xml.SXML; import jadex.xml.TypeInfo; import jadex.xml.XMLInfo; import jadex.xml.bean.BeanObjectReaderHandler; import jadex.xml.bean.BeanObjectWriterHandler; import jadex.xml.bean.JavaReader; import jadex.xml.bean.JavaWriter; import jadex.xml.reader.Reader; import jadex.xml.writer.Writer; import java.util.HashMap; import java.util.Map; import java.util.Set; import javax.xml.namespace.QName; /** * The remote service management service is responsible for * handling remote service invocations (similar to RMI). */ public class RemoteServiceManagementService extends BasicService implements IRemoteServiceManagementService { //-------- constants -------- /** Excluded remote methods (for all methods) * Excluded methods throw UnsupportedOperationException. */ public static String REMOTE_EXCLUDED = "remote_excluded"; /** Uncached remote methods (for methods with no parameters) * Uncached methods will be invoked on every call. */ public static String REMOTE_UNCACHED = "remote_uncached"; /** Synchronous remote methods (for methods with void return value). * If void methods are declared synchronous they will block the caller until * the method has been executed on the remote side (exception thus can arrive). */ public static String REMOTE_SYNCHRONOUS = "remote_synchronous"; /** Replacement methods to be executed instead of remote method invocation. */ public static String REMOTE_METHODREPLACEMENT = "remote_methodreplacement"; /** The default timeout. */ public static long DEFAULT_TIMEOUT = 10000; //-------- attributes -------- /** The component. */ protected IMicroExternalAccess component; /** The map of waiting calls (callid -> future). */ protected Map waitingcalls; /** The remote reference module. */ protected RemoteReferenceModule rrm; /** The rmi object to xml writer. */ protected Writer writer; /** The rmi xml to object reader. */ protected Reader reader; //-------- constructors -------- /** * Create a new remote service management service. */ public RemoteServiceManagementService(IMicroExternalAccess component, IClockService clock, ILibraryService libservice) { super(component.getServiceProvider().getId(), IRemoteServiceManagementService.class, null); this.component = component; this.rrm = new RemoteReferenceModule(this, clock, libservice); this.waitingcalls = new HashMap(); QName[] pr = new QName[]{new QName(SXML.PROTOCOL_TYPEINFO+"jadex.base.service.remote", "ProxyReference")}; Set typeinfosread = JavaReader.getTypeInfos(); TypeInfo ti_rr = new TypeInfo(new XMLInfo(pr), new ObjectInfo(ProxyReference.class, new RMIPostProcessor(rrm))); typeinfosread.add(ti_rr); Set typeinfoswrite = JavaWriter.getTypeInfos(); TypeInfo ti_proxyable = new TypeInfo(new XMLInfo(pr, null, false, new RMIPreProcessor(rrm)), new ObjectInfo(IRemotable.class)); typeinfoswrite.add(ti_proxyable); this.reader = new Reader(new BeanObjectReaderHandler(typeinfosread)); this.writer = new Writer(new BeanObjectWriterHandler(typeinfoswrite, true)); } //-------- methods -------- /** * Get a service proxies from a remote platform. * (called from arbitrary components) * @param cid Component id that is used to start the search. * @param manager The search manager. * @param decider The visit decider. * @param selector The result selector. * @return Collection or single result (i.e. service proxies). */ public IFuture getServiceProxies(final IComponentIdentifier cid, final ISearchManager manager, final IVisitDecider decider, final IResultSelector selector) { Future ret = new Future(); component.scheduleStep(new IComponentStep() { public Object execute(IInternalAccess ia) { RemoteServiceManagementAgent agent = (RemoteServiceManagementAgent)ia; final Future fut = new Future(); SServiceProvider.getService(component.getServiceProvider(), IComponentManagementService.class) .addResultListener(agent.createResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { IComponentManagementService cms = (IComponentManagementService)result; // Hack! create remote rms cid with "rms" assumption. IComponentIdentifier rrms = cms.createComponentIdentifier("rms@"+cid.getPlatformName(), false, cid.getAddresses()); final String callid = SUtil.createUniqueId(component.getComponentIdentifier().getLocalName()); RemoteSearchCommand content = new RemoteSearchCommand(cid, manager, decider, selector, callid); sendMessage(rrms, content, callid, -1, fut); } public void exceptionOccurred(Object source, Exception exception) { fut.setException(exception); } })); return fut; } }).addResultListener(new DelegationResultListener(ret)); return ret; } /** * Get a service proxy from a remote platform. * (called from arbitrary components) * @param platform The component id of the remote platform. * @param providerid Optional component id that is used to start the search. * @param service The service type. * @return The service proxy. */ public IFuture getServiceProxy(IComponentIdentifier cid, final Class service) { return getServiceProxies(cid, SServiceProvider.sequentialmanager, SServiceProvider.abortdecider, new TypeResultSelector(service, true)); } /** * Get all service proxies from a remote platform. * (called from arbitrary components) * @param platform The component id of the remote platform. * @param providerid Optional component id that is used to start the search. * @param service The service type. * @return The service proxy. */ public IFuture getServiceProxies(IComponentIdentifier cid, final Class service) { return getServiceProxies(cid, SServiceProvider.sequentialmanager, SServiceProvider.contdecider, new TypeResultSelector(service, true)); } /** * Get all declared service proxies from a remote component. * (called from arbitrary components) * @param cid The remote provider id. * @param service The service type. * @return The service proxy. */ public IFuture getDeclaredServiceProxies(IComponentIdentifier cid) { return getServiceProxies(cid, SServiceProvider.localmanager, SServiceProvider.contdecider, new AnyResultSelector(false, false)); } /** * Get an external access proxy from a remote component. * (called from arbitrary components) * @param cid Component target id. * @return External access of remote component. */ public IFuture getExternalAccessProxy(final IComponentIdentifier cid) { final Future ret = new Future(); component.scheduleStep(new IComponentStep() { public Object execute(IInternalAccess ia) { final Future fut = new Future(); RemoteServiceManagementAgent agent = (RemoteServiceManagementAgent)ia; SServiceProvider.getService(component.getServiceProvider(), IComponentManagementService.class) .addResultListener(agent.createResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { IComponentManagementService cms = (IComponentManagementService)result; // Hack! create remote rms cid with "rms" assumption. IComponentIdentifier rrms = cms.createComponentIdentifier("rms@"+cid.getPlatformName(), false, cid.getAddresses()); final String callid = SUtil.createUniqueId(component.getComponentIdentifier().getLocalName()); RemoteGetExternalAccessCommand content = new RemoteGetExternalAccessCommand(cid, callid); sendMessage(rrms, content, callid, -1, fut); } public void exceptionOccurred(Object source, Exception exception) { fut.setException(exception); } })); return fut; } }).addResultListener(new DelegationResultListener(ret)); return ret; } /** * Get the component. * @return the component. */ public IMicroExternalAccess getComponent() { return component; } /** * Get the rms component identifier. * @return The rms component identifier. */ public IComponentIdentifier getRMSComponentIdentifier() { return component.getComponentIdentifier(); } /** * Get the waiting calls. * @return the waiting calls. * / public Map getWaitingCalls() { return waitingcalls; }*/ /** * Get the reader. * @return The reader. */ public Reader getReader() { return reader; } /** * Get the writer. * @return the writer. */ public Writer getWriter() { return writer; } /** * Add a new waiting call. * @param callid The callid. * @param future The future. */ public void putWaitingCall(String callid, Future future) { getRemoteReferenceModule().checkThread(); waitingcalls.put(callid, future); } /** * Get a waiting call future. * @param callid The callid. * @return The future. */ public Future getWaitingCall(String callid) { getRemoteReferenceModule().checkThread(); return (Future)waitingcalls.get(callid); } /** * Remove a waiting call. * @param callid The callid. * @return The future. */ public Future removeWaitingCall(String callid) { getRemoteReferenceModule().checkThread(); return (Future)waitingcalls.remove(callid); } /** * Get the remote reference module. * @return the rrm. */ public RemoteReferenceModule getRemoteReferenceModule() { return rrm; } // protected static Map errors = Collections.synchronizedMap(new LRU(200)); /** * Send the request message of a remote method invocation. * (Can savely be called from any thread). */ public void sendMessage(final IComponentIdentifier receiver, final Object content, final String callid, final long to, final Future future) { component.scheduleStep(new IComponentStep() { public Object execute(IInternalAccess ia) { final RemoteServiceManagementAgent agent = (RemoteServiceManagementAgent)ia; final long timeout = to<=0? DEFAULT_TIMEOUT: to; putWaitingCall(callid, future); // System.out.println("Waitingcalls: "+waitingcalls.size()); final Map msg = new HashMap(); msg.put(SFipa.SENDER, component.getComponentIdentifier()); msg.put(SFipa.RECEIVERS, new IComponentIdentifier[]{receiver}); msg.put(SFipa.CONVERSATION_ID, callid); // msg.put(SFipa.LANGUAGE, SFipa.JADEX_XML); SServiceProvider.getService(component.getServiceProvider(), ILibraryService.class) .addResultListener(agent.createResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { final ILibraryService ls = (ILibraryService)result; SServiceProvider.getService(component.getServiceProvider(), IMessageService.class) .addResultListener(agent.createResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { // Hack!!! Manual encoding for using custom class loader at receiver side. // msg.put(SFipa.CONTENT, JavaWriter.objectToXML(content, ls.getClassLoader())); msg.put(SFipa.CONTENT, Writer.objectToXML(getWriter(), content, ls.getClassLoader(), receiver)); IMessageService ms = (IMessageService)result; ms.sendMessage(msg, SFipa.FIPA_MESSAGE_TYPE, component.getComponentIdentifier(), ls.getClassLoader()) .addResultListener(agent.createResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { // ok message could be sent. component.scheduleStep(new IComponentStep() { public Object execute(IInternalAccess ia) { // System.out.println("waitfor"); MicroAgent pa = (MicroAgent)ia; pa.waitFor(timeout, new IComponentStep() { public Object execute(IInternalAccess ia) { // System.out.println("timeout triggered: "+msg); removeWaitingCall(callid); // waitingcalls.remove(callid); // System.out.println("Waitingcalls: "+waitingcalls.size()); future.setExceptionIfUndone(new RuntimeException("No reply received and timeout occurred: "+callid)); return null; } }).addResultListener(agent.createResultListener(new DefaultResultListener() { public void resultAvailable(Object source, Object result) { // cancel timer when future is finished before. final ITimer timer = (ITimer)result; future.addResultListener(agent.createResultListener(new IResultListener() { public void resultAvailable(Object source, Object result) { removeWaitingCall(callid); // waitingcalls.remove(callid); // System.out.println("Waitingcalls: "+waitingcalls.size()); // System.out.println("Cancel timeout (res): "+callid+" "+future); // errors.put(callid, new Object[]{"Cancel timeout (res)", result}); timer.cancel(); } public void exceptionOccurred(Object source, Exception exception) { removeWaitingCall(callid); // waitingcalls.remove(callid); // System.out.println("Waitingcalls: "+waitingcalls.size()); // System.out.println("Cancel timeout (ex): "+callid+" "+future); // errors.put(callid, new Object[]{"Cancel timeout (ex):", exception}); timer.cancel(); } })); } })); return null; } }); } public void exceptionOccurred(Object source, Exception exception) { // message could not be sent -> fail immediately. // System.out.println("Callee could not be reached: "+exception); // errors.put(callid, new Object[]{"Callee could not be reached", exception}); removeWaitingCall(callid); // waitingcalls.remove(callid); // System.out.println("Waitingcalls: "+waitingcalls.size()); future.setException(exception); } })); } public void exceptionOccurred(Object source, Exception exception) { // errors.put(callid, new Object[]{"No msg service", exception}); removeWaitingCall(callid); // waitingcalls.remove(callid); // System.out.println("Waitingcalls: "+waitingcalls.size()); future.setException(exception); } })); } public void exceptionOccurred(Object source, Exception exception) { // errors.put(callid, new Object[]{"No lib service", exception}); removeWaitingCall(callid); // waitingcalls.remove(callid); // System.out.println("Waitingcalls: "+waitingcalls.size()); future.setException(exception); } })); return null; } }); } }