Click here to Skip to main content
15,896,111 members
Articles / Programming Languages / C++

Remote SOF - An OSGI-like modularization framework for C++ supporting distributed software modules

Rate me:
Please Sign up or sign in to vote.
4.58/5 (8 votes)
13 Jul 2009BSD10 min read 31.4K   15  
The article describes the usage of a modularization framework called Remote SOF supporting distributed software modules.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<!---------------------------------------------------------------------------><!-- INTRODUCTION The Code Project article submission template (HTML version) Using this template will help us post your article sooner. To use, just follow the 3 easy steps below: 1. Fill in the article description details 2. Add links to your images and downloads 3. Include the main article text That's all there is to it! All formatting will be done by our submission scripts and style sheets. --><!---------------------------------------------------------------------------><!-- IGNORE THIS SECTION -->


















  


  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  <title>The Code Project</title>
  <style>
BODY, P, TD { font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 10pt }
H2,H3,H4,H5 { color: #ff9900; font-weight: bold; }
H2 { font-size: 13pt; }
H3 { font-size: 12pt; }
H4 { font-size: 10pt; color: black; }
PRE { BACKGROUND-COLOR: #FBEDBB; FONT-FAMILY: "Courier New", Courier, mono; WHITE-SPACE: pre; }
CODE { COLOR: #990000; FONT-FAMILY: "Courier New", Courier, mono; }
  </style>
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  <link rel="stylesheet" type="text/css" href="global.css">
</head>


<body color="#000000" style="background-color: rgb(255, 255, 255);">


















<!---------------------------------------------------------------------------><!------------------------------- STEP 1 --------------------------->
<!-- Fill in the details (CodeProject will reformat this section for you) -->
<pre>Title: SOF - A modularization framework for C++<br>Author: Mattheus <br>Email: mattheus@t-online.de<br>Member ID: 3337502<br>Language: C++<br>Platform: Platform independent<br>Technology: -<br>Level: Intermediate<br>Description: The article describes the usage of a modularization framework called SOF.<br>Section: Platforms, Frameworks &amp; Libraries<br>SubSection: Libraries<br>License: GNU General Public License (GPL)<br></pre>


















<!------------------------------- STEP 2 --------------------------->
<!-- Include download and sample image information. -->
<ul class="download">


















  <li><a href="sof_8070.zip">Download source with examples -
328 Kb</a></li>


















</ul>


















<p><img style="width: 568px; height: 203px;" src="sof_console.gif" alt="Sample Image - maximum width is 600 pixels"></p>


















<!------------------------------- STEP 3 --------------------------->
<!-- Add the article text. Please use simple formatting (<h2>, <p> etc) -->
<h2>Introduction</h2>


















<p>
<em>SOF</em> (<span style="font-weight: bold;">S</span>ervice
<span style="font-weight: bold;">O</span>riented <span style="font-weight: bold;">F</span>ramework)
provides a OS independent
infrastructure for developing component based software. That means the
framework helps developers to build software systems consisting
of&nbsp;modules which are communicating via clearly defined service
interfaces
with other modules of the system. Components can be started and stopped
at runtime and the components are
notified about the lifecycle of other components if necessary. It can
be chosen whether the components are loaded locally (here the component
code is linked to the class which starts the framework) or loaded
dynamically from shared libraries (UNIX) or dynamic link libraries (Windows) for example.  <em>SOF</em>
is implemented in standard C++ and the <em>SOF</em>
API is very similar to the OSGI API (see <a target="_blank" title="External link to http://www.osgi.org" href="http://www.osgi.org/" class="externalLink">OSGI</a>).&nbsp;</p>


















<h2>Using the Code</h2>


















<h3>What does a module consist of?</h3>


















<p>Each module (also called 'bundle') consists of following code
parts:</p>


















<ul>


















  <li>A class which implements the&nbsp;<code>sof::framework::IBundleActivator&nbsp;</code>interface.&nbsp;</li>


















  <li>One or several service interfaces which can be called by
other bundles.</li>


















  <li>Optionally: A class providing an interface for loading a dynamic link
library or shared library&nbsp;</li>


















  <li>Classes implementing the business logic</li>


















</ul>


















The following example describes the implementation of two bundles
(called 'bundle1' and 'bundle2') which
communicate via one service interface. The first bundle registers a
service object of type <code>IMultiplier</code>. The second
bundle listens for
service objects of type&nbsp;<code>IMultiplier</code>. As soon as
a service object of type&nbsp;<code>IMultiplier</code> is
available the second bundle calls the <code>getValue()</code>
method of the service
object.
<h3>The&nbsp;<code>IBundleActivator</code> interface</h3>


















<div>The <code>IBundleActivator</code> interface
provides three methods (destructor, <code>start</code>, <code>stop</code>),
which have to be
implemented for creating, starting and stopping a bundle. The example
below shows the implementation of the&nbsp;<code>IBundleActivator</code>
interface for 'bundle1' whereas the destructor, <code>start</code>
and <code>stop</code> method
are not filled with code yet.<br>


















<br>


















<span style="font-weight: bold;">Header file:</span><br>


















<pre>#ifndef BUNDLE_ACTIVATOR<br>#define BUNDLE_ACTIVATOR1_H<br><br>#include "sof/framework/IBundleActivator.h"<br>#include "sof/framework/IBundleContext.h"<br><br>using namespace sof::framework;<br><br>class BundleActivator1 : public IBundleActivator<br>{<br>&nbsp;&nbsp;&nbsp; public:<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; virtual ~BundleActivator1();<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; virtual void start( IBundleContext::ConstPtr context <br>	virtual void stop( IBundleContext::ConstPtr context );<br>};<br>#endif<br></pre>


















<span style="font-weight: bold;">Implementation:</span><br>


















</div>


















<pre>#include "BundleActivator1.h"<br><br>#include "sof/instantiation/ObjectCreator.h"<br><br>using namespace sof::instantiation;<br>using namespace sof::framework;<br><br>BundleActivator1::~BundleActivator1() <br>{<br>	// Deallocate memory<br>}<br><br>void BundleActivator1::start(IBundleContext::ConstPtr context) <br>{<br>	// Add code for registering services and service listeners<br>}<br><br>void BundleActivator1::stop(IBundleContext::ConstPtr context) <br>{<br>	// Add code for deregistering services and service listeners<br>}<br><br>REGISTER_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )<br><br><br></pre>


















<p>Only the framework calls the <code>start</code>
and <code>stop</code> methods&nbsp;for starting and
stopping the
bundle. When these methods are called, a parameter of type
<code>IBundleContext</code> is passed which allows
communicating with the framework (registering/deregistering services,
service listeners
etc.). The framework is also responsible for creating an instance of the
bundle activator class, before the bundle is started. For this the type
and the name of the bundle activator class (here&nbsp;<code>BundleActivator1</code>)
have
to be registered at the framework by using the
<code>REGISTER_BUNDLE_ACTIVATOR_CLASS</code> macro.</p>


















<span style="font-weight: bold;"><br>


















</span>
<h3>Services</h3>


















Bundles communicate with other bundles via services. Each service class
has to implement the <code>IService </code>interface
which
does not provide any
methods. It is only a marker interface. Our interface for the
communication between 'bundle1' and 'bundle2'&nbsp;defines only one
method which multiplies two integer values and returns the result of
the multiplication.&nbsp;<br>


















<br>


















<span style="font-weight: bold;">Header file:</span><br>


















<pre>#ifndef IMULTIPLIER_H<br>#define IMULTIPLIER_H<br><br>#include "sof/framework/IService.h"<br><br>using namespace sof::framework;<br><br>class IMultiplier : public IService<br>{<br>	public:<br>		virtual int multiply( int x, int y ) = 0;<br>};<br><br>#endif</pre>


















The implementation of the service interface is done in the 'IMultiplierImpl.h' file which is not shown here.<br>














<br>


















<h3>Registering and Deregistering Services</h3>


















After the service interfaces are defined  (here only one interface) and implemented, the
code for registering and deregistering the service can be implemented.
For this the <code>IBundleContext</code> instance which is
passed to the <code>start</code> and
<code>stop</code> method of the bundle activator class is
used. The <code>start</code> method of&nbsp;'bundle1' creates an instance of the <code>IMultiplierImpl</code> class at first. Then a <code>Properties</code> object is created which holds key/value pairs of type <code>string</code> and allows specifying the service object in detail. Finally the service object is registered at the framework by calling the <code>registerService</code> method of the <code>IBundleContext</code> object. Three parameters have to be passed to the registerService call:<br>















<ul>















  <li>The name of the service&nbsp;(typically the name of the service class)</li>















  <li>The service object itself</li>















  <li>The&nbsp;<code>Properties</code> object</li>















</ul>















After the registration process of the service object, the multiplier can be used by other bundles.<br>
















For the case that the service object should be no longer available for
other bundles, the service can be unregistered which is done here in
the <code>stop</code> method.<br>















<br>


















<span style="font-weight: bold;">Implementation:</span><br>


















<pre>#include "BundleActivator1.h"<br><br>#include "sof/instantiation/ObjectCreator.h"<br>#include "sof/framework/Properties.h"<br><br>#include "IMultiplier.h"<br>#include "IMultiplierImpl.h"<br><br>using namespace sof::instantiation;<br>using namespace sof::framework;<br><br>BundleActivator1::~BundleActivator1() <br>{<br>	// Deallocate memory<br>}<br><br>void BundleActivator1::start(IBundleContext::ConstPtr context) <br>{	<br>	this-&gt;service = new IMultiplierImpl();	<br><br>	Properties props;<br>	props.put( "instance", "1" );<br><br>	this-&gt;serviceReg = context-&gt;registerService( "IMultiplier", this-&gt;service, props ); <br>}<br><br>void BundleActivator1::stop(IBundleContext::ConstPtr context) <br>{<br>	this-&gt;serviceReg-&gt;unregister();<br>	delete this-&gt;serviceReg;<br>	delete this-&gt;service;<br>}<br><br>REGISTER_BUNDLE_ACTIVATOR_CLASS( "BundleActivator1", BundleActivator1 )<br><br><br></pre>
















<br>


















<h3>Registering and Deregistering Service Listeners</h3>


















The previous chapter described how to register a service object. Now we
talk about how registered services can be used by other bundles. For
this you have to create a&nbsp;<code>ServiceTracker</code> object which expects three parameters in the constructor:<br>















<ul>















  <li>The&nbsp;<code>IBundleContext</code> object</li>















  <li>The name of the service which has to be found (here the name of the service class)</li>















  <li>An object implementing the <code>IServiceTrackerCustomizer</code> interface (in the following example it is implemented by the bundle activator class)</li>















</ul>















As soon as the&nbsp;<code></code><code>startTracking</code> method of the&nbsp;<code>ServiceTracker</code>
instance is called, the service tracker begins to listen for registered
services of name 'IMultiplier'. If there is a registered service object
of name 'IMultiplier' (no matter the service was registered before or
after the service tracker was started) the framework notifies the <code>IServiceTrackerCustomizer</code>
object (here the bundle activator which is implementing this
interface)&nbsp;of the existing service object by calling the&nbsp;<code>addingService</code> method. In the&nbsp;<code>addingService</code>
method you can ask for the name or the properties of the found service
in order to check whether it is the service object you are interested
in. If so the service object can be retrieved from the service
reference and casted to the service interface (<code>IMultiplier</code>). Now the service can be used. It must be pointed out that t<span style="font-weight: bold;"></span>he&nbsp;<code>addingService</code>
method has to return a boolean value. You have to return true, if you are
interested in using the found service, otherwise false.<br>














For the case that the&nbsp;<code>IMultiplier</code> service is deregistered by the other bundle the relating service trackers are notified by a&nbsp;<code>removedService</code> method call.<br>














In order to stop listening for service objects the&nbsp;<code>stopTracking</code> method of the&nbsp;<code>ServiceTracker</code> object has to be called.<br>














<br>


















<span style="font-weight: bold;">Implementation:</span>


<pre>#include "BundleActivator2.h"<br><br>#include &lt;iostream&gt;<br><br>#include "sof/instantiation/ObjectCreator.h"<br><br>#include "IServiceA.h"<br><br>using namespace std;<br>using namespace sof::instantiation;<br><br>BundleActivator2::~BundleActivator2() <br>{<br>	// Deallocate memory<br>}<br><br>void BundleActivator2::start(IBundleContext::ConstPtr context) <br>{<br>	this-&gt;tracker = new ServiceTracker( context, "IMultiplier", this );<br>	this-&gt;tracker-&gt;startTracking();<br>}<br><br>void BundleActivator2::stop(IBundleContext::ConstPtr context) <br>{<br>	this-&gt;tracker-&gt;stopTracking();<br>	delete ( this-&gt;tracker );<br>}<br><br>bool BundleActivator2::addingService( const ServiceReference&amp; ref )<br>{<br>	if ( ref.getServiceName() == "IMultiplier" )<br>	{<br>		Properties props = ref.getServiceProperties();<br>		if ( props.get( "instance" ) == "1" )<br>		{<br>			this-&gt;service = static_cast&lt;IMultiplier*&gt; ( ref.getService() );<br>			cout &lt;&lt; "[BundleActivator2#addingService] Calling IMultiplier..." &lt;&lt; endl;<br>			int value = this-&gt;service-&gt;multiply( 47, 11 );<br>			cout &lt;&lt; "[BundleActivator2#addingService] Returned value of IMultiplier: " &lt;&lt; value &lt;&lt; endl;<br>			return true;<br>		}<br>		else<br>		{<br>			return false;<br>		}<br>	}<br>	else<br>	{<br>		return false;<br>	}<br>}<br><br>void BundleActivator2::removedService( const ServiceReference&amp; ref )<br>{<br>}<br><br>REGISTER_BUNDLE_ACTIVATOR_CLASS( "BundleActivator2", BundleActivator2 )<br><br></pre>


















<span style="font-weight: bold;"></span><br>














<br>














<h3>Creating Bundle Libraries</h3>














Up till now we've implemented the code for two bundles. One bundle registers a service object of type&nbsp;<code>IMultiplier</code>,
the other bundle listens for this service object and calls it. Now we
want to create two&nbsp;bundle libraries, one library for each
bundle.&nbsp;Since this code example ought to be run on the Windows
platform we have to implement a Windows DLL:<br>













<br>











<span style="font-weight: bold;">Implementation (dll.cpp):</span><br>













<pre>#include &lt;windows.h&gt;<br><br>#include &lt;stdlib.h&gt;<br>#include &lt;string&gt;<br>#include &lt;iostream&gt;<br><br>#include "sof/instantiation/ObjectCreator.h"<br>#include "sof/framework/IBundleActivator.h"<br><br>#define DLL extern "C" __declspec(dllexport)<br><br>using namespace std;<br>using namespace sof::instantiation;<br>using namespace sof::framework;<br><br>BOOL APIENTRY DllMain( HANDLE hModule, <br>                       DWORD  ul_reason_for_call, <br>                       LPVOID lpReserved<br>					 )<br>{				<br>	return TRUE;<br>}<br><br><br>DLL IBundleActivator* createObject( const string &amp;className )<br>{	<br>	ObjectCreator&lt;IBundleActivator&gt; OC_BUNDLE_ACTIVATOR;<br>	return OC_BUNDLE_ACTIVATOR.createObject( className );					<br>}<br></pre>













The <code>dllMain</code> method represents the entry point for the DLL where initialization calls (not necessary) can be done. The&nbsp;<code>dllMain</code>
method is defined by the Windows platform and is called when the DLL is
loaded. <span style="font-style: italic;">SOF</span> itself requires an implementation of the&nbsp;<code>createObject</code> method which provides the functionality for creating <code>IBundleActivator</code> instances. This 'dll.cpp' file does not have to be changed for the implementation of other bundles. It can be always reused.<br>









<br>











<img style="width: 205px; height: 353px; float: left;" alt="project view" src="project_view.gif">The
picture on the left side shows a snapshot of the project view in Visual Studio. The
first bundle is implemented in the 'sof_examples_bundle1' project and
contains following files:<br>









<ul style="margin-left: 160px;">









  
  
  
  
  
  
  
  
  
  <ul>









    <li>The header and source file of the bundle activator (BundleActivator1)</li>









  
  
  
  
  
  
  
  
  
  </ul>









  
  
  
  
  
  
  
  
  
  <ul>









    <li>The interface definition (IMultiplier.h) and implementation (IMultiplierImpl.h) of the service interface</li>









  
  
  
  
  
  
  
  
  
  </ul>









  
  
  
  
  
  
  
  
  
  <ul>









    <li>The definition of the DLL interface ('dll.cpp')</li>









  
  
  
  
  
  
  
  
  
  </ul>









</ul>









The project view of the second bundle (project 'sof_examples_bundle2') contains:
<ul style="margin-left: 160px;">









  
  
  
  
  
  
  
  
  
  <ul>









    <li>The bundle activator</li>









  
  
  
  
  
  
  
  
  
  </ul>









  
  
  
  
  
  
  
  
  
  <ul>









    <li>Only the service interface (IMultiplier.h) (not an implementation of the service)</li>









  
  
  
  
  
  
  
  
  
  </ul>









  
  
  
  
  
  
  
  
  
  <ul>









    <li>The definition of the DLL interface</li>









  
  
  
  
  
  
  
  
  
  </ul>









</ul>









Each project can now be build as Windows DLL and then there will be two bundle libraries: 'bundle1.dll' and 'bundle2.dll'.<br>




The next chapter describes how the bundles can be started which is
implemented in the 'sof_examples.cpp' file of the 'sof_examples'
project.<br>









<br>









<br>









<br>









<h3><br>





</h3>





<br>





<br>





<br>






<h3>Starting the Framework</h3>















For starting the framework an instance of class&nbsp;<code>Launcher</code> has to be created (see 'sof_examples.cpp' file). The&nbsp;<code>Launcher</code> class represents a template based class which allows the specification of a<br>












<ul>












  <li>Threading policy</li>












  
  
  
  
  
  
  
  
  
  
  
  
  <ul>












    <li>Influences the threading behaviour of the framework.</li>












  
  
  
  
  
  
  
  
  
  
  
  
  </ul>












  <li>Creation policy</li>












  
  
  
  
  
  
  
  
  
  
  
  
  <ul>












    <li>Defines the way a bundle is loaded from a library (e.g. Windows DLL, Unix shared library etc.).</li>












  
  
  
  
  
  
  
  
  
  
  
  
  </ul>












</ul>











In order to keep OS dependent code away from the framework, the
threading and creation behaviour can be easily adapted to any operating
system by using this template solution. The framework only provides the <code>SingleThreaded</code> class and the&nbsp;<code>WinDllCreator</code> class as implementations of the threading and creation policies. <br>










The&nbsp;<code>SingleThreaded</code> class requires that framework
calls (e.g. registering/deregistering services and service listeners)
are done in a single thread, otherwise race conditions can occur.<br>










The&nbsp;<code>WinDllCreator</code> class can be only used for the Windows platform and supports the loading of bundles which are built as Windows DLLs.<br>








<br>








Afterwards the&nbsp;<code>Launcher</code> class is created, the bundle configuration for each bundle must be specified. A&nbsp;<code>BundleConfiguration</code>&nbsp;contains all relevant information of a bundle for creating and starting it:<br>








<ul>








  <li>The name of the bundle</li>








  <li>The name of the bundle activator class</li>








  <li>The directory where the bundle library is located, e.g. '.' for the current directory or 'c:/temp' </li>








  <li>The name of the bundle library</li>








</ul>








Now the bundle configuration can be passed to the&nbsp;<code>Launcher</code> instance by calling the <code>start</code> method. This is the trigger for starting the framework, creating and starting the bundle activators.<br>






<br>








<span style="font-weight: bold;">Main method of 'sof_examples.cpp':</span>


<pre>#include &lt;iostream&gt;<br>#include &lt;vector&gt;<br><br>#include "sof/framework/Launcher.h"<br>#include "sof/framework/Global.h"<br>#include "sof/config/BundleConfiguration.h"<br>#include "sof/instantiation/win/WinDllCreator.h"<br>#include "sof/util/threading/SingleThreaded.h"<br><br>using namespace std;<br><br>using namespace sof::framework;<br>using namespace sof::config;<br>using namespace sof::util::threading;<br>using namespace sof::instantiation::win;<br><br>int main(int argc, char* argv[])<br>{<br>	Launcher&lt;SingleThreaded,WinDllCreator&gt; launcher;<br><br>	// Specifying the bundle configuration<br>	BundleConfiguration bundle1( "bundle1", "BundleActivator1", ".", "sof_examples_bundle1.dll" );<br>	BundleConfiguration bundle2( "bundle2", "BundleActivator2", ".", "sof_examples_bundle2.dll" );<br><br>	vector&lt;BundleConfiguration&gt; configuration;<br>	configuration.push_back( bundle1 );<br>	configuration.push_back( bundle2 );<br><br>	// Starting the framework<br>	launcher.start( configuration );<br><br>	// Starting the administration console for interacting with the framework<br>	launcher.startAdministrationBundle();<br><br>	return 0;<br>}</pre>


















The following sequence chart shows the startup procedure in a simplified way.<br>








<br>








<img style="width: 693px; height: 624px;" alt="" src="starting_bundles_sequence.gif"><br>




After the executable file 'sof_examples.exe' is started,
both bundles are started and we can see that 'bundle2' calls the
'multiplier' service of&nbsp;'bundle1' (see&nbsp;highlighted lines in
the picture below). <br>




<br>




<img style="width: 603px; height: 451px;" alt="" src="console_after_startup.gif"><br>




<br>




<br>




As soon as all configured bundles (here: 'bundle1' and 'bundle2') are started, the <span style="font-style: italic;">SOF</span> console allows&nbsp;entering commands in order to interact with the
framework. Please enter 'help' for displaying all available commands. There are commands for&nbsp;
<ul>




  <li>Showing all relevant information of a bundle (e.g. registered services, service listeners and used services)</li>



</ul>



<div style="margin-left: 40px;"><img style="width: 571px; height: 202px;" alt="" src="dump_bundle.gif"></div>



<ul>




  <li>Starting and stopping further bundles</li>



</ul>



<ul>




  <li>Listing the names of all started bundles</li>



</ul>



<div style="margin-left: 40px;"><img style="width: 203px; height: 67px;" alt="" src="dump_all_bundles.gif"></div>



<ul>




</ul>




<h2>Conclusion</h2>














This article gave a small introduction to the <span style="font-style: italic;">SOF</span> framework which provides following features:




<br>








<ul>


  <li>It can be configured which bundles ought to be started at framework startup.</li>


  <li>




Software modules can be started and stopped&nbsp;during runtime (via <span style="font-style: italic;">SOF</span> console), so a bundle implementation can be replaced by an other one.</li>








  <li>




Loose coupling between modules (bundles): Software modules only communicate via service interfaces.</li>








  <li>



The framework can be easily adapted&nbsp;to specific operating systems by using templates.</li>








</ul>








You'll find more documentation on the project's web site <a href="http://sof.tiddlyspot.com/">http://sof.tiddlyspot.com</a>. Please do not hesitate to give me your opinion about this framework or the article.









<h2>History&nbsp;</h2>















<span id="intelliTXT">July 25, 2008 - Created the article.</span>







</body>
</html>

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer
Germany Germany
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions