2005/5/10

     
 

Remote Dynamic Method Invocation Handbook

artefaktur

RDMI or Remote DMI is a remoting interfacing for ACDK similar to Java RMI.


Content of this chapter:

   Introduction
     First look
     Principles
       Which classes can be used remote
       How to access Remote Objects
         Client Object Proxies
         Weak Types/Invocation
         Strong Types/Invocation
         Comparing Strong and Weak Types/Invocation
       Parameters and Return Values
         Serialized values
         Remote References
       Selection when to transfer by value or by reference
       Parameter
         IN, OUT, INOUT
         Selection by the parameter types of the method declararation
         Selection by the parameter attributes of the method declararation
         Selection by the the caller in case of weak invocation
       Return values
       Exceptions
       Call models
       Parameters and Return Values
     Remote Garbage Collection


 Introduction


 First look

acdkx_rdmi is a simple solution for object oriented remote calls.

The client and the server communicates over network (by default).

Here the sample code for a Remote DMI Server written in CfgScript.


// create a RDMI server using binary protocol and TCP as transport protocol
RemoteDmiServer server = new RemoteDmiServer(new TcpServer(1111), new BinaryProtocol());
// starts the server in a background thread
server.startInBackground();

out.println("RemoteDmiServer started on port 1111");
// wait for finishing server
// this only happens if a client request for shuting down the server
server.join();

out.println("RemoteDmiServer terminates");


// create a client connecting to server
RemoteDmiServer client = new RemoteDmiServer(new TcpServer(InetAddress::getLocalHost(), 1111), new BinaryProtocol());
// create an object with 'new acdk.lang.StringBuffer("hello")' on 
// the server and return the object as remote object
Object o = client.createRemote("acdk.lang.StringBuffer", "hello");
// call the append function on the remote object
o.append(" from remote");
// call the toString method on the remote object
out.println(o.toString());

Similar client code for C++

  RRemoteDmiServer client = new RemoteDmiServer(new TcpServer(::acdk::net::InetAddress::getLocalHost(), 1111), new BinaryProtocol());
  RObject remSb = client->createRemoteObject("acdk/lang/StringBuffer", "acdk/lang/Object", inOf("Hello"));
  System::out->println((RString)remSb->invoke("toString"));
See  Concept about Remote DMI.

 Principles

 Which classes can be used remote

Every class, which is loaded via ClassLoader and supports MetaInfo can be used as a remote object. This inludes f.e. nearly every C++ ACDK class and all CfgScript classes.

!

In a later version of RDMI, the classes available as remote object, may be restricted by the server.

 How to access Remote Objects

Basically RDMI using the standard DMI (dynamic method invocation) mechanism of ACDK. DMI is used to interconnect the different object worlds of C++, CfgScript, CORBA, Java, Perl, Python, Tcl, Lisp, COM, ...).
All these different implementation of the DMI protocoll are also enabled to work remote using RDMI.


 Client Object Proxies

If an object will be created remote:

Object o = client.createRemote("acdk.lang.StringBuffer", "hello");
not the real object will be returned, but a proxy for the object. The object itself will life on the server.

All calls to the proxy object will be forwarded to the remote object, so that the code of the method will be executed on the server.


There are two different ways to retrive remote objects and invoke theire methods.

 Weak Types/Invocation
Weak objects are opaque. Basically opaque objects are from a generic Type, like acdk::lang::Object and does not provide futher metainfo (reflection).
The connection of calling a method with the corresponding method code will not be done by a compiler and linker, but will be evaluated at runtime.


// C++
// Weak object
RObject object = client.createRemote("acdk.lang.StringBuffer", "hello");
// make a weak call.
// If the object doesn't provide the toString method
// an exception will be thrown
RString erg = (RString)object->invoke("toString");

Some scripting languages solely relies on weak invokation.
CfgScript support both (strong and weak) objects/invokations, but hides the details behind the scene:

// o is a weak object
Object o = client.createRemote("acdk.lang.StringBuffer", "hello");
// CfgScript knowns, that o is a weak type
// so the method call append will be tranformed to a weak call
o.append(" from remote");



 Strong Types/Invocation
Strong types are interesting in languages, which itself relies on strong types, like C++.
The remote object is not an opaque type, but a typed interface:


// C++
// constructor are still called via the weak interface
acdk::io::RPrintWriter rout = 
  (acdk::io::RPrintWriter)client->peekStaticAs("acdk/lang/System", "acdk/io/PrintWriter", "out");
// as result a native interface can be used with no difference in the call syntax to
// local object invocations
rout->println("Hello");

Because all calls to the (remote) typed object has to be forwarded to the remote object there are some limitation:
  • Remote types can only be interfaces.
  • It does not work with non virtual methods
  • The dmiproxy metainfo has to be generated with acdkmc.


 Comparing Strong and Weak Types/Invocation

Weak types/invocation has following advantages:
  • works directly with objects, not only via interfaces
  • All methods - including static and non-virtual methods can be invoked remotly.
  • Fields can be read and written.
  • Do explictly DmiProxy has to be generated for usage in C++.
  • transparent in scripting langauges like CfgScript

The advantages of types/invocation can be found in using them in C++:
  • Strong typed remote proxies can be used in C++ transparent to local object. Neither server nor client code doesn't directly recognise, that the object is a remote object.
  • No casting of return types with inOf() for parameters is needed for C++.
  • Having the advantages of strong typed languages. Misstyping/use (for example wrong function name or parameter type) will be recognise by the C++ compiler and not later at runtime.
If you use strong types on client side, you still can use weak invocation:

// C++ sample
RComparable cmp = (RComparable)client->createRemoteAs("acdk/lang/StringBuffer", "acdk/lang/Comparable", "hello");
// the interface Comparable only support the compareTo method
// with weak invocation the other methods of the real remote object
// can be addressed
cmp->invoke("append", " to remote");




 Parameters and Return Values

Parameters and return values can be tranfered on two ways:
  • By value: The complete object will be serialized and transfered to the other side.
  • By (remote) reference: A weak or strong proxy identifier

 Serialized values
All basic types - like int, char, float, etc. are transfered by value.
By default also all classes which directly or indirectly implements the acdk::io::Serializable interface are transfered by value.
The classes are serialized with the standard ACDK serialization mechanism and transported as (more or less structured) stream to the server
On the server side this stream is deserialized to a local real instance of the serialized class.

 Remote References
A remote reference contains following information:
  • The server signature, where this object lifes.
    For example: "inet:/192.168.0.95:1111"
  • An opaque object identifier as integer. This enables the server to associate the remote reference to a real object.
  • Optionally a class name like "acdk/io/PrintWriter" identifies an interface.
    This is used by the client to create an strong typed (C++) Proxy for this interface.

 Selection when to transfer by value or by reference


There are multiple answers to the question wether a parameter or return type will be transfered by value or by reference.
The anwer also depends on whether the remote object is held as strong or weak type by the client.

  • In case of weak invocation (or CfgScript) using an argument modifier (byVal, byRef, byRefAs, etc.)
  • In case of strong typed invocation selection made by argument modifier in the method signature (byval, byref, etc.).
  • In case of weak invocation by the real type of the argument: Basic and serializable types will be transfered as value, all other as remote reference.
  • In case of strong typed invocation by the declared type of the argument: Basic and serializable types will be transfered as value, all other by remote reference.

 Parameter

 IN, OUT, INOUT
acdkx_rdmi supports also the IN, OUT, INOUT (in, out, inout in CfgScript) parameter attributes in the method signature.
  • Parameter with the IN attribute (parameter without any directional are also IN parameters) are send from caller (client) to callee (server).
  • Parameter with the OUT attribute are only send from the callee back to the caller.
    These parameter works like additionally return values.
  • Parameter with the INOUT attribute are transfered from the caller to the callee and while returning from callee also back to caller.

IN, OUT and INOUT parameter can be transfered either by value or by (remote) reference.

 Selection by the parameter types of the method declararation
Given method signature

  // C++
class XY
{
  int callFoo(IN(RStringBuffer) sb, OUT(RComparable) cmp)
  {
    cmp = (RComparable)sb;
    return sb->length();
  }
  // ...
  // or CfgScript
  int callFoo(StringBuffer sb, out Comparable cmp)
  {
    cmp = sb;
    return sb.length();
  }
  // ...
  
// client code
RStringBuffer sb1 = new StringBuffer();
RComparable cmp;
int i = remoteObject->callFoo(sb1, cmp);
  • StringBuffer implements the Serializable interface, so the sb parameter is tranfered from caller (client) to callee (server) by value.
  • Comparable is not derived from Serializable, so this parameter will be tranfered from callee to caller (because it has the OUT attribute) as remote references.
  • int is a basic type, so this value is tranfered from calle to caller by value.
  • After the call the client holds an remote object reference to a a copy of the StringBuffer held by the server.


 Selection by the parameter attributes of the method declararation

It is also possible to add an attribute in the method signature to signal if a parameter shell be tranfered by reference or by value.


  // C++
class XY
{
  int callFoo(INBYVAL(RStringBuffer) sb, OUTREF(RStringBuffer) sb2)
  {
    sb2 = sb;
    return sb->length();
  }
  // ...
  // or CfgScript
  int callFoo(StringBuffer sb, out byref StringBuffer sb2)
  {
    sb2 = sb;
    return sb.length();
  }
  // ...
Athough the second parameter also is serializable the method attribute tells that the parameter should be tranfered by reference.

Note 1:

!

These additionally attributes has only limited use in C++
  • The returned byref StringBuffer may be casted to StringBuffer, but all calls of non virtual methods are not dispatched to remote object.
  • Forcing serialization of objects, which doesn't implement the Serializable interface may result in errors either rised by the remoting protocol or results in incorrectly initialized objects.
Note 2:

!

Local calls of methods with the byval attribute will not serialize/deserialize its parameters and so the the local call does not really pass its parameter by value.

 Selection by the the caller in case of weak invocation

In case of weak objects - where the client only holds a opaque object type with no further knowledge about methods and parameter the in, out, inout and byval, byref attributes can be given in the object call:

  // C++ & CfgScript
  RStringBuffer sb  = new StringBuffer("asdf");
  RObject objremote;
  // C++
  sb->invoke("callFoo", inOf(sb), byRef(outOf(objremote)));
  // CfgScript
  sb.callFoo(inOf(sb), byRef(outOf(objremote)));

In many cases it is also important, that the types matched:

  // C++
  sb->invoke("callFoo", byValAs(inOf(sb), StringBuffer::getClass()), byRefAs(outOf(objremote), Comparable::GetClass()));
  // CfgScript
  using acdk.cfgscript.ScriptGlobals;
  sb.callFoo(byValAs(inOf(sb), StringBuffer.getClass()), byRefAs(outOf(objremote), Comparable.GetClass()));

 Return values



// CfgScript
Object o = ...;
o.toString__byRefAs("acdk/lang/Comparable");
// C++
RObject o = ...;
RComparable rcomp = o->invoke("toString__byRefAs", "acdk/lang/Comparable");

 Exceptions

acdkx_rdmi supports Exceptions in a transparent way equally to local calls.
Important for exception types is, that metainfo is available for the thrown exceptions.

Here a sample in CfgScript:

Object o = client.createRemote("acdk.lang.StringBuffer", "hello");
try {
  String s = o.substring(-2, 2004); // misuse: the server throws IndexOutOfBoundsException
                                    // The exception will be transfered serialized to client
                                    // and will (re-)thrown in the client
} catch (IndexOutOfBoundsException ex) {
  out.println(ex.getMessage());
}




 Call models

In the first release of acdkx_rdmi only the synchronous call convention is supported.

A remote method call returns, if the server returned from the call.

Different to other remoting protocoll acdkx_rdmi also supports reentrant calls.

To illustrate this following sample in CfgScript:

// common source, known by server and client
interface Callback 
{
  void callIt(String s) = 0;
}
interface CallCallback 
{
  void callForEach(Callback cb) = 0;
}

// A server object
class MyServerObject
  implements CallCallback
{
  StringArray sa = [ "a", "b", "c" ];
  MyServerObject() {}
  void callForEach(Callback cb)
  {
    foreach (String s in sa)
    {
      cb.callIt(s);
    }
  }
}

// use it in client
class MyCallback
  implements Callback
{
  MyCallback() {}
  void callIt(String s)
  {
    out.println("MyCallback.callIt: " + s);
  }
}
MyCallback mycb = new MyCallback();

CallCallback cb =  client.createRemoteAs("MyServerObject", "CallCallback");
cb.callForEach(mycb);
What happens here?

cb.callForEach(mycb); make a remote call to MyServerObject.
Inside the callForEach on the server remote calls back to the client will be done for each element in sa.
After this the remove call cb.callForEach(mycb); returns back to client.

Resume:
  • an RDMI Server can also invoke remote objects from client.
    In this case the client automatically also works as RDMI Server.
  • The control flow in calling a remote method is the same, like normal local method calls.

 Parameters and Return Values

Parameters and return values can be tranfered on two ways:
  • By value: The complete object will be serialized and transfered to the other side.
  • By (remote) reference: A weak or strong proxy identifier

 Remote Garbage Collection

The remote garbace collection depends on concret protocoll. By default a statefull TCP/IP connection between client and server is used (in contrast to stateless HTTP connections).


  1. A remote construction of a object creates the object instance on the server side.
  2. This server object instance will be registered in pool connected with its connection.
  3. The remote reference will be returned to client. The client creates a local proxy.
  4. On the client the garbage collection will work the normal way on client side.
  5. If the local proxy will be deleted, the client sends a message to the server, which owns the object, that the object can be released.
  6. On the server side the object will be released from the pool.
  7. In case the connection between client and server will be closed (f.e. because client dies)
  8. all objects in the server object pool connected with this connection will be released.