Introduction
Remote SOF (Remote Service Oriented Framework) represents an extension of SOF (Service Oriented Framework). Before reading this article, I recommend to read the article about SOF here, or the documentation at the website which describes the basic mechanisms of the SOF framework. Remote SOF supports, like SOF, the modularization of software by dividing the code into several components (called 'bundles') which are able to communicate via clearly defined interfaces (called 'services'). Using SOF, the communication between the 'bundles' is limited to one process. That means, only bundles of the same process can call each other:
Remote SOF goes a step further, and allows the communication between bundles running in different processes:
The picture above shows the basic architecture of Remote SOF. There can exist several SOF containers (processes), instead of only one process when using SOF. The registry component of the SOF framework represents the central administration component for holding all the information about registered services and service listeners. For Remote SOF, this information is distributed to each 'local' registry component of all SOF containers by the remote registry component. For example, 'bundle 1' knows the services and service listeners which are registered by 'bundle 3', and vice versa. If 'bundle 1' registers a service which 'bundle 3' is interested in, 'bundle 3' will be notified as soon as the service has been registered by 'bundle 1'. Remote SOF also allows bundles to call services of bundles within the same process, of course.
CORBA (especially the CORBA implementation MICO) acts as communication layer for the Remote SOF project. That means the remote callable service objects, the remote callable service object listeners, and the remote registry are implemented as CORBA objects. Remote SOF tries to hide the CORBA infrastructure as much as possible from you, but for some cases (e.g., narrowing CORBA objects, using CORBA types etc.), you have to know about the CORBA basics.
Note: If you only want to implement 'local' bundles (where all bundles run within the same process), use SOF. For implementing distributed software bundles and remote callable services, you have to use Remote SOF. It is planned for the next versions of Remote SOF to support a mixture of 'local' and remote callable bundles running within one SOF container.
Using the code
Building the software
The implementation of Remote SOF is OS independent. But currently, only a Visual Studio project file is available for building the software. Please look at the project's web page for further information. There are future plans for providing make files for other platforms.
What is the difference to SOF?
As the SOF source code was reused for Remote SOF as far as possible, the Remote SOF API does not differ very much from the SOF API.
Changes are:
- Service interfaces are not implemented as C++ interfaces but as CORBA IDL interfaces.
- Use
IRemoteBundleActivator
for implementing a bundle instead of IBundleActivator
.
Use IRemoteBundleContext
instead of IBundleContext
for registering services.- Use
RemoteServiceTracker
instead of ServiceTracker
for creating service trackers. - Implement the
IRemoteServiceTracker
interface instead of IServiceTracker
for tracking services.
In the following section, the example of the SOF article is reused for the explanation of Remote SOF. All relevant source code of the example can be found under the directory 'sof/remote/examples'. There are also two bundles called 'bundle1' and 'bundle2' which communicate via one service interface of type IMultiplier
. Apart from the SOF example, the two bundles run in different processes.
Implementing the IRemoteBundleActivator interface
The IRemoteBundleActivator
interface provides the same methods (a destructor for cleaning up used resources, the start
method for starting the bundle, and the stop
method for stopping the bundle) like the IBundleActivator
interface. In contrast to SOF, the parameter of the start
and stop
method which is used for registering and deregistering services is not of type IBundleContext
but IRemoteBundleContext
. The following example shows the implementation of the IRemoteBundleActivator
interface for 'bundle1'. The destructor, and the start
and stop
methods are filled with code later.
Header file:
#ifndef BUNDLE_ACTIVATOR1_H
#define BUNDLE_ACTIVATOR1_H
#include "sof/framework/remote/corba/IRemoteBundleActivator.h"
#include "sof/framework/remote/corba/IRemoteBundleContext.h"
using namespace sof::framework::remote::corba;
class BundleActivator1 : public IRemoteBundleActivator
{
public:
virtual ~BundleActivator1();
virtual void start( IRemoteBundleContext::ConstPtr context );
virtual void stop( IRemoteBundleContext::ConstPtr context );
};
#endif
Implementation: For registering the type and name of the bundle activator class, the macro REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS
has to be used instead of REGISTER_BUNDLE_ACTIVATOR_CLASS
.
#include "BundleActivator1.h"
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"
using namespace sof::instantiation;
using namespace sof::framework;
BundleActivator1::~BundleActivator1()
{
}
void BundleActivator1::start(IRemoteBundleContext::ConstPtr context)
{
}
void BundleActivator1::stop(IRemoteBundleContext::ConstPtr context)
{
}
REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )
Services
The interface of a service which can be called remotely by other bundles has to be defined in CORBA IDL (= Interface Definition Language) which is independent from any programming language (C++, Java etc.). Our multiplier service used for this example is defined as follows:
#include "../../idl/CORBAObjects.idl"
interface Multiplier : sof::framework::remote::corba::generated::CORBAService
{
long multiply( in long x, in long y );
};
The mechanism is very similar to SOF:
- You have to define the service interface (with SOF in C++, with Remote SOF in IDL).
- The service interface must inherit from a basis interface (with SOF from
IService
, with Remote SOF from CORBAService
).
The CORBAService
type is defined in the IDL file 'CORBAObjects.idl' which is distributed with the Remote SOF software and placed in the directory 'sof/remote/idl'. In this IDL file, you can find the interface definitions of all remote callable objects.
After the definition of the service interface in IDL, the language specific code has to be generated by the IDL compiler. There is a Windows Shell script called 'gen_multiplier.bat' in the directory 'sof/remote/examples/idl' which generates the files 'Mutiplier.h' and 'Multiplier.cpp' and copies them into 'sof/remote/examples/common/src'. The generated code contains the C++ service interface (Multiplier
), the stub (Multiplier_stub
), and the skeleton (POA_Multiplier
) implementation, where the stub and skeleton encapsulate the details of communication. The stub substitutes the remote object on the client side and forwards all calls via network connection to the skeleton on the server side. Then, the skeleton on the server side calls the remote object. The first bundle ('bundle1') must implement the C++ service interface by inheriting from the generated type POA_Multiplier
:
Header file:
#ifndef MULTIPLIER_IMPL_H
#define MULTIPLIER_IMPL_H
#include "Multiplier.h"
using namespace std;
class MultiplierImpl : virtual public POA_Multiplier
{
public:
virtual CORBA::Long multiply( CORBA::Long x, CORBA::Long y );
};
#endif
Implementation:
#include <CORBA.h>
#include "MultiplierImpl.h"
#include <iostream>
using namespace std;
CORBA::Long MultiplierImpl::multiply( CORBA::Long x, CORBA::Long y )
{
cout << "Multiplier called. " << endl;
return x*y;
}
Registering and deregistering services
Now, we are ready for registering the service object. Here, the BundleActivator1
class registers two instances of the multiplier service. Member variables for the service object (MultiplierImpl
) and the registration object (IServiceRegistration
) are defined for each service instance in the header file.
Header file:
#ifndef BUNDLE_ACTIVATOR1_H
#define BUNDLE_ACTIVATOR1_H
#include "sof/framework/remote/corba/IRemoteBundleActivator.h"
#include "sof/framework/remote/corba/IRemoteBundleContext.h"
#include "sof/framework/IServiceRegistration.h"
#include "MultiplierImpl.h"
using namespace sof::framework::remote::corba;
class BundleActivator1 : public IRemoteBundleActivator
{
private:
IServiceRegistration* serviceReg1;
MultiplierImpl* service1;
IServiceRegistration* serviceReg2;
MultiplierImpl* service2;
public:
virtual ~BundleActivator1();
virtual void start( IRemoteBundleContext::ConstPtr context );
virtual void stop( IRemoteBundleContext::ConstPtr context );
};
#endif
In the following implementation of the BundleActivator1
class, the start
method sets the properties of the service instances and creates the service objects. Afterwards, both service instances are registered by calling registerRemoteService
(instead of registerService
with SOF).
From now on, the service instances can be tracked and called by other bundles.
Implementation:
#include "BundleActivator1.h"
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"
using namespace sof::instantiation;
using namespace sof::framework;
BundleActivator1::~BundleActivator1()
{
}
void BundleActivator1::start(IRemoteBundleContext::ConstPtr context)
{
Properties props;
props.put( "instance", "1" );
this->service1 = new MultiplierImpl();
this->serviceReg1 =
context->registerRemoteService( "Multiplier", this->service1, props );
props.put( "instance", "2" );
this->service2 = new MultiplierImpl();
this->serviceReg2 =
context->registerRemoteService( "Multiplier", this->service2, props );
}
void BundleActivator1::stop(IRemoteBundleContext::ConstPtr context)
{
this->serviceReg1->unregister();
delete this->serviceReg1;
delete this->service1;
this->serviceReg2->unregister();
delete this->serviceReg2;
delete this->service2;
}
REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )
Registering and deregistering service listeners
As learned in the SOF article, services can be found by creating tracker objects. For this, Remote SOF provides the RemoteServiceTracker
class. The RemoteServiceTracker
class expects, like the SOF ServiceTracker
class, three parameters at the constructor:
- The bundle context object of type
IRemoteBundleContext
- The name of the service which has to be found
- An object implementing the
IRemoteServiceTrackerCustomizer
interface
The following implementation of the class BundleActivator2
shows how to create and use the service tracker object for finding registered services. Unlike SOF where a ServiceReference
object is passed to the addingService
method, here a RemoteServiceReference
object is passed which encapsulates the characteristics (service name, properties, reference to the service object) of a remote service. For calling the remote service, the reference to the remote service object (of type CORBAService_var
) has to be narrowed (similar to the casting of C++ objects) to the correct service object type (Multiplier_var
). Afterwards, the service can be called.
Implementation:
#include "BundleActivator2.h"
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/Properties.h"
#include "sof/framework/remote/corba/RemoteServiceReference.h"
using namespace sof::instantiation;
using namespace sof::framework;
using namespace sof::framework::remote::corba;
BundleActivator2::~BundleActivator2()
{
}
void BundleActivator2::start(IRemoteBundleContext::ConstPtr context)
{
this->tracker = new RemoteServiceTracker( context,
"Multiplier", this );
this->tracker->startTracking();
}
void BundleActivator2::stop(IRemoteBundleContext::ConstPtr context)
{
this->tracker->stopTracking();
delete ( this->tracker );
}
bool BundleActivator2::addingService( const RemoteServiceReference& ref )
{
cout << "[BundleActivator2#addingService] Called." << endl;
if ( ref.getServiceName() == "Multiplier" )
{
Properties props = ref.getServiceProperties();
cout << "[BundleActivator2#addingService] Multiplier instance found." << endl;
cout << "[BundleActivator2#addingService] Properties: "
<< props.toString() << endl;
cout << "[BundleActivator2#addingService] Service reference: "
<< ref.toString() << endl;
Multiplier_var multiplier = Multiplier::_narrow( ref.getRemoteService() );
CORBA::Long result = multiplier->multiply( 8, 15 );
cout << "Result: " << result << endl;
return true;
}
else
{
return false;
}
}
void BundleActivator2::removedService( const RemoteServiceReference& ref )
{
cout << "[BundleActivator2#removedService] Called, ref: " << ref.toString() << endl;
}
REGISTER_REMOTE_BUNDLE_ACTIVATOR_CLASS( "BundleActivator2", BundleActivator2 )
Creating bundle libraries
Now, the two implemented bundles can be tested. For this, there are two possibilities:
- The bundles are built as DLLs and can be started via the interactive console application of Remote SOF (comparable to the example in the SOF article).
- A main method is implemented which starts the
RemoteSOFLauncher
class for loading the bundle at startup.
Here, for this example, the first possibility is chosen again, and here is the code for making the bundles ready for being loaded as a Windows DLL:
Implementation (dll.cpp):
#include <windows.h>
#include "sof/instantiation/ObjectCreator.h"
#include "sof/framework/remote/corba/IRemoteBundleActivator.h"
#define DLL extern "C" __declspec(dllexport)
using namespace sof::framework;
using namespace sof::framework::remote::corba;
using namespace sof::instantiation;
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
DLL IRemoteBundleActivator* createObject( const string &className )
{
ObjectCreator<IRemoteBundleActivator> OC_BUNDLE_ACTIVATOR;
return OC_BUNDLE_ACTIVATOR.createObject( className );
}
Like with SOF, a createObject
method has to be implemented which returns the bundle activator instance of the loaded bundle. Unlike SOF, the method returns an instance of IRemoteBundleActivator
instead of an instance of IBundleActivator
. The 'dll.cpp' file can be reused for the implementation of other bundles which are loaded as Windows DLLs.
The following picture (snapshot of the Visual Studio project files) shows all files which belong to the implementation of the two bundles:
Each bundle contains a bundle activator class (BundleActivato1
, BundleActivator2
) for registering services and service listeners, and the multiplier interface classes (Multiplier.h, Multiplier.cpp). 'dll.cpp' enables the loading of the bundles as a Windows DLL. Additionally, 'bundle1' provides the implementation of the multiplier interface (MultiplierImpl.h, MultiplierImpl.cpp).
After building both bundles as Windows DLLs, the bundles can be tested, which is described in the next section.
Testing the bundles
Generally, before Remote SOF containers can be started, the CORBA naming service and the remote registry process have to be started (please look at the Remote SOF diagram at the beginning).
CORBA naming service
The CORBA naming service is a service which is specified in CORBA. It allows you to associate abstract names with CORBA objects, and allows clients to find those objects by looking up the corresponding names. The used CORBA implementation MICO provides a CORBA naming service which can be started as follows:
- Open a Windows command shell
- Change to the directory 'sof\remote\registry\bin'
- Enter run_ns.bat which executes nsd.exe -ORBIIOPAddr inet:localhost:5000
Note: The passed parameters '-ORBIIOPAddr inet:localhost:5000' define the IP address and port number where the naming service shall run. Here, 'localhost' and port '5000' are chosen.
Remote registry
For starting the remote registry process:
- Open a Windows command shell
- Change to directory 'sof\remote\registry\bin'
- Type run_registry.bat which executes registry.exe -ORBNamingAddr inet:localhost:5000
The registry executable registers a remote registry object at the CORBA naming service which was started before.
Starting the test bundles
At first, a Remote SOF container has to be started for each test bundle:
- Open a Windows command shell
- Change to directory 'sof\remote\console\bin'
- Type run_remote_console.bat which starts the Remote SOF framework and an user interface for entering commands (e.g., for starting and stopping bundles)
The following diagram clarifies what actions have been executed so far. At first, the CORBA naming service was started. Afterwards, the registry was started, which creates a remote registry object (=CORBA object) and registers this object at the CORBA naming service. In the final step, the Remote SOF containers were created which register an observer object at the remote registry.
Now, the test bundles can be loaded. Please enter:
>
stbdll bundle1 BundleActivator1
<SOF_HOME>/remote/examples/bundle1/bin remote_bundle1.dll
at the console of the first Remote SOF container. After entering this command, the first bundle is loaded which registers a service object called 'Multiplier'.
For starting the second test bundle, you have to enter:
> stbdll bundle2
BundleActivator2 <SOF_HOME>/remote/examples/bundle2/bin
remote_bundle2.dll
at the second Remote SOF console. Of course, it is also possible to load both bundles in the same container. The second bundle tracks the service of the first test bundle and is notified about the available service object. 'Bundle2' calls the service object of 'bundle1'. In the first console, 'bundle1' prints out the message 'Multiplier called.', which signals that the service object was called. 'Bundle2' of the second console prints out the result of the multiplication.
Note: It makes no difference in which order the two bundles are started ('bundle1' before 'bundle2' or vice versa).
Conclusion
This article shortly described how distributed bundles can be implemented by using Remote SOF whereas the Remote SOF API differs not very much from the SOF API. Please have a look at the project's website for further documentation.
For the future, the implementation of the following issues is considered:
- Currently, you have to decide for either implementing distributed bundles (using the Remote SOF framework) or 'local' bundles (using the SOF framework). The aim is to enable a mix of distributed and local bundles within one SOF container.
- Platform independent make files.
- GUI for monitoring the Remote SOF processes (remote SOF containers, remote registry etc.).
- Supporting further communication layers besides CORBA (e.g., ICE).
- Making the CORBA naming service disposable for simplifying the startup.
History
- July 8, 2009 - Created the article.