9. Remoting¶
Remoting is the ability to make method calls on remote objects as if they were local. In this context ‘remote’ means an object in a different JVM (which can be on the same machine when running in dummy mode or on a different machine in live mode). The execution will be blocked while the remote call is made and if the method called returns something it will be returned to the caller. The general term for this remote procedure call (RPC).
9.1. RMI¶
RMI stands for Java Remote Method Invocation and it is a Java specific implementation of RPC. It uses Java object serialization for passing arguments and return types. It requires the interface to:
Implement
java.io.Serializable.Extends
java.rmi.Throw
java.rmi.RemoteExceptions. Some of these requirements are not always desirable. For example there may be cases where one does not want remote objects to throw the exceptions. Luckily Spring have wrapped RMI to remove these constraints.
Example
Suppose lots of clients need to get a number which must be unique. This can be achieved using RMI.
Write an interface that offers the functionality that the client wants:
public interface Counter extends Remote { int getNumber() throws RemoteException; }
Design a class which implements this functionality:
public class CentralCounter implements Counter { AtomicInteger centralCounter = new AtomicInteger(); @Override // From Counter public int getNumber() throws RemoteException { return centralCounter.getAndIncrement(); } }
Write a server that has an instance of CentralCounter and make it available over RMI.
Server: Instance of CentralCounter ----- EXPORT ----> rmi://host:port/serviceName
Now the client can get a stub connected to the server which can be cast to the implemented interface:
public class RmiClient { public static void main(String args[]) throws Exception { Counter clientCounter = (Counter) Naming.lookup("rmi://host:port/serviceName"); System.out.println(clientCounter.getNumber()); } }
9.1.1. Implementation in GDA¶
GDA uses Spring RMI support which removes the need for an interface to extend
java.rmi and methods throwing java.rmi.RemoteException. An addition layer
has also been added in GDA to support proxies.
One of the issues with RMI is that it provides the ability for a client to make a method call on the server object, but the other way round. In GDA this is important as lots of classes implement gda.observable.IObservable which allows listeners to be registered and so when event happen, the gda.observable.IObserver.update() method is called. The ideal behaviour would be that if a client registers as a listener on the RMI proxy, then the update method is called when events happen on the server object.
[Note: You may come across a duplicate of
IObservable/IObserverin the form ofObservable/Observer. The latter (non-‘I’ versions) are ignored by the events forwarding and should NOT be used.]
9.2. Event forwarding¶
When an IObservable object is exported over RMI, an IObserver is added which can receive events and send them to the EventService. The ServerSideEventDispatcher then sends them to the client via JMS.
Aside: JMS stand for Java Message Service and is a Java message-orientated middleware API for sending messages between or more components of a distributed application. It allows application components based on Java EE to create, send, receive, and read messages.
When a method interceptor is added for IObservable methods when a RMI proxy
is created on the client. This means that if an IObserver is added to a RMI
proxy, the call gets handled locally by the interceptor which has an
ObservableComponent and dispatches the event inside the
client.

The JythonServer is exported with a Jython interface which extends IObservable.
The ServerSideEventDispatcher is registered as an IObserver to the
JythonServer.The
MyClassinstance is added as an observer by calling addIObserver on theJythonServerproxy.The ClientSide
IObservablemethod interceptor has an ObservableComponent and intercepts client calls to addIObserver. It will call the IObserver.update() method on the listeners to theJythonServerRMI proxy.The ClientSideEventReceiver wires together the EventService and the
ObservableComponent.
Class Diagram
Example in GDA
Get the Jython server (or proxy) from the finder:
Jython jy = Finder.find("command_server");
Make a method call (could be local or via RMI):
String result = jy.evaluateCommand(command, jsfIdentifier);
Add an
IObserver(ifjyis actually a proxy it will be intercepted on the client and never makes it to the server):jy.addIObserver((source, event) -> System.out.println(event);
This works exactly the same on both the client and the server so if the objects is actually remote, it is all handles seamlessly and you will always be notified of event. In fact you will not even know if the code is server or client side as it will work on both.
9.3. Finders and Factories¶
A Finder is a singleton that holds a set of factories. These are ‘object factories’ which implement gda.factory.Factory. They provide/produce Findable objects when looked up using the name. When a finder is asked to find something, it goes to each of the factories to see if they know about an object with that name. If they do then they return it to the finder and the object has been found. In addition, a factory can also be implemented so that it automatically provides proxies to remote objects which makes finding remote objects as simple as local objects.
Two of the core factories used in GDA are: * [gda.spring.SpringApplicationContextBasedObjectFactory][_SpringApplicationContex tBasedObjectFactory] - this finds Findable objects defined in Spring.
uk.ac.gda.remoting.client.RmiProxyFactory- provides RMI proxies for RPC on remote objects.
Class Diagram
9.4. Exporting over RMI¶
For any client processes to work, first you have to export the object you want over RMI. There are 2 ways of doing this: defining in Spring, or using automated RMI exporting.
9.4.1. Spring definition¶
This is the method that has been around for a while. Simply define it in the server Spring:
<gda:rmi-export
service="plot_server"
serviceName="gda/plotserver"
serviceInterface="uk.ac.diamond.scisoft.analysis.PlotServer" />
Or use the Spring RMI exporting and add event support:
<bean class="uk.ac.gda.remoting.server.GdaRmiServiceExporter">
<property name="serviceName" value="gda/plotserver" />
<property name="service" ref="plot_server" />
<property name="serviceInterface" value="gda.scan.IScanDataPointServer" />
</bean>
9.4.2. Automated RMI Exporting¶
This method uses automated RMI exports where anything that can be exported (and
is not explicitly made local) is exported by the
uk.ac.gda.remoting.server.RmiAutomatedExporter.
E.g.
private Map<String, Findable> getRmiExportableBeans() {
final Map<String, Findable> allFindableBeans = applicationContext.getBeansOfType(Findable.class);
return allFindableBeans.entrySet().stream()
.filter(this::hasServiceInterfaceAnnotation) // Removes beans without @ServiceInterface
.filter(this::isNotLocal) // Removes beans declared as local
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
}
The @ServiceInterface annotation is used to tell the
automated RMI exporter what interface to use to export with. Remote calls to
methods defined by this interface can be made (the interface must extend
Findable). If a class does not have the @ServiceInterface
annotation then it will not be automatically RMI exported.
Certain instances of a class can be kept local (and so not exported for remote access) using gda.factory.Localizable. The default is typically ‘false’ so the the property must be explicitly declared local in Spring to ensure it is not exported: E.g.
<property name="local" value="true"/>
Automated exports are done as part of the sever start-up and so it is possible to identify what objects have been automatically exported. E.g.
2018-06-18 17:24:03,812 DEBUG u.a.g.r.s.RmiAutomatedExporter - Exported 'GDAMetadata' with interface 'gda.data.metadata.Metadata'
2018-06-18 17:24:04,887 DEBUG u.a.g.r.s.RmiAutomatedExporter - Exported 'stagey' with interface 'gda.device.IScannableMotor'
2018-06-18 17:24:05,412 DEBUG u.a.g.r.s.RmiAutomatedExporter - Exported 'stagex' with interface 'gda.device.IScannableMotor'
Class Diagram
9.4.2.1. Automated RMI Importing¶
This would be done on the client side and simply required the following line in the client Spring:
<bean class="uk.ac.gda.remoting.client.RmiProxyFactory" />
This takes care of importing the available objects and making them available via the finder.
Remote objects can be obtained and injected into anther object, e.g.
<bean id="andor_x" class="uk.ac.gda.remoting.client.GdaRmiProxy" />
<bean id="some_other_object" class="uk.ac.gda.RandomClass">
<property name="detectorStageX" ref="andor_x" />
</bean>


