4. Remoting

4.1. CORBA in the GDA

4.1.1. Introduction

This section describes how to create a new object and CORBA-enable it - that is, create the additional files, interfaces and classes needed for the clients to interact with the object remotely.

4.1.2. Writing the Java code

4.1.2.1. Create the Java interface for the object

External hyperlinks, like gda.device.detector.Phantom.

4.1.2.2. Write an implementation of your interface

For example gda.device.detector.phantom.PhantomV73.

Note that if the interface is some.package.Xxx, the implementation should be in some.package.xxx.SomeClass.

In the case of the Phantom, the PhantomV73 class fits into the Device/Scannable/Detector hierarchy, but an object implementing the IPhantomV73Controller interface is used to actually interact with the hardware (or a simulation of it).

4.1.3. Creating CORBA-specific files

4.1.3.1. Create an IDL that matches the Java interface

For example, phantom.idl

A few points:

  • Note that whereas the Java interface is called Phantom, the CORBA interface is called CorbaPhantom.
  • Java and CORBA types are different; for example, a Java int corresponds to a CORBA long.
  • Input parameters must be prefixed with in; for example: double getDemandVoltage(in long electrodeNumber) raises (device::corba::CorbaDeviceException);

4.1.3.2. Compile the IDL to create CORBA classes

(It is no longer necessary to add the IDL file to an Ant script; the make-corba-jar target automatically picks up all IDLs.)

From the root of the GDA project, type:

$ ant make-corba-jar

Or if this fails, try:

ant -f build-classic.xml make-corba-jar

which will create a new gda-corba.jar that will include new classes for your object. For Phantom these classes include:

  • CorbaPhantomOperations - interface containing the Phantom-specific operations (e.g. setupForCollection)
  • CorbaPhantom - interface representing the CORBA version of Phantom; extends CorbaPhantomOperations plus some other CORBA interfaces
  • _CorbaPhantomStub - implements CorbaPhantom and makes the CORBA remote requests
  • CorbaPhantomHelper - various utility methods for working with CorbaPhantom objects

4.1.3.3. Write the CORBA implementation/adapter classes

These classes must be located in the correct package so they are found.

  • The interface for the device will be in some.package.Xxx.

  • The ImplFactory requires the implementation class to be named some.package.xxx.corba.impl.XxxImpl.

  • The AdapterFactory

    AdapterFactory] requires the adapter class to be named some.package.xxx.corba.impl.XxxAdapter.

4.1.3.3.1. Implementation class

For Phantom, this is PhantomImpl.

The implementation class must extend your CORBA object’s POA class (for Phantom, this is called CorbaPhantomPOA).

  • The class needs two fields: * The real object - a Phantom in the case of the Phantom. * A POA field.
  • You need a 2-arg constructor which takes the “real” object and the POA. ImplFactory will use this constructor.
  • Each method that you implement should delegate to the “real” object; any exceptions must be converted into CORBA-specific exceptions (e.g. DeviceException to CorbaDeviceException). See PhantomImpl for examples of how to implement these methods.
4.1.3.3.2. Adapter class

For Phantom, this is PhantomAdapter.

The adapter class may extend other adapter classes but always needs to implement your Java interface (e.g. Phantom).

  • The class needs three fields: * A CORBA object (e.g. a CorbaPhantom for the Phantom). * A NetService. * The object’s name.
  • You need a 3-arg constructor which takes a CORBA object, the object’s name, and a NetService. AdapterFactory will use this constructor.
  • Each method that you implement should delegate to the CORBA object; any CORBA exceptions must be converted into corresponding non-CORBA exceptions (e.g. CorbaDeviceException to DeviceException). See PhantomAdapter for examples of how to implement these methods.

4.1.4. How the remote call works

Once the CORBA work has been done, the object can be used like this:

MyObject myObject = Finder.getInstance().find("My_Object_Name");
myObject.myMethod("foobar");

The way this is handled is as follows:

  • myObject.myMethod(“foobar”) calls the corresponding method in the adapter.
  • The adapter calls the CORBA stub.
  • The CORBA stub makes the remote call across the network.
  • On the server, the corresponding method in the implementation class is called by CORBA.
  • The implementation class calls the “real” object.

4.1.5. Reference

Phantom.idl:

#ifndef _PHANTOM_IDL_
#define _PHANTOM_IDL_

#include <detector.idl>

module gda {
module device {
module detector {
module phantom {
module corba {

/**
* An interface for a distributed motor class
*/
interface CorbaPhantom : device::detector::corba::CorbaDetector
{
   void setUpForCollection(in long numberOfFrames, in long framesPerSecond, in long width, in long height)
   raises (device::corba::CorbaDeviceException);
   any retrieveData(in long cineNumber, in long start, in long count) raises (device::corba::CorbaDeviceException);
   string command(in string commandString) raises (device::corba::CorbaDeviceException);
};

};};};};};
#endif

ImplFactory:

gda.factory.corba.util.ImplFactory

AdaptorFactory:

gda.factory.corba.util.AdaptorFactory

PhantomImpl:

gda.device.detector.phantom.corba.impl.PhantomImpl

PhantomAdaptor:

gda.device.detector.phantom.corba.impl.PhantomAdaptor

4.2. Alternatives to CORBA

4.2.1. Using RMI

4.2.1.1. Using a standard RMI exporter/proxy

For newly-written objects, RMI can be used to make those objects available over the network.

Spring’s RmiServiceExporter can be used on the server side to make an object remotely available. It must be told which object is being exported, the name to export the object with, and the service interface - the interface defining the methods that should be available to clients. For example:

<!-- the object that is to be made remotely available -->
<bean id="server" class="...">
    ...
</bean>

<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
    <property name="serviceName" value="gda/ProsilicaServer" />
    <property name="service" ref="server" />
    <property name="serviceInterface" value="gda.images.camera.prosilica.server.ProsilicaImageServer" />
</bean>

On the client side, Spring’s RmiProxyFactoryBean can be used to generate a proxy to the object on the server. It will create a proxy object that implements the service interface; each method makes a call to the remote object. For example:

<bean id="prosilica_server" class="org.springframework.remoting.rmi.RmiProxyFactoryBean">
    <property name="serviceUrl" value="rmi://otherserver/gda/ProsilicaServer" />
    <property name="serviceInterface" value="gda.images.camera.prosilica.server.ProsilicaImageServer" />
    <property name="refreshStubOnConnectFailure" value="true" />
</bean>

The refreshStubOnConnectFailure property causes the client to reconnect to the server if, for example, the server is restarted. This allows a hot restart of the server without the need for the client to be restarted.

Note that using RmiProxyFactoryBean means that every call to a method in the service interface will result in a remote method invocation. This is not appropriate, for example, for objects that implement IObservable for eventing. See the next section for a solution to this.

There are currently a number of issues that prevent this mechanism from being used instead of CORBA for objects such as scannables:

  • The ‘remote interface’ defined by the CORBA IDL files, and the adapter and implementation classes, often differ from the methods implemented by the ‘real’ object. An object exported using RmiServiceExporter, and a proxy automatically generated by RmiProxyFactoryBean, would not account for these differences.
  • CORBA adapter and implementation classes often include additional logic not present in the ‘real’ object. They also sometimes carry out type conversion. Again, using the standard RMI exporter/proxy beans would not take these differences into account.
  • CORBA adapter and implementation classes often carry out conversions between ‘real’ exception types (such as DeviceException) and CORBA-specific exception types (such as CorbaDeviceException). This means that the exceptions that a client needs to be prepared to handle are often quite limited. Using an automatically-generated RMI proxy would mean that the client may need to be modified to deal with other exception types.

However, if you are developing new objects and wish to invoke methods remotely, using this mechanism may be sufficient.

4.2.1.2. Using GdaRmiProxyFactoryBean

Spring’s RmiProxyFactoryBean will perform a remote method invocation for every method in an object’s service interface, including methods in the IObservable interface, if the server-side object implements that interface. To avoid this, GdaRmiProxyFactoryBean should be used instead.

GdaRmiProxyFactoryBean creates a proxy that handles IObservable method calls locally. The proxy is automatically connected to the CORBA event dispatch system, and registers to receive events related to the server-side object. It also maintains a client-side list of observers. When events are received by the client-side proxy, they will be dispatched to observers.