| Creating SCA Components in Tuscany SCA C++ |
| ========================================== |
| |
| This document describes how to create and run SCA components in Tuscany SCA C++ |
| milestone release 1. |
| |
| |
| Creating and deploying an SCA C++ Component |
| =========================================== |
| |
| Each SCA C++ component needs: |
| o A service header file that defines the operations that can be invoked on the |
| component |
| o An implementation header file that defines the implementation and extends |
| the service header file |
| o A C++ implementation of the service that implements the operations defined |
| in the service header file |
| o Proxy and wrapper header and implementation files generated by the Tuscany |
| C++ SCAGEN tool |
| o A component definition in the module sca.module file |
| o A service definition in a .componentType file |
| o A module component definition in a sca.subsystem file |
| |
| |
| 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. |
| |
| |
| 1. 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 |
| |
| |
| 2. 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 |
| |
| |
| 3. Create the implementation for the component based on the implementation |
| header file. E.g. CalculatorImpl.cpp contains the following code: |
| |
| #include "CalculatorImpl.h" |
| #include <stdio.h> |
| |
| 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; |
| } |
| |
| |
| 4. 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: |
| |
| <?xml version="1.0" encoding="ASCII"?> |
| <componentType xmlns="http://www.osoa.org/xmlns/sca/0.9" |
| xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
| |
| <service name="CalculatorService"> |
| <interface.cpp header="Calculator.h"/> |
| </service> |
| </componentType> |
| |
| |
| 5. Create the sca.module file for your module 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 sca.module file contains the following XML: |
| |
| <?xml version="1.0" encoding="ASCII"?> |
| <module xmlns="http://www.osoa.org/xmlns/sca/0.9" |
| xmlns:v="http://www.osoa.org/xmlns/sca/values/0.9" |
| name="CalculatorModule"> |
| |
| <!-- The Calculator component --> |
| <component name="CalculatorServiceComponent"> |
| <implementation.cpp dll="Calculator.dll" header="CalculatorImpl.h"/> |
| <properties></properties> |
| <references></references> |
| </component> |
| </module> |
| |
| |
| 6. 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.module 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/CalculatorModule -output samples/Calculator/CalculatorModule |
| |
| which produces the following files: |
| |
| CalculatorImpl_CalculatorService_Proxy.h |
| CalculatorImpl_CalculatorService_Proxy.cpp |
| CalculatorImpl_CalculatorService_Wrapper.h |
| CalculatorImpl_CalculatorService_Wrapper.cpp |
| |
| |
| 7. 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 sca.module file. |
| |
| |
| 8. Create the sca.subsystem file and define your module component within it. |
| The module component definition should specify the service name used in the |
| componentType file and the module name used in the sca.module file. E.g. |
| the Calculator sca.subsystem file contains the following XML: |
| |
| <?xml version="1.0" encoding="ASCII"?> |
| <subsystem xmlns="http://www.osoa.org/xmlns/sca/0.9" |
| name="CalculatorSubsystem"> |
| |
| <moduleComponent name="CalculatorService" module="CalculatorModule"/> |
| </subsystem> |
| |
| |
| 9. Deploy the various files into the SCA directory structure, as follows: |
| |
| <deploy_root>/modules/ModuleName/ServiceHeader.h |
| <deploy_root>/modules/ModuleName/ImplementationHeader.h |
| <deploy_root>/modules/ModuleName/sca.module |
| <deploy_root>/modules/ModuleName/Implementation.componentType |
| <deploy_root>/modules/ModuleName/Implementation.dll (or .so on Linux) |
| <deploy_root>/subsystems/SubsystemName/sca.subsystem |
| |
| E.g. for the Calculator sample the structure is: |
| |
| samples/Calculator/deploy/modules/CalculatorModule/Calculator.h |
| samples/Calculator/deploy/modules/CalculatorModule/CalculatorImpl.h |
| samples/Calculator/deploy/modules/CalculatorModule/sca.module |
| samples/Calculator/deploy/modules/CalculatorModule/CalculatorImpl.componentType |
| samples/Calculator/deploy/modules/CalculatorModule/Calculator.dll |
| samples/Calculator/deploy/subsystems/CalculatorSubsystem/sca.subsystem |
| |
| |
| 10. Your component, module and subsystem are now ready to be invoked. Create a |
| client that will call the service. E.g. the Calculator client (in the |
| Calc.cpp file) contains code similar to the following: |
| |
| try |
| { |
| // Define and start the Tuscany runtime: |
| // Set the system root path that contains the SCA directory structure, set |
| // the default module component that will be used based on the format |
| // <SubsystemName>/<ModuleComponentName> and then start the runtime |
| TuscanyRuntime rt; |
| rt.setSystemRoot("C:/tuscany_sca/samples/Calculator/deploy"); |
| rt.setDefaultModuleComponent("CalculatorSubsystem/CalculatorService"); |
| rt.start(); |
| |
| |
| // Locate the service |
| ModuleContext myContext = ModuleContext::getCurrent(); |
| Calculator *calcService = (Calculator*) myContext.locateService("CalculatorServiceComponent"); |
| if (calcService == 0) |
| { |
| cout << "MyCalculatorClient.exe: Unable to find Calculator service" << endl; |
| } |
| else |
| { |
| result = calcService->add(arg1, arg2); |
| cout << "Calculator: add(" << arg1 << "," << arg2 << ") = " << result << endl; |
| } |
| } |
| catch (ServiceRuntimeException& ex) |
| { |
| cout << "MyCalculatorClient.exe: Error whilst starting or invoking Tuscany: " << |
| ex.getMessageText() << endl; |
| } |
| |
| |
| 11. 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 (or LD_LIBRARY_PATH on Linux). 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.92-win32 |
| set PATH=%PATH%;C:/tuscany_sca/bin;C:/tuscany_sdo/bin;C:/axis2c-bin-0.92-win32/lib |
| ./MyCalculatorClient.exe |
| |
| |
| 12. 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 |
| |
| |
| Further Steps |
| ------------- |
| |
| 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. See the |
| WSEntrypoint.txt document for more details on exposing components as Web |
| Services. |
| |
| |
| |