Articles     Forum     Examples     Download

 

Getting Started with PocoCapsule/CORBA Component Framework

Ke Jin

Oct 17, 2007

 

Copyright(c) 2007 by Pocomatic Software. All Rights Reserved.

 

A typical CORBA application development cycle consists of the following three phases:

This article demonstrates a full development cycle of a simple CORBA client-server application. The full source code of these examples are available in examples/corba/hello and  examples/corba/hello-tie directories of the PocoCapsule installation. Other additional PocoCapsule/CORBA examples including POA server, OMG-Event/Notification, OMG-DDS, OMG-RTC, and JTRS-SCA are also available from the installation.

Defining the object interface

In OMG CORBA object model, interfaces of objects are defined using the programming language neutral interface define language (IDL). For instance, the object interface of this example is defined in the Greeting.idl as follows

 

 

module sample {

     interface Greeting {

          string hello(in string msg);

     };

};

 

 

The IDL interface definition above is intuitive for average C++ and/or Java application developers. Conceptually, it could be understood as merely a language independent expression of the following C++ pure virtual class definition:

 

 

class sample {

  public:

      class Greeting : public CORBA::Object {

        public:

            virtual char*  hello(const char* msg) = 0;

      };

};

 

 

In fact, IDL to C++ mapping utilities/tools indeed generate the client-side stub class sample::Greeting more or less similar to this conceptual mapping.

Implementing the server object

The second development phase is to implement the business logic of this Greeting service object. There are two portable implementation styles standardized by OMG, namely the “servant inheritance” and the “TIE delegation”, and are referred to as “servant” and “native” respectively in PocoCapsule/CORBA.

 

In the “servant” style, the object’s server implementation (known as the servant) C++ class should support all operations defined in the IDL interface and should have the so-called POA server skeleton as its direct or indirect parent class. This POA server skeleton is generated from the user defined object IDL using the tool that comes with the underlying ORB (such as the idl2cpp of VisiBroker/C++ or tao_idl of TAO). In this example, the generated POA server skeleton C++ class is the POA_sample::Greeting. The “servant” implementation class MyGreetingServantImpl is therefore prototyped/implemented, for instance, in the GreetingImpl.h/GreetingImpl.C as follows:

 

 

class MyGreetingServantImpl : public POA_sample::Greeting

{

  public:

      char* hello(const char* s) {

            printf(“server received a %s\n“, s);

            return CORBA::string_dup(“hello greeting from server”);

      }

};

 

 

In the “native” style, the object’s server implementation C++ class should support all operations defined in the IDL interface as in the case of “servant” style, however, not necessary to have the so-called POA server skeleton in the parent class hierarchy. In this example, a “native” implementation class MyGreetingNativeImpl is simply prototyped/implemented  in the GreetingImpl.h/GreetingImpl.C as follows:

 

 

class MyGreetingNativeImpl : public POA_sample::Greeting

{

  public:

      char* hello(const char* s) {

            printf(“server received a %s\n“, s);

            return CORBA::string_dup(“hello greeting from server”);

      }

};

 

 

The compiled binary object file(s) of the implementation above, either the servant or the native class, can be directly linked into the server application main executable later on, or can be linked into a standalone shared or dynamic loadable library and then linked or loaded by the main server executable. 

 

Notably, the object interface and its implementations in either the servant or the native form are designed, implemented, and built as ordinary CORBA 2.x application components. Developers do not need to have knowledge or concern about PocoCapsule/CORBA and can assume the resulting component binary (shared/dll library or object file) is going to be used within a plain old CORBA 2.x runtime environment.  

Declarative deployment descriptor

The third development phase is deployment, namely to enlist instances of server object implementation classes to the ORB and retrieve the object references or URLs for clients. In PocoCapsule/CORBA, this is done by writing a deployment descriptor that describes the desired deployment setup.

 

A PocoCapsule/CORBA deployment descriptor describes the structure or arrangement of a given CORBA application in a declarative style, to be distinct from the traditional imperative programming style. A declarative program describes “what” are the intended structures and dependencies without specifying procedures to construct them. An imperative program instructs “how” to proceed step by step in performing given operations.

 

Imperative programming style is familiar to most OO professionals and is widely used in component implementations and traditional manual application assembly and deployment. However, it is not the most expressive way to describe high level service or system level deployment compositions. Such compositions are best to be comprehended and expressed by their deployed structures rather than by their deployment procedures.

 

For the servant style implementation, the declarative deployment descriptor (examples/corba/hello/setup.xml) looks like the following:

 

 

...

<corba-application-context>

    <bean id=”my-impl” class=”MyGreetingServantImpl”/>

 

    <orb id=”my-orb”>

        <object uri=”my-server”

                impl-ref=”my-impl"/>

    </orb>

 

    ...

</corba-application-context>

 

Similarly, for the native (namely, the TIE delegation) style implementation, the deployment descriptor (examples/corba/hello-tie/setup.xml) looks like the following:

 

 

...

<corba-application-context>

    <bean id=”my-impl” class=”MyGreetingNativeImpl”/>

 

    <orb id=”my-orb”>

        <object uri=”my-server”

                impl-ref=”my-impl"

                impl-type="native"

                interface="sample::Greeting"/>

    </orb>

   

    ...

</corba-application-context>

 

The deployment descriptor, either the first (for servant implementation) or the second (for native implementation), describes the following server components and setup:

Dynamic invocation proxies

The PocoCapsule/C++ core framework and the server component implementations above in form of plain old C++ object (POCO) classes are developed independently without knowing each other. However, when deploying these implementations, the core framework has to invoke operations defined on these POCO classes, such as its constructor. This is achieved in PocoCapsule/C++ IoC framework through dynamic invocation proxies.

 

A unique technique of PocoCapsule/C++ IoC framework is to generate dynamic invocation proxies from deployment descriptors. This scenario is opposite to  “reflection” and therefore is referred to as “projection”. Unlike CASE tools or MDA code generators, PocoCapsule/C++ only generates the invocation proxies implied by metadata of deployment descriptors rather than the procedure code with actual invocation parameters to be performed by these descriptors. Hence, the proxy code does not need to be regenerated under method argument value changes of the deployment descriptor.

 

The utility tool in PocoCapsule/C++ for this “projection” code generation is pxgenproxy. In this example, the proxy code is generated from the descriptor file setup.xml as follows:

 

 

% pxgenproxy setup.xml -h=GreetingImpl.h

 

The generated source code of proxies will be written to a file (happens to be setup_reflx.cc in this example). More detailed description on pxgenproxy utility can be found in PoocCapsule/C++ IoC & DSM Framework Developer Guide.

 

Unlike CASE, MDA, and IDL that either require application developers to manually insert application code into the generated code, or require application code to extend and implement or invoke generated interfaces/stubs, the PocoCapsule/C++ generated code are completely unknown to applications and largely transparent to application developers.

 

Application developers only need to look into the generated proxy code if an operation invocation signature mismatch error was reported during its compilation, which is most likely caused by bugs inside the user supplied deployment descriptor. However, the PocoCapsule/CORBA framework’s high level domain specific declarative schema is able to catch most of such low-level errors by the schema validation. Therefore, with PocoCapsule/CORBA, application developers rarely need to look into generated code and can simply compile and build it into a shared/dynamic library or directly link it with the application main executable. In this example, the shared library approach is used and the library is named as reflx.so (or reflx.dll on windows).

The Container Server

The Greeting object server object implementation should be deployed, with the deployment descriptor, to a runtime environment, also known as a container. In PocoCapsule and most other IoC containers, the term “container” merely refers to a application context factory object, which can be embedded in any C++ or Java applications. In this example with PocoCapsule/C++ IoC, this factory is embedded in a tiny C++ main() function (examples/corba/hello/server.C) as follows:

 

 

#include <pocoapp.h>

...

int main(int argc, char** argv)

{

    POCO_AppContext::initDefaultAppEnv(argc, argv);

   

    // create the server context representation

    POCO_AppContext* ctxt

        = POCO_AppContext::create(“setup.xml”, “file”);

     

    // establish the server structure

    ctxt->initSingletons();

   

    // start the ORB runner

    printf(“Server is ready, with URL:\n”);

    ctxt->getBean(“pocomatic.dispatcher:my-orb”);

          

    return 0;

}

 

This mini container is application independent. Therefore, it is used by many other examples in the PocoCapsule/C++ installation, and can be used by real production applications as well. The POCO_AppContext class in the code is described in the PocoCapsule/C++ IoC & DSM framework developer guide. In a brief description, the code above in this main executable does:

Playing this example

The first thing to do in playing this example is to start the server. As said above, the server will print out the URL of the server object with the URI (i.e. “my-server”) specified in deployment descriptor setup.xml:

 

 

% server

Server is ready, with URL:

corbaloc::192.168.2.2:2809/my-server

 

To see what objects and methods are inversely called back by the container behind the scene, one can restart the server with the “-Dpococapsule.trace.enable=true” command line option. This command line argument will then be passed to the PocoCapsule/C++ engine through the application context to turn on the tracing option. The trace messages are printed on the stderr, as highlighted in the following result:

 

 

% server –Dpococapsule.trace.enable=true

POCO_CORBAHelper::ORB_init(...) = (CORBA::ORB_ptr)(0x86daa50)

((CORBA::ORB_ptr)0x86daa50)->resolve_initial_references(...) = (CORBA::Object_ptr)(0x8700494)

PortableServer::POA::_narrow(...) = (PortableServer::POA_ptr)0x86ffc48

CORBA::release((CORBA::Object_ptr)(0x8700494))

((PortableServer::POA_ptr)0x86ffc48)->the_POAManager()->activate()

new MyGreetingServantImpl() = (MyGreetingServantImpl*)0x8707090

POCO_CORBAHelper::export_object(...) = (CORBA::Object_ptr)0x87073ac

Server is ready, with URL:

PCOO_CORBAHelper::run(...)

corbaloc::192.168.2.2:2809/my-server

 

The client invokes the hello() method of this server object by calling the method on its stub. This stub is a sample::Greeting C++ object, and can be obtained from the ORB based on the known server object URL as in the following code (examples/corba/hello/client.C):

 

  

CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);

CORBA::String_var url

    = (const char*)”corbaloc::192.168.2.2:2809/my-server";

   

// destringfy the url into a client stub

sample::Greeting_var stub

    = sample::Greeting::_narrow(orb->string_to_object(url.in()));

  

// make an invocation on the stub

CORBA::String_var reply = stub->hello(“hello from client”);

   

// print out what returned from the server

printf(“client receives %s\n”, reply.in());

Changing Server Structure/Configuration

With the PocoCapsule/CORBA declarative deployment, a server application can easily be restructured without rewriting/modifying a single line of code. Assuming the developer wants the server to have the following setting:

This setting can easily and intuitively be declared in the following deployment description (using the servant style implementation for instance):

 

  

...

<corba-application-context>

    <bean id=”my-impl” class=”MyGreetingServantImpl”/>

   

    <orb id=”my-orb”>

        <poa>

            <policies>

                <policy name=”thread” value=”single”/>

                <policy name=”lifespan” value=”persistent”/>

            </policies>

 

            <object uri=”my-server” impl-ref=”my-impl”/>

        </poa>

    </orb>

</corba-application-context>

 

The differences to the previous descriptor are highlighted. The Greeting object was previously declared to be activated (enlisted) on the (hidden) default object adapter (therefore, use default policies) is now declared to be activated (enlisted) on an object adapter (POA) with the user specified thread and lifespan policies.

 

This structure change implies additional dynamic invocation signatures for deployment, therefore, requires regenerate their proxies using pxgenproxy. After regenerating and rebuilding the reflection dynamic invocation proxy library reflx.so (or .dll on windows), this new server structure can be instantiated by simply restarting the server with the new deployment descriptor, without having to rebuild the server binary.

 

Back to the PocoCapsule articles page

 

This page has been visited:

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