|
|
|
|
|
Remote Dynamic Method Invocation Handbook
|
|
|
RDMI or Remote DMI is a remoting interfacing
for ACDK similar to Java RMI.
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.
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.
|
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.
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 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 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.
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 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
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.
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.
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.
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.
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.
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.
|
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()));
|
// CfgScript
Object o = ...;
o.toString__byRefAs("acdk/lang/Comparable");
// C++
RObject o = ...;
RComparable rcomp = o->invoke("toString__byRefAs", "acdk/lang/Comparable");
|
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());
}
|
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 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
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).
- A remote construction of a object creates the object instance on the server side.
- This server object instance will be registered in pool connected with its connection.
- The remote reference will be returned to client. The client creates a local proxy.
- On the client the garbage collection will work the normal way on client side.
- 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.
- On the server side the object will be released from the pool.
- In case the connection between client and server will be closed (f.e. because
client dies)
- all objects in the server object pool connected with this connection
will be released.
|
|