Skip to main content

Build a simple C++ service component, Part 1: A quick tour of the C++ API for the Service Component Architecture

Ed Slattery (slattery@uk.ibm.com), Software Engineer, IBM UK
Ed Slattery photo
Ed Slattery joined the Java Technology Centre as a software engineer in 1999. He has worked on shiraz reusable VM technology, graphics (AWT/Swing) and then the incubator projects, where he has written the core of the C++ implementation of SDO. This core is now been contributed to the Tuscany incubator project with the Apache Software Foundation.
Pete Robbins (robbins@uk.ibm.com), Software Engineer, IBM UK
Pete Robbins photo
Pete Robbins is a software engineer at IBM Hursley, UK. He joined IBM in 1981 and has worked in a variety of development and technical planning roles. Most recently, he has been working on the Tuscany open source SOA project, where he developed the XML serialization of SDO in C++. He then moved on to develop the initial SCA for C++ implementation that is part of the Apache incubator project. He is also involved in the SCA specification collaboration.
Andrew Borley (borley@uk.ibm.com), Software Engineer, IBM UK
Andrew Borley photo
Andrew Borley is an IT Specialist at IBM Hursley, UK. He has held various roles including developer, team leader and project manager and has worked with various technologies in his 7 years at IBM. Since 2001 he has been working with customers on Web Services and Grid projects, and his current role is developing the SCA for C++ implementation within the Apache Tuscany open source SOA project.

Summary:  Get acquainted with the API that will work with Apache Tuscany SCA for C++. You'll see how to access the main elements of the API for a rapid startup.

View more content in this series

Date:  08 Sep 2006
Level:  Intermediate
Activity:  1147 views

Building and wiring a simple C++ Service Component with Apache Tuscany for C++

About Tuscany

Apache Tuscany is a project undergoing incubation at the Apache Software Foundation. One of the goals of the project is to produce a C++ runtime that implements the following Service Component Architecture (SCA) specifications (see Resources for more information):

  1. SCA Assembly Model
  2. SCA C++ Client and Implementation

In this article, we'll walk through the development and deployment of a Service Component in C++ for the Apache Tuscany C++ runtime, based on the Milestone 2 release.

Introduction

The Tuscany C++ Service Component Architecture (SCA) runtime allows you to build SCA components using standard C++, Python or Ruby code, and deploy them to a location where the SCA runtime can locate and load them. In order to achieve this dynamic loading of components, some description files are required by the runtime, and these artifacts, alongside your own header files, are used to generate proxies and wrappers that enable the invocation of your component from other components or client code as if it were a local C++ object.

We'll create a simple SCA component, then a second component and wire the two together.

We use Microsoft Visual studio as the development environment, but you can use a command line compiler and a text editor. You'll see how to set up the studio project as well as developing the application.

NOTE: Tuscany SCA is dependent on the Tuscany SDO project and the Apache Axis2/C project. The Tuscany SCA/SDO libraries and the Apache Axis libraries must be on your PATH somewhere before proceeding. See the project download instructions for more information.

The Tuscany SCA C++ runtime will need to know where the SCA composites and their components are deployed. The deployment root is identified by an environment variable, TUSCANY_SCACPP_SYSTEM_ROOT. We will set this now so that we can run our test program from within Visual Studio. If you are using the command line, you don't need to set these until run time.

TUSCANY_SCACPP_SYSTEM_ROOT specifies a path to where the runtime will look for deployed composites, which will be explained later.

Use the control panel to set: TUSCANY_SCACPP_SYSTEM_ROOT=c:\mybasicsample

Go to Control Panel, then System, then choose the Advanced tab, then the Environment Variables button. Click the New button and set Variable name to TUSCANY_SCACPP_SYSTEM_ROOT and Variable value to c:\mybasicsample. Then click OK to set the environment variable.

Create a directory in the C:\ drive called mybasicsample, with a subdirectory called samplecomposite.

Now we are ready to deploy. We perhaps ought to write something deployable.

A brief recap of the SCA specification (you have read the spec - right?) will remind us that an SCA system consists of one or more composites. Composites can contain components, services and references, where components provide some functionality, services expose that functionality and references allow the components to call out to other services. The model is also recursive: a component can itself be a composite, so it's easy to create layers of more and more finely grained functionality. In the C++ world, there are a set of descriptive XML files which are used both at compile time to generate service proxies and wrappers, and at runtime to locate services offered. It is worth getting to know these files well before we start developing. The file describing a composite must have .composite as it's file suffix and a .composite file must be found in the TUSCANY_SCACPP_SYSTEM_ROOT directory or any subdirectory. We define a "root composite" file that specifies which components are used in the entire SCA system and generally contains components which are implemented as composites themselves, allowing the real functionality to be separated into discrete parts that can then be assembled up into the entire system. In our case, we create a mybasicsample.composite file in the mybasicsample directory containing the following XML:


Listing 1. Root composite file
        
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0" name="MyBasicSample">
        <component name="SampleComposite.FloatConverter">
            <implementation.composite name="SampleComposite"/>
       	</component>
</composite>
      

Listing 1 tells the SCA runtime that the component "SampleComposite.FloatConverter" is implemented by a composite named "SampleComposite", so we must now build that composite.

The root composite file is purely a runtime artifact, it serves no purpose at compile time. Other files (composite files and componentType files) describe the composite "SampleComposite" and the components within it such that they can be found and invoked at runtime; these files also help the code generator build wrappers and proxies for the services. We will meet these files in more detail during the development process below.

Let's now go back to the beginning of the story. We want to deploy a C++ class as a service and put our service into a composite called "SampleComposite". The following the steps will show you how.

First off, at the risk of being redundant (many of you have no doubt done this before), we will create a sample C++ application.

NOTE: Before beginning the development process, you must have either downloaded the Tuscany SCA and SDO source code and built it, or have downloaded the binary version, so you will later need to tell your project where to find the SCA runtime. Set up two environment variables now called TUSCANY_SCACPP and TUSCANY_SDOCPP - if you are using the binary version of Tuscany, set these to point to the tuscany_sca_cpp-1.0-incubator-M2-bin and tuscany_sdo_cpp-1.0-incubator-M2-bin directories respectively. If you have compiled the source version, set the variables to point to the deploy directories of the SCA and SDO projects, under which you can find the bin,lib and include directories.

To begin we create an abstract base class which represents the service we want to expose. This is just the equivalent of defining a java interface. The header file we create here will be used by a client application to understand the available service interface.

Here is the class, in a header file called "Sample.h":


Listing 2. Sample class interface
        
class Sample
{
public:
    // we will get a float from a string
    virtual float toFloat(const char* input) = 0;

    // we will convert a float to a string
    virtual char* toString(float value) = 0;
};
      

We need to tell the code generator which component type will expose this abstract behaviour, so we will create a componentType file. The componentType file links the name of the implementing class with the abstract behaviour by naming convention. The file is named after the implementation class, and contains a reference to the abstract class, so in this case the file will be called "SampleImpl.componentType":


Listing 3. componentType file
        
<?xml version="1.0" encoding="UTF-8"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0">
	<service name="SampleService">
		<interface.cpp header="Sample.h"/>
	</service>
</componentType>
      

Listing 3 tells the code generator that a component named "SampleService" will expose the behavior found in the header file "Sample.h". It also tells the runtime that the service which is implemented by the header "SampleImpl.h" is the SampleService.

So, in Visual Studio we create a win32 dll project, and insert the header file above. We call the project "TheSampleProject", so we would expect it to produce a dll by default called "TheSampleProject.dll". Now create an implementation for the service, naturally calling the files SampleImpl.cpp and SampleImpl.h:


Listing 4. Service implementation header
        
#include "Sample.h"

class SampleImpl : public Sample
{
public:
    SampleImpl();
    virtual ~SampleImpl();

    // Sample interface
    virtual float toFloat(const char* input);

    virtual char* toString(float value);

};
      


Listing 5. Service implementation code
        
#include "SampleImpl.h"
#include <stdio.h>
#include <stdlib.h>

SampleImpl::SampleImpl()
{
}
    
SampleImpl::~SampleImpl()
{
}

// Sample interface

float SampleImpl::toFloat(const char* input)
{
    if (input != 0)
    {
        float f = (float)atof(input);
        return f;
    }
    return (float)0;
}

char* SampleImpl::toString(float value)
{
    char * r = new char[100];
    sprintf(r,"The float is : %5.5f", value);
    return r;
}
      

At this point, the DLL will compile and link, so one could write a test program to use the Sample API and test it. Skip that step, as that is really standard stuff.

Now we introduce the remaining pieces of the jigsaw which link the "SampleComposite" composite to the code we have written.

The SCA runtime needs to know where the service is located. In the client code, we shall be using the runtime to get a CompositeContext and to call "locateService". The runtime can determine the default CompositeContext in 2 ways:

  1. From the environment variable TUSCANY_SCACPP_DEFAULT_COMPONENT=<componentname> so in this instance it would be: TUSCANY_SCACPP_DEFAULT_COMPONENT=SampleComposite.FloatConverter
  2. The client code can specify the default module using the TuscanyRuntime class. This is the method we use in this Sample.

Now the runtime just needs to locate the service within the module, and find the name of the associated DLL. This is done by reading another composite file (which is the one we referenced from the root composite file above) - this time the composite will define a C++ component that uses our DLL. Here is the XML:


Listing 6. The sample.composite file
        
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
    name="SampleComposite">
    <component name="SampleComponent">
        <implementation.cpp library="TheSampleProject" header="SampleImpl.h"/>
    </component>
</composite>
      

We now know that the composite SampleComposite contains a component called SampleComponent. SampleComponent is implemented in TheSampleProject.dll, and its methods are described in the header "SampleImpl.h".

We should now add the composite and componentType files to our studio project, just to keep track of them.

Our SCA runtime now has all the information it needs to locate the service and call a method. However, we cannot write a client which directly calls the service, otherwise we would get a dependency at compile time on our dll, so we need a proxy to call, and the library needs a wrapper to wrap the service. These will be generated by the Tuscany scagen utility. scagen takes the header file defined in the composite file, and builds the required code. You just need to run it, telling it where to find the file, and where to write the output. Let us suppose our composite file and the header files of our project are in c:\mybasicsample\MyServiceProject. In the directory c:\mybasicsample type:

scagen -dir MyServiceProject -output MyServiceProject
      

NOTE: But first, of course, you need to have scagen available, and you need to know that scagen is a java application. If you downloaded the binary, then scagen is already in the "bin" directory, otherwise you have to build it. To build scagen, you just need a java JDK (1.4.2 or above) installed, and Apache Ant installed. Go to the tools/scagen directory in the sca project and type "ant". The scagen utility will be built in the bin directory.

The four files generated follow a naming convention as shown in Listing 7.


Listing 7. scagen utility
        
<headername>_<servicename>_Proxy.cpp 
<headername>_<servicename>_Proxy.h 
<headername>_servicename>_Wrapper.cpp
<headername>_servicename>_Wrapper.h
      

Add these new files to your visual studio project.

Your project is now dependent on SCA and the SCA C++ extension, and will not build until you tell it where to find SCA headers and libraries. Add $(TUSCANY_SCACPP)/include, $(TUSCANY_SCACPP)\extensions\cpp\include and $(TUSCANY_SDO)/include to the path for included header files (in Visual Studio 7 go to Project -> Properties -> C/C++ -> General -> Additional Include Directories). Add $(TUSCANY_SCACPP)\lib, $(TUSCANY_SCACPP)\extensions\cpp\lib,$(TUSCANY_SDOCPP)\lib as additional library paths (Project -> Properties -> Linker -> General -> Additional Library Directories) and add tuscany_sca.lib, tuscany_sca_cpp.lib and tuscany_sdo.lib to the libraries required (Project -> Properties -> Linker -> Input -> Additional Dependencies).

Now your project will compile again, and we are ready to write a client.

The client program will be in a new console executable project, so we can go ahead and create one of those. The client is dependent on the SCA runtime, but clearly must not be dependent on the dll project. However the client does need to know what methods the service offers, so finally we get to use the abstract base class which we defined right at the beginning. The Sample.h header should be added to the client project. The client project is dependent on the SCA runtime, so we must also add all the libs and headers as we just did for the dll project.

Now create the client cpp file:


Listing 8. Client cpp file
        
#include "..\MyServiceProject\Sample.h"
#include "osoa/sca/sca.h"
#include <iostream>
#include <stdlib.h>
#include <tuscany/sca/core/TuscanyRuntime.h>

using namespace osoa::sca;
using namespace tuscany::sca;
using namespace std;

int main(int argc, char* argv[])
{
	
    if (argc != 2)
    {
        cout << "MyClient.exe: Would you cast me adrift without my float?" << endl;
    	return 0;
    }
    
    // Set the default component
    TuscanyRuntime runtime("SampleComposite.FloatConverter");

    try	{
        runtime.start(); // bootstrap the Tuscany Runtime

        // Get the current composite context (which is the default)
        CompositeContext myContext = CompositeContext::getCurrent();
        // Get an Sample service
        Sample *theService = 
              (Sample*) myContext.locateService("SampleComponent/SampleService");
        if (theService == 0) {
            cout << "MyClient.exe: Unable to find SampleService" << endl;
        }
        else {
             try {
                 float result = theService->toFloat(argv[1]);
                 cout << "The float returned is "  << result << endl;
                 char *str = theService->toString(result + 1111);
                 cout << "The string came back as \"" << str << "\"" << endl;
             }
             catch (char* x) {
                 cout << "MyClient.exe: exception caught: " << x << endl;
             }
        }
    }
    catch (ServiceRuntimeException& ex) {
        cout << "MyClient.exe: runtime exception caught: " << ex << endl;
    }
    runtime.stop(); // stop the Tuscany Runtime
    return 0;
}
      

And there we have it. Your first SCA application calling a single service from a client. To deploy it, you must put the runtime artifacts in the deployment directory, as follows:

To the TUSCANY_SCACPP_SYSTEM_ROOT directory:

  • mybasicsample.composite

To the TUSCANY_SCACPP_SYSTEM_ROOT/samplecomposite directory:

  • TheSampleProject.dll
  • SampleImpl.componentType.
  • sample.composite

Now, with tuscany_sca.dll, tuscany_sca_cpp.dll, tuscany_sdo.dll and the Axis2C dlls somewhere on your path, and the TUSCANY_SCACPP_SYSTEM_ROOT environment variable set, your client should run with the following commands:

C:\>cd path\to\my\projects\MyClient\Debug
C:\path\to\my\projects\MyClient\Debug>MyClient.exe 123.456
      

And the results should look something like:

The float returned is 123.456
The string came back as "The float is : 1234.456"
      

Well done! - you have built and run a component. However, a single component is not very much use - you do not buy a resistor and say " oh look - less current". You want to build a radio, so let's talk about wiring.

A service may invoke another service, and these can be wired together such that the runtime resolves the service usage in much the same way as it resolves the client invocation. The key concept is the ComponentContext. We have seen that the runtime has a CompositeContext allowing it to find the service. Once in the service, there is also a ComponentContext which allows the runtime to locate services upon which the current service depends.

In a later article in this series, you will see that these services can be exposed as Web services, and equally our SCA services can depend on incoming web services, but for now, we are just going to wire together two services running in the same application space.

First, we need to create a second service. We do not need to tell you how to do that. We'll just create a simple service which returns a string:


Listing 9. Simple service example interface header
        
class StringThing
{
public:

    // we will get a string
    virtual char* getString() = 0;
};
      


Listing 10. Simple service example implementation header
        
#include "StringThing.h"

class StringThingImpl : public StringThing
{
public:
    StringThingImpl();
    virtual ~StringThingImpl();

    // interface

    virtual char* getString();

};
      


Listing 11. Simple service example implementation code
        
#include "StringThingImpl.h"
#include <stdio.h>

StringThingImpl::StringThingImpl()
{
}
    
StringThingImpl::~StringThingImpl()
{
}

// interface

char* StringThingImpl::getString()
{
    char * r = new char[100];
    sprintf(r,"The string from stringthing");
    return r;
}
      

Now before reusing scagen, we need to create a new componentType file for the new component, and we need to add the new component to the sample.composite file. Finally we also need to tell the system that our first componentType will reference our second. This is done by altering the SampleImpl.componentType file.

This is the new componentType (StringThingImpl.componentType):


Listing 12. StringThingImpl.componentType
<?xml version="1.0" encoding="UTF-8"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0">
    <service name="StringService">
        <interface.cpp header="StringThing.h"/>
    </service>
</componentType>
      

This is the altered sample.composite file:


Listing 13. Altered sample.composite file
<?xml version="1.0" encoding="UTF-8"?>
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
    name="SampleComposite">
    <component name="SampleComponent">
        <implementation.cpp library="TheSampleProject" header="SampleImpl.h"/>
        <reference name="stringService">StringThingComponent/StringService</reference>
    </component>
    <component name="StringThingComponent">
        <implementation.cpp library="TheSampleProject" header="StringThingImpl.h"/>
    </component>
</composite>
      

And this is the altered SampleImpl.componentType file:


Listing 14. altered SampleImpl.componentType
<?xml version="1.0" encoding="UTF-8"?>
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0">
    <service name="SampleService">
        <interface.cpp header="Sample.h"/>
    </service>
    <reference name="stringService">
        <interface.cpp header="StringThing.h"/>
    </reference>
</componentType>
      

Note that we used a lower case "stringService" to refer to the upper case service "StringService". On re-running scagen you will notice six extra files have been created. These are the proxy and wrapper header and cpp files for the "StringThing" component as well as proxy header and cpp files for the reference "stringService" when called from SampleImpl. You will need to add these new files to your project.

Finally, here is some new code for the Sample service, which resolves the new service using the ComponentContext:


Listing 15. Sample service
        
#include "StringThing.h"
#include "osoa/sca/ComponentContext.h"
#include "osoa/sca/ServiceRuntimeException.h"
using namespace osoa::sca;

...

char* SampleImpl::toString(float value)
{
    char * r = new char[100];
    // now make a service call to stringthing...
    try {
        ComponentContext myContext = ComponentContext::getCurrent();
        StringThing* stringService = (StringThing*)myContext.getService("stringService");

        if (stringService == 0)
        {
            printf("Unable to find string thing service\n");
        }
        else
        {
            char* chars = stringService->getString();
            if (chars != 0)
            {
                sprintf(r,"%s and the float is %5.5f",chars,value);
                delete chars;
                return r;
            }
        }
    }
    catch (ServiceRuntimeException& e)
    {
        printf("Error from service: %s\n", e.getMessageText());
        // .. just carry on
    }

    sprintf(r,"The float is : %5.5f", value);
    return r;
}
      

Remember we have a new service, so we have to deploy the new runtime files in the TUSCANY_SCACPP_SYSTEM_ROOT/samplecomposite directory:

  • StringThingImpl.componentType

And of course the dll and composite files changed, so we'll need to copy those too.

Run MyClient.exe again (with no recompilation required - see the power of SCA!) and the results should now look something like:

The float returned is 123.456
The string came back as "The string from stringthing and the float is : 1234.456"
      

Conclusion

And there it is: Our first ready-wired and working SCA composite. Figure 1 will help to remind you of the path used to resolve the service name. We hope this worked Sample has given you a feel for the power of SCA. As an exercise you might like to add some properties to the components, or try re-packaging StringThing into another dll.

In the next article we will cover writing components in Python or Ruby instead of C++, using inbound and outbound web services and explain the relationship to Apache Axis


Figure 1. Service layout
layout

Resources

About the authors

Ed Slattery photo

Ed Slattery joined the Java Technology Centre as a software engineer in 1999. He has worked on shiraz reusable VM technology, graphics (AWT/Swing) and then the incubator projects, where he has written the core of the C++ implementation of SDO. This core is now been contributed to the Tuscany incubator project with the Apache Software Foundation.

Pete Robbins photo

Pete Robbins is a software engineer at IBM Hursley, UK. He joined IBM in 1981 and has worked in a variety of development and technical planning roles. Most recently, he has been working on the Tuscany open source SOA project, where he developed the XML serialization of SDO in C++. He then moved on to develop the initial SCA for C++ implementation that is part of the Apache incubator project. He is also involved in the SCA specification collaboration.

Andrew Borley photo

Andrew Borley is an IT Specialist at IBM Hursley, UK. He has held various roles including developer, team leader and project manager and has worked with various technologies in his 7 years at IBM. Since 2001 he has been working with customers on Web Services and Grid projects, and his current role is developing the SCA for C++ implementation within the Apache Tuscany open source SOA project.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and Web services
ArticleID=158120
ArticleTitle=Build a simple C++ service component, Part 1: A quick tour of the C++ API for the Service Component Architecture
publish-date=09082006
author1-email=slattery@uk.ibm.com
author1-email-cc=
author2-email=robbins@uk.ibm.com
author2-email-cc=flanders@us.ibm.com
author3-email=borley@uk.ibm.com
author3-email-cc=flanders@us.ibm.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers