Articles     Forum     Examples     Download     Open Source

 

Component-Based WebServices and SCA in PocoCapsule

 

Ke Jin

Oct 15, 2007

 

Copyright 2007 by Pocomatic Software. All Rights Reserved.

 

Web Services containers

Web Services applications are software systems consist of service users (clients) and service providers (servers). Clients use SOAP protocol to access services provided by remote servers. These services are business logic components implemented, for instance, in conventional programming languages (such as C++ and Java).

 

Service components are deployed to a runtime environment, referred to as WebServices container hereafter, which adopts them as service endpoints to a message dispatch engine atop a SOAP stack.  Ideally, such a container infrastructure should reduce the complexities and cost of WebServices application development and maintenance.

 

Unfortunately, available WebServices containers are mostly proprietary and primitive, especially for C++ applications. Not only they rely on vendor specific component programming, configuration, and deployment models, but also they do not have a declarative assembly model. These problems largely prohibit the reusability of third party and legacy service components, rule out the transferability of developer/user skills and experiences, and discourage the interchangeability of development tools across containers from different vendors.

The SCA (OASIS CSA)

A group of vendors known as the Open SOA Collaboration have been soliciting their Web Services component architecture, the Open Service Component Architecture (SCA), as an industrial standard. Lately, the SCA effort has been moved into OASIS and renamed as Open Composite Service Architecture (CSA).

 

As a design-by-committee architecture, the SCA (especially the SCA C++ mapping) emphasizes its sophisticated appearance rather than the straightforwardness and the effectiveness. The SCA C++ mapping committee decided not to use the non-invasive inversion of control (IoC) on plain old C++ objects (POCO) for component wiring and property setting. Instead, a contrived thread local context lookup scenario was invented, a kludge that has been abandoned by the mainstream industry for years.

 

For instance, in a plain old C++ object implementation, a dependent service object reference can be injected to the user object through a setter method (accountDataService()) as follows: 

 

 

// a setter method sets the dataService dependency

void AccountServiceImpl::accountDataService(

AccountDataService* service) {

      ...

      _dataService = service;

}

 

AccountReport AccountServiceImpl::getAccountReport(

            const std::string& customerId) {

      // calling a method on the service reference.

      ... = _dataService->getCheckingAccount(customerId);

      ...

}

 

 

By the SCA C++ mapping, however, an assembly container does not actually wire up components. Rather, it only configures the wiring on component shadow contexts. These SCA proprietary component contexts are served by the container runtime. Before calling a dependent service, the business logic implementation (such as the getAccountReport()) of a component supposes to resolve the reference of that service from its shadow context and then dangerously cast the returned void pointer to the assumed service interface type. This scenario is illustrated in the following code (see section 1.3 of the SCA C++ mapping specification as well as the BigBank example):

 

 

AccountReport AccountServiceImpl::getAccountReport(

const std::string& customerId) {

ComponentContext* context

= ComponentContext::getCurrent();

 

// Look up the wiring from the thread local context.

// The return value is an opaque pointer (void*).

void* ptr = context->getService(

<id-of-account-data-service>);

 

// Dangerously cast the opaque pointer to the

// assumed class type without any type check.

AccountDataService* _dataService

= (AccountDataService*)ptr;

 

// Finally, the _dataService dependent reference

// is ready for use. 

... = _dataService->getCheckingAccount(customerId);

...

}

 

 

This design is not only dangerously type unsafe but also forces component implementations to be tightly coupled with the underlying SCA (OASIS-CSA) container. Consequently, SCA C++ components have to be designed and implemented specifically for a SCA container, debugged and tested within the SCA container, and built against the SCA container runtime library.

 

The SCA C++ mapping also imposes many contrived restrictions in its component and deployment models, such as rules on file names of composite descriptions and rules on component implementation classes (have to have their methods declared in abstract base classes). These contrived restrictions do give the SCA a sophisticated look and can also help SCA vendors to tightly lock-in adopted customers. However, they largely sacrifice the simplicity, usability, legacy compatibility, and flexibility of the framework itself and its application implementations.

IoC for Web Services components

A non-invasive IoC framework, such as PocoCapsule, inherently supports Web Services component assembly and configuring. In contrast to those proprietary, complex, and restrictive Web Services component solutions and SCA, this IoC based assembly and configuration alternative is open, straightforward, and flexible. It has the following characteristics:

 

The IoC technique also provides a straightforward and declarative method to deploy service components to arbitrary WebServices runtime. For instance, with explicit imperative program, a Web Services service component could be deployed as a service endpoint in the following pseudo C++ code:

 

 

AccountServiceImpl my_service; // a service component

...

 

// deploy it to the WS runtime adapte env

WSAdaptor::export_service(

..., &my_service, "AccountService", ...);

 

 

From IoC perspective, such a service endpoint deployment is no more than injecting references of either service component beans or their factory/manager beans as dependencies to the Web Services runtime environment adapter bean(s). The above C++ code, therefore, can be expressed in the following declarative application description:

 

 

<bean id="my-service" class="AccountServiceImpl"/>

 

<bean class="void"

factory-method="WSAdaptor::export_service">

...

<method-arg ref="my-service"/>

<method-arg type="string" value="AccountService"/>

...

</bean>

 

 

Further, to conceal low level complexities of this service deployment programming model from domain users, a domain specific modeling (DSM) schema can be defined that uses an <endpoint> element to express the factory injection based on the factory method WSAdaptor::export_service(). With this DSM schema, the application description becomes:

 

 

<bean id="my-service" class="AccountServiceImpl"/>

 

<web-server>

<endpoint ref="my-service" name="AccountService"/>

</web-server>

 

 

The BigBank example wiring by IoC

As concluded in the previous section, an IoC framework, such as PocoCapsule, is inherently a web services component assembly, configuring, and deployment container. With minimum extension, a DSM schema is able to express web service endpoint deployment in very high level declarative application descriptions. Such a DSM model, defined in the http://www.pocomatic.com/ws-application.dtd, is supported by PocoCapsule/C++ out-of-the-box.

 

The BigBank example from SCA is used here to illustrate how to assemble, configure, and deploy a component based Web Services application in this IoC model. The full source code and document of this example can be found in the examples/web-services/bigbank-ws directory of a PocoCapsule/C++ IoC&DSM installation. This application is declared in the following description:

 

 

<ws-application>

   <bean id="AccountService" class="AccountServiceImpl">

     <ioc method="accountDataService">

        <method-arg ref="AccountDataService"/>

     </ioc>

 

     <ioc method="stockQuoteService">

        <method-arg ref="StockQuoteService"/>

     </ioc>

 

     <ioc method="currency">

        <method-arg type="string" value="EURO"/>

     </ioc>

   </bean>

 

   <bean id="AccountDataService" class="AccountDataService"/>

 

   <bean id="StockQuoteService" class="StockQuoteService">

     <ioc method="webService">

        <method-arg  ref="StockQuoteWebService"/>

     </ioc>

   </bean>

 

   <bean id="StockQuoteWebService" class="StockQuoteSoap"/>

 

   <web-server>

     <endpoint name="AccountService" ref="AccountService"/>

   </web-server>

</ws-application>

 

 

By this description, this application consists of the following beans and organization structure:

 

The application structure described above is illustrated in the following diagram:

 

 

SCA as a DSM of an IoC Framework

PocoCapsule IoC & DSM is not only an inherent and straightforward framework for building component-based Web Services applications, but also an open and effective platform for quickly authoring other user-defined or committee-designed assembly/deployment models, including the SCA assembly model.

 

A SCA assembly/deployment model authored as a domain specific model (DSM) is available out-of-the-box in the PocoCapsule installation. It demonstrates that the combination of a plain-old C++ object (POCO) based SCA component programming model and a non-invasive inversion of control (IoC) based SCA assembly model is much superior to its predecessors. Not only this POCO+IoC based SCA container (supporting SCA assembly model) is built within a few hours (after spending weeks to figure out the "simple" SCA specification itself) instead of nearly a year of heavy engineering effort, but also it largely avoids and eliminats complexities, flaws, and restrictions of the original design-by-committee SCA C++ mapping specification and its reference implementation, as summarized in the following table:

 

 

The designed-by-committee SCA C++ mapping and its reference implementation

The SCA based on plain C++ components and authored as a DSM of an IoC framework

Engineering cost

Several thousands lines of code involving low level APIs. A team of senior developers, nearly a year of heavy engineering effort.

Only a few hundreds lines of code without involving low level APIs. A few days or even hours of casual exercise.

Component-container coupling.

Components have to look up container served shadow contexts to resolve dependency references and property settings, and then set up these dependencies and properties by themselves.

Components are container agnostic and only need to consist pure business logic. Dependency and property settings are explicitly injected and set via IoC invocations.

Type safety

Dangerously unsafe (getService() returns void*). Equivalent to C-style cast or the reinterpret_cast, the potential type mismatch is very hard to be diagnosed.

Type safe, with both build time and runtime type check.  Type mismatch will be detectable at build time as well as runtime.

Support multi-hop local component calls, dependency calls in user thread, etc..

No. The ComponentContext:: getCurrent() only returns the component context on the thread-local-storage that is set by the container on dispatching the request to the initial component.

Yes, the wiring model is completely orthogonal to the underlying thread model.

Life cycle control

No

Yes

Wiring and property setting through constructors/factories

No

Yes

Names of SCDL composite description file names

Has to use the ".composite" as the extension name.

No restrictions

Component implementation programming model

All methods of a component implementation class have to be declared in an abstract base class (SCA C++ mapping spec. section 1.2.1). All public methods of this abstract base class have to be pure virtual (SCA C++ mapping spec. Appendix 3)

No restrictions. The assembly model seamlessly works with Apache Axis C++, gSOAP C/C++, HP Systinet WASP Server for C++, as well as any plain-old C++ classes that are declared and implemented in any conventional style or paradigm (such as generic-programming).

C++ preprocess macros, friend classes, templates, non-virtual methods, inline methods, static/global functions, function pointers, etc. in component interface define classes.

Largely prohibited (SCA C++ mapping spec Appendix 3). Legacy components have to be refactored with wrapper implementations.

Fully and seamlessly supported.

The SCDL component descriptions.

Heavily rely on component type files to ensure implementation classes are converted to desired interface classes

No longer needed.

Component wiring multiplicity attribute

Mandatory for the assembly to work properly in case of multiplied wiring end (a subscribe(..) setter on an emitter component).

No longer needed. Container is agnostic to multiplicity.

Proprietary SCA C++ annotation

Tying components to the container. Violated the plain old object premise. Implementations have to be specially implemented for SCA, or heavy manual refactoring.

Not needed at all.

 

The SCA BigBank example is used here to illustrate the POCO component implementation and IoC wiring/deployment in SCDL schema of SCA. The full source code and document of this example can be found in the examples/web-services/bigbank-ocsa directory of a PocoCapsule/C++ IoC&DSM installation. In SCDL schema of SCA, this application is declared in the following composite description:  

 

 

<composite name="bigbank.account">

  <service name="AccountService">

    <binding.ws port="http://www.bigbank.com/AccountService

                      #wsdl.endpoint(AccountService)"/>

    <reference>AccountServiceComponent</reference>

  </service>

 

  <component name="AccountServiceComponent">

    <implementation.cpp class="AccountServiceImpl"/>

    <reference

      name="accountDataService">AccountDataServiceComponent</reference>

 

    <reference

      name="stockQuoteService">StockQuoteServiceComponent</reference>

    <property type="xsd:string" name="currency">EURO</property>

  </component>

 

  <component name="AccountDataServiceComponent">

    <implementation.cpp class="AccountDataService"/>

  </component>

 

  <component name="StockQuoteServiceComponent">

    <implementation.cpp class="StockQuoteService"/>

    <reference name="webService">StockQuoteWebService</reference>

  </component>

 

  <reference name="StockQuoteWebService">

    <interface.wsdl interface="http://www.webserviceX.NET/

                               #wsdl.interface(StockQuoteSoap)"/>

  </reference>

</composite>

 

 

By this description, this SCA composite consists of the following components and organization structure:

 

 

The application structure described above is illustrated in the following diagram:

 

 

More out-of-the-box WebServices and SCA examples

PocoCapsule/C++ IoC&DSM framework provides 25 examples out-of-the-box.  Among them, the following five are for WebServices and SCA.

examples

descriptions

hello-ws

This is a basic client/server Web Services example deployed using PocoCapsule/WebServices deployment model. It comprises one service provided by a plain old server object extended from C++ SOAP skeleton.

bigbank-ws

This example is originated from an OASIS-OpenCSA (formerly known as OpenSOA-SCA) user example highlighted in the official OASIS-OpenCSA documents and a prove-of-concept prototype implementation. Here, it is converted to use the PocoCapsule/WebServices service deployment and component assembly model, that is not only more intuitive, simpler and flexible but also powerful than OASIS-OpenCSA's model.  

hello-sca

This is a basic client/server Web Services example of using OASIS-OpenCSA assembly model. The assembly descriptor (service.composite) declares an OASIS-OpenCSA composition that comprises of one service provided by a plain old service object extended from a C++ SOAP skeleton.

bigbank-sca

This is the well known OASIS-OpenCSA (formerly known as OpenSOA-SCA) user example highlighted in official OASIS-OpenCSA documents and the prove-of-concept prototype implementation. The assembly descriptor (bigbank.account.composite) declares an OASIS-OpenCSA composite that exports one web service, uses one external service, and contains three internal components.

calculator-sca

This is also a OASIS-OpenCSA (formerly known as OpenSOA-SCA) user example originated from an OASIS-OpenCSA prove-of-concept prototype implementation. In this examples, components are wired up by using either OASIS-OpenCSA <wire> elements (if the calculator-by-wire.composite descriptor is used) or <reference> elements (if the calculator-by-ref.composite descriptor is used).

 

Back to the PocoCapsule articles page

 

This page has been visited:

click here for a fancy web counter
free hit counter html code