Index of RMI for C++


1. Using RMI step by step

The following example illustrates how to use and implement remote object using the Zeus-Framework. The RMI for C++ is illustarted here.


1.1 Step 1: Write a remote interface

We start our example with the definition of the remote interface. There are simple rules to write a remote interface:

  • Instead of class-keyword use the key word 'remote_interface'.
  • Each method must return an error code of type Retval.

Our example object is a simple object having one getter- and one setter method. Each method witch is accessible through the network must be defined in a interface:


#ifndef IRemoteTestObjectHPP
#define IRemoteTestObjectHPP

#include <zeusbase/System/Interfaces/IZUnknown.hpp>
#include <zeusbase/System/Interfaces/IString.hpp>

#define INTERFACE_IRemoteTestObject      0x00FFFFFF
BEGIN_NAMESPACE_Zeus

/***************************************************/
/*! interface definition of the remote test object
*/
/***************************************************/
remote_interface IRemoteTestObject : public IZUnknown
{
  public:
    /***********************************************/
    /*! Returns the double value
    */
    /***********************************************/
    virtual Retval MQUALIFIER getFloatValue(Float64& rValue) 
      const=0;

    /***********************************************/
    /*! Sets the double value
    */
    /***********************************************/
    virtual Retval MQUALIFIER setFloatValue(Float64 dValue)=0;
};

END_NAMESPACE_Zeus

#endif

This interface is the base of the remote method invocation compiler for C++ [rmicc].

1.2 Step 2: Creating the Servant (RemoteObject)

This step implements the servant, the object doing some work inside a distributed application. This object will be accessed from outside, from a network client. Because more than one client may access an object, we must carefully design the methods and should use locking.

Our remote class must implement the methods of the remote interface. We use the abstract implementation of the remote object from the Zeus-Framework. We use also the macro REMOTE_OBJECT_DECL witch declares some methods for remote access. The m_pLock is needed for threadsafe access.

Header:


#include <interfaces/IRemoteTestObject.hpp>
#include <zeusbase/Remote/AbstractRemoteObject.h>

BEGIN_NAMESPACE_Zeus

class TRemoteTestObject : public TAbstractRemoteObject, 
                          public IRemoteTestObject
{
  public:
    TRemoteTestObject();
    
    //Methods of IRemoteTestObject
    virtual Retval MQUALIFIER getFloatValue(Float64& rValue) const;
    virtual Retval MQUALIFIER setFloatValue(Float64 dValue);
        
    //Methods of IRemoteObject
    REMOTE_OBJECT_DECL
    
    //Methods of IZUnknown
    MEMORY_MANAGER_DECL
  
  protected:
    virtual~ TRemoteTestObject();
  
  private:
    ///Float64 value
    Float64 m_dValue;
    ///Lock
    TCriticalSection& m_rLock;
};
END_NAMESPACE_Zeus

Implementation:


#include "RemoteTestObject.h"
#include "RemoteTestObject_Skel.h"

USING_NAMESPACE_Zeus

//Macro for Remote object
REMOTE_OBJECT_IMPL(TRemoteTestObject,     //class id
                   L"TRemoteTestObject",  //class name (string)
                   L"",                   //code module
                   TRemoteTestObject_Skel //skeleton class id);

TRemoteTestObject::TRemoteTestObject()
 : TAbstractRemoteObject(),
   m_rLock(*new TCriticalSection())
{
  m_dValue = 0.0;
}

TRemoteTestObject::~TRemoteTestObject()
{ m_rLock.release(); }

Retval MQUALIFIER TRemoteTestObject::getFloatValue(Float64& rValue) const
{ 
  m_rLock.enter();
  rValue =  m_dValue; 
  m_rLock.leave();
  
  return RET_NOERROR;
}

Retval MQUALIFIER TRemoteTestObject::setFloatValue(Float64 dValue)
{ 
  m_rLock.enter();
  m_dValue = dValue; 
  m_rLock.leave();
  
  return RET_NOERROR;
}

//Macro for memory management (addRef, release and casting)
MEMORY_MANAGER_IMPL(TRemoteTestObject);
  INTERFACE_CAST(IRemoteTestObject, INTERFACE_IRemoteTestObject);
MEMORY_MANAGER_IMPL_PARENT_END(TAbstractRemoteObject);

This is all we need for our remote object.

1.3 Step 3: Create Stub and Skel classes using [rmicc]

The Stub is the client of a remote object. This class implements the same interface like the remote object. Each method call is serialized and transmitted through a network. The other side called skeleton will deserialize this remote method call and reassemple a real method call on the servant.

Since RMI for C++ 2.0 we generate the Stubs and Skeletons using the [rmicc] tool. This tool is included in the Zeus-Framework, and should be available after building and installing the framework.


> rmicc ./IRemoteTestObject.hpp

This will generate follwoing files:

  • Client files: RemoteTestObject_Stub.h and RemoteTestObject_Stub.cpp
  • Server files: RemoteTestObject_Skel.h and RemoteTestObject_Skel.cpp

Add the RemoteTestObject.h, RemoteTestObject.cpp, RemoteTestObject_Skel.h and RemoteTestObject_Skel.cpp files to the server project.

Add the RemoteTestObject_Skel.h and RemoteTestObject_Skel.cpp to the client project.

1.4 Step 4: Use of NameService

Now we are almost ready for a first test. We need to register the servant so that the client can find the servant on the network.

Server side implemenation for registering the servant. This example shows the binding of an object to a fixed ip address:


#include <zeusbase/Remote/Naming.h>
#include "RemoteTestObject.h"
...
{
  ...
  //Connect to naming server
  Naming.connect(L"124.33.21.5", 7000);
  ...
  //Create remote object and register object
  TAutoPtr<TRemoteTestObject> pServant = new TRemoteTestObject();
  pServant->connect(L"124.33.21.2", 6000);
  Naming.bind(TString(L"MyObject"), *pServant);
  ...
  //loop here (wait for clients and handle requests
  ...
  Naming.unbind(TString(L"MyObject"));
  
  //Optional. Destroying the pServant auto pointer will also 
  // the servant disconnect
  pServant->disconnect();
}

Instead of the binding the object to a fixed address, you can call simply pServant->connect() for dynamic binding. The object will be bound to the first ethernet interface (eth0) and a unused port.

Important:
Connect the object first before you call Naming.bind(), because the naming service needs to know the ip address and port of the object.

The client needs following implementation:


#include <zeusbase/Remote/Naming.h>
#include "IRemoteTestObject.hpp"
...
{
  //Connect to naming server
  Naming.connect(L"124.33.21.5", 7000);
  ...
  //Lookup for remote object
  TAutoPtr<IRemoteObject> pRemoteObject = NULL;
  if (Naming.lookup(TString(L"MyObject"), pRemoteObject) == RET_NOERROR)
  {
    IRemoteTestObject* pMyObject = NULL;
    if (pRemoteObject->askForInterface(INTERFACE_IRemoteTestObject,
        ICAST(pMyObject)) == RET_NOERROR)
    {
      //Use myobject here
      pMyObject->setFloatValue(12.005);
      ...
      pMyObject->release();
    }
  }
}

When we start client and server we must be sure, that the name server is running. Use the script run_cellregistry.sh witch starts the nameserver and the cell registry. To configure the IP address change the entry inside cellreg.xml.