Service Component Architecture (SCA) supports many communication protocols. In SCA terms, these are called bindings. Bindings encapsulate the complexities of communication protocols and enable components to be implemented and wired together without a direct dependency on the communication protocols used. What does this mean in reality? It means that an SCA developer can implement a component without polluting the business code with details of how the communication between components will be handled. This separation of concerns has the added advantage of making the communication protocol a pluggable entity that can be changed at any time based on how and where the component is deployed. Bindings are used by services and references. References use bindings to describe the access mechanism used to call a service (which can be a service provided by another SCA composite). Services use bindings to describe the access mechanism that clients (which can be a client from another SCA composite) have to use to call the service. SCA supports the use of multiple different types of bindings. Trentino supports SCA binding via TCP/IP. Trentino will also support interprocess communication binding via shared memory, REST binding, bacnet binding and profinet binding. SCA provides an extensibility mechanism by which an SCA runtime can add support for additional binding types. A binding is defined by a binding element which is a child element of a service or of a reference element in a composite. The following snippet shows the binding element pseudo-schema.
<?xml version="1.0" encoding="ASCII"?> <!-- Bindings schema snippet --> <composite ... > ... <service ... > <interface … /> <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"?policySets="listof xs:QName"?> <wireFormat/> <operationSelector/> <requires/> <policySetAttachment/> </binding> <callback> <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"? policySets="list of xs:QName"?> <wireFormat/> <operationSelector/> <requires/> <policySetAttachment/> </binding> </callback> </service> <reference ... > <interface … /> <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"?policySets="listof xs:QName"?> <wireFormat/> <operationSelector/> <requires/> <policySetAttachment/> </binding> <callback> <binding uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"? policySets="list of xs:QName"?> <wireFormat/> <operationSelector/> <requires/> <policySetAttachment/> </binding> </callback> </reference> </composite>
SCA binding for Trentino is done via TCP/IP. Following snippet shows the SCA binding element pseudo-schema.
<binding.sca uri="xs:anyURI"? name="xs:NCName"? requires="list of xs:QName"? policySets="list of xs:QName"?> <wireFormat/>? <operationSelector/>? <requires/>* <policySetAttachment/>* </binding.sca>
<?xml version="1.0" encoding="utf-8"?> <composite name="BasicCalculatorRemoteClientComposite" xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" targetNamespace="BasicCalculatorNamespace"> <component name="BasicCalculatorRemoteClientImpl"> <implementation.cpp class="Trentino::Example::BasicCalculatorRemoteClient::BasicCalculatorRemoteClientImpl" header="TrentinoExampleBasicCalculatorRemoteClientBasicCalculatorImpl.h" scope="composite" eagerInit="true" library="BasicCalculatorRemoteClient" > <function name="onInit" init="true" /> </implementation.cpp> <reference name="BasicCalculatorRef" > <binding.sca uri="tcp://localhost:5342/BasicCalculatorImpl/BasicCalculator"/> </reference> </component> </composite>
tcp://localhost:5342/BasicCalculatorImpl/BasicCalculator
In the next 10 minutes, we want to create a Hello World remote application and deploy it on two Trentino runtimes. The first runtime is a server and the second a client. On the Server, a contribution implementing the Helloworld Service is deployed. On the client, the contribution is having a reference using the service provided by server. The overall deployment diagram of our system is described in the following picture.
Assembly diagram of Helloworld remote
Binding.sca is the default binding for all remotable services. If there are not any bindings specified for a remotable service, we can say that it uses binding.sca. If there are not any binding.sca specified, which port will be used by Trentino runtime? The Trentino runtime hosting the server application has a Binding.conf file. The contents of the Binding.conf is below:
binding.sca.port=5342 binding.sca.dll=TrentinoBindingSCA binding.sca.thread.number=1 binding.sca.client.timeout=1500 #in miliseconds
Download and install the Trentino runtime as described in the Trentino Getting Started guide. It includes all the necessary binaries for developing a remote application. Now create a folder called HelloWorldRemote_Source. At the end of this section, we will end up with the following file structure:
Server folder structure
cmake_minimum_required(VERSION 2.6) project(HelloWorldRemote) set(PNAME HelloWorldRemote) ############################################################################## # MACROS # ############################################################################## #---------------------------------- # MY_LINK_THIRD_PARTY_LIBRARIES #---------------------------------- #The libs to be linked to are expected to be passed as additional arguments after 'target'. #This arguments have to be uncomplete variable names representing the libs #The macro will append '_DEBUG' respectively '_RELEASE' to the passed arguments #in order to produce real variable names that are expected to contain the real lib names. #EXAMPLE: #The call MY_LINK_THIRD_PARTY_LIBRARIES(main Boost_FILESYSTEM_LIBRARY) expects the variables #'Boost_FILESYSTEM_LIBRARY_DEBUG' (if build type is debug) respectively #'Boost_FILESYSTEM_LIBRARY_RELEASE' (if build type is release) to be defined #---------------------------- macro(MY_CREATE_VALIDATE_GENERATE_INSTALL_TARGET installDir) if (WIN32) set(GEN_INSTALL_COMMAND msbuild INSTALL.vcxproj) else() set(GEN_INSTALL_COMMAND make install) endif() add_custom_target([Trengen_Validate_Generate_Install] COMMAND Trengen -cont ${installDir} #create a build folder for the generated code COMMAND ${CMAKE_COMMAND} -E make_directory ${installDir}Gen_Build #build the project using cmake COMMAND ${CMAKE_COMMAND} -E chdir ${installDir}Gen_Build cmake ../${installDir}Gen #now install the application: on windows, call msbuild on linux call make COMMAND ${CMAKE_COMMAND} -E chdir ${installDir}Gen_Build ${GEN_INSTALL_COMMAND} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/../ VERBATIM ) endmacro(MY_CREATE_VALIDATE_GENERATE_INSTALL_TARGET) set (TRENTINO_RUNTIME_DIR $ENV{TRENTINO_HOME}) set (DEFAULT_INSTALL_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../) if (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) set (CMAKE_INSTALL_PREFIX ${DEFAULT_INSTALL_PATH} CACHE STRING "Install path" FORCE) endif() ############################################################################## # COMPILER OPTIONS # ############################################################################## if(MSVC) add_definitions(-DUNICODE -D_UNICODE) add_definitions(/W4) else() add_definitions(-Wall -pedantic) endif() ############################################################################## # OUTPUT CONFIGURATION # ############################################################################## set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/build/${CMAKE_SYSTEM_PROCESSOR}${CMAKE_SYSTEM_NAME}) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/build/${CMAKE_SYSTEM_PROCESSOR}${CMAKE_SYSTEM_NAME}) set(SCAAPI_INCLUDE_DIRS ${TRENTINO_RUNTIME_DIR}/include) set(BOOST_INCLUDE_DIRS ${TRENTINO_RUNTIME_DIR}/include) #source and header files FILE (GLOB ${PNAME}_HEADER_FILES *.h *.inl services/*.h services/*.inl) FILE (GLOB ${PNAME}_SCA_FILES *.xml *.componentType META-INF/*.xml META-INF/*.composite) FILE (GLOB ${PNAME}_BINDING_SCA_FILES *.tidl.properties *.java) FILE (GLOB ${PNAME}_SCA_COMPONENT_TYPES *.componentType ) FILE (GLOB ${PNAME}_SOURCE_FILES *.cpp services/*.cpp) SET(${PNAME}_SCA_FILES ${${PNAME}_SCA_FILES} ${${PNAME}_SCA_COMPONENT_TYPES}) FILE(GLOB ${PNAME}_EXPORTED_HEADERS *.h *.inl) #libs set( ${PNAME}_THIRD_PARTY_LIBS ) set( ${PNAME}_CUSTOM_LIBS ) set( ${PNAME}_SOURCE_FILES ${${PNAME}_SOURCE_FILES} ${${PNAME}_HEADER_FILES} ${${PNAME}_SCA_FILES}) if (WIN32) add_definitions(-DHELLOWORLDREMOTE_EXPORTS) endif() if(MSVC_IDE) source_group("SCA Files" FILES ${${PNAME}_SCA_FILES}) endif() include_directories(${SCAAPI_INCLUDE_DIRS} ${BOOST_INCLUDE_DIRS}) add_library(${PNAME} SHARED ${${PNAME}_SOURCE_FILES}) set(${PNAME}_INSTALL_DIR ${PNAME}) set_target_properties(${PNAME} PROPERTIES OUTPUT_NAME ${PNAME}) install(TARGETS ${PNAME} RUNTIME DESTINATION ${${PNAME}_INSTALL_DIR} LIBRARY DESTINATION ${${PNAME}_INSTALL_DIR} ARCHIVE DESTINATION ${${PNAME}_INSTALL_DIR}) install(DIRECTORY META-INF DESTINATION ${${PNAME}_INSTALL_DIR}) install(FILES ${${PNAME}_EXPORTED_HEADERS} ${${PNAME}_BINDING_SCA_FILES} ${${PNAME}_SCA_COMPONENT_TYPES} DESTINATION ${${PNAME}_INSTALL_DIR}) ############################################################################## # CUSTOM TARGET FOR VALIDATING AND GENERATING CODE BY TRENGEN# ############################################################################## MY_CREATE_VALIDATE_GENERATE_INSTALL_TARGET(${${PNAME}_INSTALL_DIR} )
A remote application using the SCA Binding should be defined using an interface description language. Trentino default binding defines its own IDL called TIDL (Trentino IDL) that is derived from the CORBA IDL specification. More details about TIDL can found in the next chapter. A TIDL interface is defined in one or more .java classes. TIDL uses part of the java syntax to describe the interfaces. The use of the java syntax makes it easy to extend and parse the interface. It also makes it possible to write TIDL files using existing java editors. Create a file named HelloWord.java in HelloWorldRemote_Source. The file should have the following content:
package Trentino.Example.HelloWorldRemote; //We should ALWAYS Import org.trentino.tidl.* in a TIDL file import org.trentino.tidl.*; /** * HelloWord Service, receive hello world messages * and send wellcome to the client. **/ public interface HelloWorld extends TInterface{ /** * The server receive an Hello message and send a * wellcome message back to the client who sent * the message * @param name: the name of the client who sent the message * @return TString a welcome message **/ TString hello(TString name); }
interface=./HelloWorld.java
Important: Trengen relies on JDK and not only the JRE (Java Runtime Environment). Make sure that a JDK 1.6.X has been installed.
From the TIDL definition of Helloword, we then generate the corresponding C++ interface. This is done using Trengen. (See Chapter 4 to learn more about Trengen tool). In the command line type:
Trengen -gencppitf Path/to/HelloWorldRemote_Source
Create a class named HelloWordImpl in a header file named TrentinoExampleHelloWorldRemoteHelloWordImpl.h and copy the following implementation into it.
#ifndef TrentinoExampleHelloWorldRemoteHelloWorldImplH #define TrentinoExampleHelloWorldRemoteHelloWorldImplH #ifdef WIN32 #ifdef HELLOWORLDREMOTE_EXPORTS #define HELLOWORLDREMOTE_IMPORT_EXPORT __declspec(dllexport) #else #define HELLOWORLDREMOTE_IMPORT_EXPORT __declspec(dllimport) #endif //HELLOWORLDREMOTE_EXPORTS #else #define HELLOWORLDREMOTE_IMPORT_EXPORT #endif //WIN32 //baseclass #include "TrentinoExampleHelloWorldRemoteHelloWorld.h" namespace Trentino{ namespace Example{ namespace HelloWorldRemote{ //forward declaration //************************************************************************************** // HelloWorld //************************************************************************************** //! \brief implementation of helloword service //************************************************************************************** class HELLOWORLDREMOTE_IMPORT_EXPORT HelloWorldImpl : public HelloWorld{ //construction public: HelloWorldImpl(); virtual ~HelloWorldImpl(); //services public: virtual std::string hello (const std::string& name); }; //class HelloWorld } //namespace Trentino } //namespace Example } //namespace HelloWorldRemote #endif //TrentinoExampleHelloWorldRemoteHelloWorldImplH
Create the corresponding C++ class TrentinoExampleHelloWorldRemoteHelloWordImpl.cpp and copy the following code into it.
#include "TrentinoExampleHelloWorldRemoteHelloWorldImpl.h" using namespace Trentino::Example::HelloWorldRemote; //class HelloWorldImpl HelloWorldImpl::HelloWorldImpl() { } HelloWorldImpl::~HelloWorldImpl() { } std::string HelloWorldImpl::hello (const std::string& name) { std::string result("Hello "); result+=name; return name; }
Now our service is implemented (a C++ implementation is available), we need to create SCA component composite and contribution in other to describe our application and make it deployable into a Trentino runtime.
HelloWorld remote componentWe start by defining our SCA component type which represents the configurable aspects of our implementation. A component type is described in a *.componentType xml document, that must be located in the same folder with the component implementation (the location of the component header file), in our case the component type name is TrentinoExampleHelloWorldRemoteHelloWordImpl.componentType. It has the following content:
<?xml version="1.0" encoding="utf-8"?> <componentType xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912"> <service name="HelloWorld" > <interface.cpp remotable="true" class="Trentino::Example::HelloWorldRemote::HelloWorld" header="TrentinoExampleHelloWorldRemoteHelloWorld.h"/> </service> </componentType>
Now we can create a composite that has one instance of our previously defined component type For that purpose Trentino requires composites to be located in a folder named META-INF. In our source folder, create META-INF folder and create an xml file named: HelloWorldRemote.composite (composite files always end with .composite), and paste following content into it:
<?xml version="1.0" encoding="utf-8"?> <composite name="HelloWorldRemoteComposite" xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" targetNamespace="HelloWorldRemoteNamespace"> <component name="TrentinoExampleHelloWorldRemoteHelloWorldImpl"> <implementation.cpp class="Trentino::Example::HelloWorldRemote::HelloWorldImpl" header="TrentinoExampleHelloWorldRemoteHelloWorldImpl.h" scope="composite" library="HelloWorldRemote" eagerInit="true"/> <service name="HelloWorld" > <interface.cpp remotable="true" class="Trentino::Example::HelloWorldRemote::HelloWorld" header="TrentinoExampleHelloWorldRemoteHelloWorld.h"/> </service> </component> </composite>
The last step in your application description is to create a contribution that is an xml document that describes all the artifacts (the so-called deployable elements) to be deployed into and managed together by the runtime. For our simple case, the only available deployable element is the previously created composite and its dependencies (all componentTypes and compiled libraries). The name of the contribution file should be sca-contribution.xml and it should be created in our META-INF folder. Paste the following code into it:
<?xml version="1.0" encoding="utf-8"?> <contribution xmlns="http://docs.oasis-open.org/ns/opencsa/sca/200912" xmlns:sample="HelloWorldRemoteNamespace"> <deployable composite="sample:HelloWorldRemoteComposite"/> </contribution>
We would like to do all these steps without an IDE but rather with a console.
Before been deployed into the runtime, a prepared contribution as we did in the last steps will go through a complex validation and code generation process. The validation process make sure that only a consistent and specification conform contribution can be deployed into the runtime. To validate and generate code, type for windows:
msbuild [Trengen_Validate_Generate_Install].vcxproj
make [Trengen_Validate_Generate_Install]
The steps for creating the client applications are similar to the ones for the server application Please download the source code for the client from our samples examples. And build as described for the server example.
The client application is a component that is initialized directly after deployment (eager initialization). It references the HelloWorld Service created before. It has an initialisation lifecycle method that is called as soon as the component is initialized. That lifecycle method just connects to the HelloWord Server and calls the remote HelloWord Service. Here is the source code of the HelloWorldClient
#include <iostream> #include "TrentinoExampleHelloWorldRemoteHelloWorldClientImpl.h" using namespace Trentino::Example::HelloWorldRemote; //class HelloWorldClientImpl HelloWorldClientImpl::HelloWorldClientImpl() { } HelloWorldClientImpl::~HelloWorldClientImpl() { } void HelloWorldClientImpl::setHelloWorld(HelloWorld* helloWorld){ mHelloWorld = helloWorld; callHello(); } //This method is called by the Runtime as soon as this component is initialized void HelloWorldClientImpl::callHello () { try { std::string name("Trentino"); std::string response = mHelloWorld->hello(name); std::cout << "Remote HelloWorld Service called. Response is:" << response << std::endl; } catch (std::exception& e) { std::cout << "Exception during call of hello world:" << e.what() << std::endl; return; } }
In this last step start two different instances of the Trentino runtime. In the first instance, deploy the server and wait until the server deployment is completed. In the second instance, deploy the client. The client will print:
Remote HelloWorld Service called. Response is: Hello Trentino
For the server side, the files generated by Trengen can be seen in the following figure:
server-side generated files
client-side generated files
The following diagram represents the sequence of binding in Trentino. It covers the "boot", "install server" and "install client" commands.
Sequence diagram for binding SCA
Inter-process communication (IPC) is a set of methods for the exchange of data among multiple threads in one or more processes. IPC methods are divided into methods for
Binding Framework is a set of interfaces, guidelines and documentation that enables a user not directly involved in Trentino development to extend Trentino with additional Bindings. Siemens develops the core and Trentino users are supposed to develop Bindings as needed. The goal for Binding Framework was to keep it as easy as possible to extend. Thus binding framework in Trentino is a generic module. Extending bindings requires: