Tuscany SCA for C++ - Creating C++ SCA Components
This document describes how to create and run SCA components in Tuscany SCA C++
milestone release 2.
See the SCA
C++ Client and Implementation specification for more details about the SCA C++
programming model.
See Calculator or
BigBank for samples that
demonstrate the use of C++ components
Creating and deploying an SCA C++ Component
Each SCA C++ component needs:
- A service header file that defines the operations that can be invoked on the
component
- An implementation header file that defines the implementation and extends
the service header file
- A C++ implementation of the service that implements the operations defined
in the service header file
- Proxy and wrapper header and implementation files generated by the Tuscany
C++ SCAGEN tool
- A service definition in a .componentType file
- An SCDL component definition within an SCDL composite file. Usually this
composite file will contain multiple components configured and assembled together.
Once these items are in place for each component in your composite, you will need to
deploy this composite to your SCA system. In this release we are
using the SCA recursive composition model to do this. You simply create another
SCDL component definition in a separate composite file that will represent the composite
you created above in the SCA system. Follow the steps below to see each of these items
being created and used.
In this section we will use the Calculator sample as a worked example.
The Calculator code and files can be found at samples/Calculator and has been
developed further than the details specified below. In the interests of
readability, the example used below takes the simplest path.
- Create the service header file that defines the operations your component
will implement. E.g. Calculator.h contains the following:
#ifndef CALCULATOR_H
#define CALCULATOR_H
class Calculator
{
public:
virtual float add(float arg1, float arg2) = 0;
virtual float sub(float arg1, float arg2) = 0;
virtual float mul(float arg1, float arg2) = 0;
virtual float div(float arg1, float arg2) = 0;
};
#endif
- Create the implementation header file that extends the service header file.
E.g. CalculatorImpl.h contains the following:
#ifndef CALCULATORIMPL_H
#define CALCULATORIMPL_H
#include "Calculator.h"
class CalculatorImpl : public Calculator
{
public:
CalculatorImpl();
virtual ~CalculatorImpl();
// Calculator interface
virtual float add(float arg1, float arg2);
virtual float sub(float arg1, float arg2);
virtual float mul(float arg1, float arg2);
virtual float div(float arg1, float arg2);
};
#endif
- Create the implementation for the component based on the implementation
header file. E.g. CalculatorImpl.cpp contains the following code:
#include "CalculatorImpl.h"
#include
CalculatorImpl::CalculatorImpl()
{
}
CalculatorImpl::~CalculatorImpl()
{
}
// Calculator interface
float CalculatorImpl::add(float arg1, float arg2)
{
float result = arg1 + arg2;
printf("CalculatorImpl::add %f + %f = %f\n", arg1, arg2, result);
return result;
}
float CalculatorImpl::sub(float arg1, float arg2)
{
float result = arg1 - arg2;
printf("CalculatorImpl::sub %f - %f = %f\n", arg1, arg2, result);
return result;
}
float CalculatorImpl::div(float arg1, float arg2)
{
float result = arg1 / arg2;
printf("CalculatorImpl::div %f / %f = %f\n", arg1, arg2, result);
return result;
}
float CalculatorImpl::mul(float arg1, float arg2)
{
float result = arg1 * arg2;
printf("CalculatorImpl::mul %f * %f = %f\n", arg1, arg2, result);
return result;
}
- Create the componentType file for your component to define the service that
your component provides. The file must be named after your implementation
class and specifies the name of the service and the service header file
(which describes the service operations). E.g. CalculatorImpl.componentType
contains the following XML:
<componentType xmlns="http://www.osoa.org/xmlns/sca/1.0">
<service name="CalculatorService">
<interface.cpp header="Calculator.h"/>
</service>
</componentType>
- Create a sample.calculator.composite file for your composite and define your
component within it. The component definition specifies the implementation
library to use (a .dll file on Windows and a .so file on Linux) and the
implementation header file (which describes the implementation class). Component
properties and references to other services can also be specified here. E.g. the
Calculator sample.calculator.composite file contains the following XML:
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
name="sample.calculator">
<component name="CalculatorComponent">
<implementation.cpp library="Calculator" header="CalculatorImpl.h"/>
</component>
</composite>
- Generate the proxy and wrapper classes and header files using the SCAGEN
tool. These classes are used by the Tuscany SCA C++ runtime to enable
service implementations to be invoked from a client or another component.
Run the SCAGEN tool, specifying the directory where your header files,
sca.composite and componentType file are and the directory where you
want the generated files to be placed. E.g. on Windows, the
following command is run from the directory where Tuscany SCA is deployed:
./bin/scagen.cmd -dir ./samples/Calculator/sample.calculator -output ./samples/Calculator/sample.calculator
which produces the following files:
- CalculatorImpl_CalculatorService_Proxy.h
- CalculatorImpl_CalculatorService_Proxy.cpp
- CalculatorImpl_CalculatorService_Wrapper.h
- CalculatorImpl_CalculatorService_Wrapper.cpp
- Compile and link the code that has been written and generated. This will
produce a .dll or .so library file. The name should match the library name
specified in the sample.calculator.composite file.
- Create the sample.calculator.solution.composite file and define the Calculator composite
as a component within it. This is used to include the Calculator composite in the SCA system
and should specify the composite name used in the sample.calculator.composite file.
E.g. the Calculator sample.calculator.solution.composite
file contains the following XML:
<composite xmlns="http://www.osoa.org/xmlns/sca/1.0"
name="sample.calculator.solution">
<component name="sample.calculator.CalculatorComponent">
<implementation.composite name="sample.calculator" />
</component>
</composite>
- Deploy the various files into the SCA directory structure, as follows:
- <deploy_root>/CompositeName/CompositeName.composite
- <deploy_root>/CompositeName/Implementation.componentType
- <deploy_root>/CompositeName/Implementation.dll (or .so on Linux)
- <deploy_root>/SolutionName.composite
E.g. for the Calculator sample the structure is:
- samples/Calculator/deploy/sample.calculator/Calculator.h
- samples/Calculator/deploy/sample.calculator/CalculatorImpl.h
- samples/Calculator/deploy/sample.calculator/sample.calculator.composite
- samples/Calculator/deploy/sample.calculator/CalculatorImpl.componentType
- samples/Calculator/deploy/sample.calculator/Calculator.dll
- samples/Calculator/deploy/sample.calculator.solution.composite
- Your component, composite and subsystem are now ready to be invoked. Create a
client that will call the service. E.g. the Calculator client (in the
CalculatorClient.cpp file) contains code similar to the following:
try
{
// Locate the service
CompositeContext myContext = CompositeContext::getCurrent();
Calculator *calcService = (Calculator*) myContext.locateService("CalculatorComponent/CalculatorService");
if (calcService == 0)
{
cout << "calculator_client: Unable to find Calculator service" << endl;
}
else
{
float result = calcService->add(arg1, arg2);
cout << "calculator_client add(" << arg1 << "," << arg2 << ") = " << result << endl;
}
}
catch (ServiceRuntimeException& ex)
{
cout << "calculator_client: Error whilst invoking Tuscany: " <<
ex.getMessageText() << endl;
}
- Compile, link and run the client that has been created. You should
(hopefully!) see your component invoked. Remember you will need to have the
TUSCANY_SCACPP, TUSCANY_SDOCPP and AXIS2C_HOME environment variables set,
as well as the SCA and SDO bin directories and the Axis2C lib directory on
your PATH on Windows or the SCA, SDO and Axis2C lib directories on
your LD_LIBRARY_PATH on Linux. You will also need to set the TUSCANY_SCACPP_SYSTEM_ROOT
and TUSCANY_SCACPP_DEFAULT_COMPONENT environment variables to the
path to your SCA component directory structure and the default component respectively.
TUSCANY_SCACPP_SYSTEM_ROOT is the directory where the SCA runtime will look for any
.composite files and TUSCANY_SCACPP_DEFAULT_COMPONENT is the name of the base component
to be used by SCA clients or containers when finding services - this component must be
an instance of a composite (i.e. contain an <implementation.composite> element).
E.g. on Windows run the following commands:
- set TUSCANY_SCACPP=C:/tuscany_sca
- set TUSCANY_SDOCPP=C:/tuscany_sdo
- set AXIS2C_HOME=C:/axis2c-bin-0.94-win32
- set PATH=%PATH%;C:/tuscany_sca/bin;C:/tuscany_sdo/bin;C:/axis2c-bin-0.94-win32/lib
- set TUSCANY_SCACPP_SYSTEM_ROOT=C:/tuscany_sca/samples/Calculator/deploy
- set TUSCANY_SCACPP_DEFAULT_COMPONENT=sample.calculator.CalculatorComponent
- ./calculator_client.exe
- Optionally, enable Tuscany logging by setting the TUSCANY_SCACPP_LOGGING
environment variable with the level you wish to log at (0 for minimal
logging, up to 9 for more detailed logging) and the TUSCANY_SCACPP_LOG
environment variable to define the file to log to (if this is not set,
logging will go to the console). E.g. on Windows run the following
commands:
- set TUSCANY_SCACPP_LOGGING=5
- set TUSCANY_SCACPP_LOG=C:/tuscany/mylogfile.txt
The Calculator sample has been developed further than the details specified
above. In particular, it demonstrates how two services can be wired together
such that one references and invokes the other. It also demonstrates how to
expose the Calculator component service as an Axis2C Web Service.