CORBA Programming

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

Contents


[edit] Concept

CORBA stands for Common Object Request Broker Architecture. The original idea was to create a single universal standard for how objects across different platforms, programming languages, network protocols can communicate with each other in a seamless manner. For example, an application developed on say a Sun Workstation running Unix under the programming language C needs to communicate by virtue of some well defined standard interface (the accepted contract) to a Intel based PC running Windows-2000 developed under Pascal. Without such a standard both sides need to negotiate all the details including the transport protocols. The CORBA standard defines general interface standards that can be supported by different programming languages. In addition it also defines the quality and robustness of the communication, error handling, and recovery. The Standard does not cover implementation details, but only specifies the general interface language (IDL) used across all supported languages, exception handling specification, a special transport protocol called IOP that sits on top of TCP/IP, as well as the specific programming language mappings. Using the IDL, an implementation for the specific platform dependent object is generated which can be compiled using the supported language.

[edit] object definition

A CORBA object is defined using the CORBA IDL progamming language. CORBA IDL is pure definitions language, like, for example, UML. You only define the external interface and then choose an implementation language to actually implement your object.

There are many implementation languages available. The OMG alone defines language mappings for Ada, C, C++, COBOL, Java, Lisp, PL/1, Python and Smalltalk. And there might be more.

However, some implementation languages make it easier than others to implement CORBA objects. In order to implement a CORBA object, the implementation's language needs to have a set of features which include object orientation, modules (packages or namespaces), and generics (templates or dynamic typing). If a language lacks any of those vital features, they have to be emulated. The language mapping provides those emulation layers for you, but it does not mean they are easy to use.

The differences are actually so large that it might be better to learn a new programming language with an easy mapping than use a known language with a particularly difficult mapping.

Another nice feature of the CORBA concept is that you don't need to use the object with the same programming language as the one the object is implemented in. So client and server may use different programming languages.

[edit] object specification

[edit] object implementation

[edit] The echo example

The following chapters describe the implementation of a simple CORBA object using PolyORB and Ada. While the functionality is very simple the design itself is more extensive using modules, PolyORB's logging facility and meta classes giving you a better view of what is really needed in a client/server application.

While knowledge of programming in Ada in particular is not needed, you should have knowledge of object orientated programming in general.

draft 
while the source of the example is complete the actual description is still work in progress.

[edit] The Echo object

The echo object is a simple object with only one method. This method takes a single string parameter and returns that string unchanged.

[edit] CORBA IDL definition

Any CORBA object needs to be defined in CORBA's Interface Definition Language (IDL). The IDL syntax is based on C/C++ including the use of the C preprocessor. Hence we need the usual protection against a double include:

File: test-echo.idl (view, plain text, download page, browse all)
#ifndef  __TEST_ECHO_DEFINED
#define  __TEST_ECHO_DEFINED

Next we define a module name. While not strictly needed for such a small example it is a good practice never to pollute the global namespace with classes and to use modules instead.

module Test
    {

A class is called an "interface" in CORBA IDL. This should not be mixed up with interfaces in Ada or Java. A CORBA interface is the public part of the class — the part of the class you can interface with.

interface Echo
       {

As said, we define just one method. It takes a string called Message as input parameter and returns a string as result.

string
       Echo_String (
          in string Message   
          );
 
       };
    // end interface Echo
 
    };
 // end module Test
 
 #endif

[edit] Ada Specification

Before you download the specification below you should try to call the following PolyORB command. It will generate an implementation template for you.

idlac -i test-echo.idl

Most CORBA implementations will generate specification and implementation template files for the chosen implementation language. Which implementation languages you can choose from depends on your ORB. In our case it is Ada.

File: test-echo-impl.ads (view, plain text, download page, browse all)
with CORBA;
 with PortableServer;

The CORBA module (Test) and class (Echo) is mapped to an Ada package hierarchy — this is the normal way to do object oriented programming in Ada. The CORBA language mapping always tries to map close the implementation language. The Echo hierarchy contains several child packages which are fully generated and need no attention. Only the child package "Impl" contains the actual implementation of our Echo class and needs editing.

package Test.Echo.Impl
 is

The type Object contains any data our class might want to store. However, all data stored is private, in fact all data in CORBA objects are private.

type Object
    is new
       PortableServer.Servant_Base
    with private;
 
    type Object_Ptr is access Object'Class;

The Echo function is specified as a normal primitive operation to a normal Ada class. If you are interested to know how Ada classes are declared you can read Ada Programming/Object Orientation.

function Echo_String (
       --  The Object itself
       Self : access Object;
       --  The string which should be echoed.
       Message : in CORBA.String)
    return
       --  The returned string - which is the same as Message
       CORBA.String;
 
 private

Here, in the private section of the package, we could now declare data members for our class. However our simple echo class needs no data, so we just declare a "null record". If you are interested to know what it would look like with data members you can read Ada Programming/Types/record.

type Object
    is new
        PortableServer.Servant_Base
    with null record;
 
 end Test.Echo.Impl;

[edit] Ada Implementation

Now we know what the CORBA object looks like in Ada we have to actualy implement the object. The implementation is a little more extensive than needed — using PolyORB logging facility to produce diagnostic output.

File: test-echo-impl.adb (view, plain text, download page, browse all)
with PolyORB.Log;
 
 with Test.Echo.Skel;
 
 --
 --  The following packages are only initialized but not used otherwise.
 --
 pragma Warnings (Off, Test.Echo.Skel);
 
 --
 --  Test.Echo.Skel is only initialized but not used.
 --
 pragma Elaborate (Test.Echo.Skel);
 
 package body Test.Echo.Impl
 is
    --
    --  a Test Module to test the basic PolyORB functions
    --
 
 --------------------------------------------------------------------------------
 
    --
    --  Initialize logging from configuration file.
    --
    package Log is new  PolyORB.Log.Facility_Log ("test.echo");
 
 --------------------------------------------------------------------------------
 
    --
    --  Log Message when Level is at least equal to the user-requested
    --  level for Facility.
    --
    procedure Put_Line (
       Message : in Standard.String;
       Level   : in PolyORB.Log.Log_Level := PolyORB.Log.Info)
    renames
       Log.Output;
 
 --------------------------------------------------------------------------------
 
    --
    --  Echo the given string back to the client.
    --
    function Echo_String (
       --  The Object itself
       Self : access Object;
       --  The string which should be echoed.
       Message : in CORBA.String)
    return
       --  The returned string - which is the same as Message
       CORBA.String
    is
       pragma Unreferenced (Self);
    begin
       Put_Line (
          "Echoing string: « " &
          CORBA.To_Standard_String (Message) &
          " »");
       return Message;
    end Echo_String;
 
 --------------------------------------------------------------------------------
 
 end Test.Echo.Impl;

[edit] The Echo meta object

[edit] CORBA IDL definition

File: test-meta_echo.idl (view, plain text, download page, browse all)
#ifndef  __TEST_META_ECHO_DEFINED
 #define  __TEST_META_ECHO_DEFINED
 
 #include "test-echo.idl"
 
 //
 //  a Test Module to test the basic PolyORB functions.
 //
 module Test
    {
 
    //
    //  The Meta Class for the echo test.
    //
    interface Meta_Echo
       {
 
       //
       //  Create a new instance of the echo object
       //
       Test::Echo  //  newly created Echo object
       New_Echo ();
 
       //
       //  There is only ony instance of the meta class object which
       //  has its own Identification.
       //
       const string Name_Service_Id = "Test/Meta_Echo";
 
       };
    // end interface Meta_Echo
 
    };
 // module Test
 
 #endif

[edit] Ada Specification

File: test-meta_echo-impl.ads (view, plain text, download page, browse all)
with Test.Echo;
 with PortableServer;
 
 package Test.Meta_Echo.Impl
 is
    --
    --  a Test Module to test the basic PolyORB functions
    --
 
 --------------------------------------------------------------------------------
 
    --
    --  The Meta Class for the echo test.
    --
    type Object
    is new
       PortableServer.Servant_Base
    with private;
 
    type Object_Ptr is access Object'Class;
 
 --------------------------------------------------------------------------------
 
    --
    --  Create a new instance of the echo object
    --
    function New_Echo (
       --  The Object itself
       Self : access Object)
    return
       --  newly created Echo object
       Test.Echo.Ref;
 
 private  --  -------------------------------------------------------------------
 
    --
    --  The Meta Class for the echo test.
    --
    type Object
    is new
       PortableServer.Servant_Base
    with null record;
 
 --------------------------------------------------------------------------------
 
 end Test.Meta_Echo.Impl;

[edit] Ada Implementation

File: test-meta_echo-impl.adb (view, plain text, download page, browse all)
with CORBA.ORB;
 with CORBA.Impl;
 
 with PortableServer.POA;
 with PortableServer.POA.Helper;
 
 with PolyORB.Log; 
 
 with Test.Echo.Impl;
 with Test.Echo.Helper;
 with Test.Meta_Echo.Skel;
 
 --
 --  The following packages are only initialized but not used otherwise.
 --
 pragma Warnings (Off, Test.Meta_Echo.Skel);
 
 --
 --  initialize packages
 --
 pragma Elaborate (Test.Meta_Echo.Skel);
 pragma Elaborate_All (Test.Echo.Impl);
 
 package body Test.Meta_Echo.Impl
 is
    --
    --  a Test Module to test the basic PolyORB functions
    --
 
 --------------------------------------------------------------------------------
 
    --
    --  Initialize logging from confiuration file.
    --
    package Log is new  PolyORB.Log.Facility_Log ("test.meta_echo");
 
 --------------------------------------------------------------------------------
 
    --
    --  Log Message when Level is at least equal to the user-requested
    --  level for Facility.
    --
    procedure Put_Line (
       Message : in Standard.String;
       Level   : in PolyORB.Log.Log_Level := PolyORB.Log.Info)
    renames
       Log.Output;
 
 --------------------------------------------------------------------------------
 
    --
    --  Create a new instance of the echo object
    --
    function New_Echo (
       --  The Object itself
       Self : access Object)
    return
       --  newly created Echo object
       Test.Echo.Ref
    is
       package ORB renames CORBA.ORB;
       package POA renames PortableServer.POA;
 
       --
       --  Get Reference to the portable server
       --
       Root_POA     :  POA.Ref
                    := POA.Helper.To_Ref (
                          ORB.Resolve_Initial_References (
                             ORB.To_CORBA_String ("RootPOA")));
       --
       --  create a new Echo object
       --
       Echo_Object  :  constant CORBA.Impl.Object_Ptr
                    := new Echo.Impl.Object;
       --
       --  create a servant for the new Echo object
       --
       Echo_Servant :  constant PortableServer.Servant
                    := PortableServer.Servant (Echo_Object);
       --
       --  activate the servant. Don't know what to do with the Id.
       --
       Echo_Id      :  constant PortableServer.ObjectId
                    := POA.Activate_Object (
                          Self      => Root_POA,
                          P_Servant => Echo_Servant);
       --
       --  Convert from servant type to the correct result type.
       --
       Result       :  Echo.Ref
                    := Echo.Helper.To_Ref (
                          POA.Servant_To_Reference (
                             Self      => Root_POA,
                             P_Servant => Echo_Servant));
 
       pragma Unreferenced (Self);
       pragma Unreferenced (Echo_Id);
    begin
       Put_Line (
          "Echo = '" &
          CORBA.To_Standard_String (CORBA.Object.Object_To_String (Result)) &
          "'");
       return Result;
    end New_Echo;
 
 --------------------------------------------------------------------------------
 
 end Test.Meta_Echo.Impl;

[edit] The server

The server procedure needs to initialize and start the server communication. It also needs to instantiate the meta objects and register them with the name server.

File: server.adb (view, plain text, download page, browse all)
--  <A HREF="http://www.adaic.org/standards/95lrm/html/RM-11-4-1.html">11.4.1 The Package Exceptions</A>
 with Ada.Exceptions;
 
 with CORBA.Impl;
 with CORBA.Object;
 with CORBA.ORB;
 
 with PortableServer.POA.Helper;
 with PortableServer.POAManager;
 
 with PolyORB.Log;
 with PolyORB.Setup.Thread_Per_Request_Server;
 with PolyORB.CORBA_P.CORBALOC;
 with PolyORB.CORBA_P.Naming_Tools;
 
 with Test.Meta_Echo.Impl;

packages are only initialized but are not used otherwise. This would normally trigger a warning, which we switch off here.

pragma Warnings (Off, PolyORB.Setup.Thread_Per_Request_Server);

initialize packages.

pragma Elaborate_All (PolyORB.Setup.Thread_Per_Request_Server);
 pragma Elaborate_All (Test.Meta_Echo);
 
 procedure Server
 is
 
   package ORB   renames CORBA.ORB;
   package MEcho renames Test.Meta_Echo;
 
Initialize logging from configuration file.
 
   package Log is new  PolyORB.Log.Facility_Log ("server");
 
Log Message when Level is at least equal to the user-requested level for Facility &mdash; which is ''notice'' for the server.
 
   procedure Put_Line (
      Message : in Standard.String;
      Level   : in PolyORB.Log.Log_Level := PolyORB.Log.Notice)
   renames
      Log.Output;

Forward declarations. You don't normally need them but GNAT is rather strict when full warnings are activated.

function Init_Echo (
      Root_POA : in PortableServer.POA.Ref)
   return
      CORBA.Object.Ref;
 
   function Init_Root
   return
      PortableServer.POA.Ref;

Set up echo meta class.

function Init_Echo (
      Root_POA : in PortableServer.POA.Ref)
   return
      CORBA.Object.Ref
   is
      package CORBALOC renames PolyORB.CORBA_P.CORBALOC;
      package Naming   renames PolyORB.CORBA_P.Naming_Tools;
      package PS       renames PortableServer;
      package POA      renames PortableServer.POA;

create the one and only meta echo instance

Meta_Object  :  constant CORBA.Impl.Object_Ptr
                   := new MEcho.Impl.Object;

create a servant for the meta echo instance

Meta_Servant :  constant PS.Servant
                   := PS.Servant (Meta_Object);

activate the servant. Don't know what to do with the Id.

Meta_Id      :  constant PortableServer.ObjectId
                   := POA.Activate_Object (
                         Self      => Root_POA,
                         P_Servant => Meta_Servant);

Convert the servant type to the correct return type.

Result       :  CORBA.Object.Ref
                   := POA.Servant_To_Reference (
                         Self      => Root_POA,
                         P_Servant => Meta_Servant);
      pragma Unreferenced (Meta_Id);
   begin

The IOR and corbaloc outputs are only for diagnostics. We use a name server to propagate the meta object.

Put_Line (
         "Meta_Echo = '" &
         CORBA.To_Standard_String (CORBA.Object.Object_To_String (Result)) &
         "'");
      Put_Line (
         "Meta_Echo = '" &
         CORBA.To_Standard_String (CORBALOC.Object_To_Corbaloc (Result)) &
         "'");

Register the meta object with the name server so the client can find the object.

Naming.Register (
         Name   => CORBA.To_Standard_String (MEcho.Name_Service_Id),
         Ref    => Result,
         Rebind => True,
         Sep    => '/');
      return Result;
   end Init_Echo;

Set up root POA

function Init_Root
   return
      PortableServer.POA.Ref
   is
      package CORBALOC renames PolyORB.CORBA_P.CORBALOC;
      package POA      renames PortableServer.POA;

Retrieve Root POA. Resolve_Initial_References will automatically create an object if it does not exist already.

Result :  POA.Ref
             := POA.Helper.To_Ref (
                   ORB.Resolve_Initial_References (
                   ORB.To_CORBA_String ("RootPOA")));
   begin

Activate the root POA.

PortableServer.POAManager.Activate (
         PortableServer.POA.Get_The_POAManager (Result));
 
      return Result;
   end Init_Root;
 
 begin
  Try :
   declare
      ORB_Id        : ORB.ORBid    := ORB.To_CORBA_String ("ORB");
      Argument_List : ORB.Arg_List := ORB.Command_Line_Arguments;
   begin
      ORB.Init (
         ORB_Indentifier => ORB_Id,
         Argv            => Argument_List);
 
      Run_ORB :
      declare
         Root_POA  : PortableServer.POA.Ref := Init_Root;
         Meta_Echo : CORBA.Object.Ref       := Init_Echo (Root_POA);
         Services  : ORB.ObjectIdList       := ORB.List_Initial_Services;
 
         pragma Unreferenced (Meta_Echo);
      begin
         List_Services :
         for Index in Positive'First .. ORB.Length (Services) loop
            Put_Line (
               "Service " &
               Positive'Image (Index) &
               " = '" &
               CORBA.To_Standard_String (
                  CORBA.String (ORB.Get_Element (Services, Index))) &
               "'");
         end loop List_Services;

Launch the server

CORBA.ORB.Run;
      end Run_ORB;
   end Try;
 exception
   when An_Exception : CORBA.Transient =>
      declare
         Member : CORBA.System_Exception_Members;
      begin
         CORBA.Get_Members (
            From => An_Exception,
            To   => Member);
         Put_Line (
            Ada.Exceptions.Exception_Information (An_Exception),
            PolyORB.Log.Error);
         Put_Line (
            "received exception transient, minor" &
            CORBA.Unsigned_Long'Image (Member.Minor) &
            ", completion status: " &
            CORBA.Completion_Status'Image (Member.Completed),
            PolyORB.Log.Error);
      end;
   when An_Exception : others =>
      Put_Line (
            Ada.Exceptions.Exception_Information (An_Exception),
            PolyORB.Log.Error);
 end Server;

[edit] The client

File: client.adb (view, plain text, download page, browse all)
--  <A HREF="http://www.adaic.org/standards/95lrm/html/RM-C-7-1.html">Ada Task Identification</A>
 with Ada.Task_Identification;
 --  <A HREF="http://www.adaic.org/standards/95lrm/html/RM-11-4-1.html">11.4.1 The Package Exceptions</A>
 with Ada.Exceptions;
 
 with CORBA.ORB;
 with CORBA.Object;
 
 with Test.Echo;
 with Test.Meta_Echo;
 with Test.Meta_Echo.Helper;
 
 with PolyORB.Log;
 with PolyORB.Setup.Client;
 with PolyORB.CORBA_P.Naming_Tools;

The following packages are only initialized but not used otherwise.

pragma Warnings (Off, PolyORB.Setup.Client);

initialize packages

pragma Elaborate_All (PolyORB.Setup.Client);
 
 procedure Client
 is
   package ORB   renames CORBA.ORB;
   package MEcho renames Test.Meta_Echo;
   package Echo  renames Test.Echo;

Initialize logging from configuration file.

package Log   is new  PolyORB.Log.Facility_Log ("client");

Log Message when Level is at least equal to the user-requested level for Facility.

procedure Put_Line (
      Message : in Standard.String;
      Level   : in PolyORB.Log.Log_Level := PolyORB.Log.Notice)
   renames
      Log.Output;

Forward declarations.

function Get_Meta_Echo
   return
      MEcho.Ref;
 
   procedure Run_Test (
      Meta_Echo : MEcho.Ref);

Retrieve meta object reference from name server.

function Get_Meta_Echo
   return
      MEcho.Ref
   is
      package Naming   renames PolyORB.CORBA_P.Naming_Tools;
 
      Obj_Ref : CORBA.Object.Ref := Naming.Locate (
         IOR_Or_Name => CORBA.To_Standard_String (MEcho.Name_Service_Id),
         Sep         => '/');
 
      Retval  : MEcho.Ref  := MEcho.Helper.To_Ref (Obj_Ref);
   begin
      return Retval;
   end Get_Meta_Echo;

Run a little test.

procedure Run_Test (
      Meta_Echo : MEcho.Ref)
   is
      package TaskID renames Ada.Task_Identification;
 
      Sent_Msg    : CORBA.String;
      Rcvd_Msg    : CORBA.String;
      Echo_Object : Echo.Ref;
   begin

Create echo object

Put_Line ("create echo object");
 
      Echo_Object := Test.Meta_Echo.New_Echo (Meta_Echo);
 
      if Test.Echo.Is_Nil (Echo_Object) then
         Put_Line ("cannot invoke on a nil reference", PolyORB.Log.Error);
      else

Sending message

Put_Line ("send message");
 
         Sent_Msg := CORBA.To_CORBA_String (
                        "Hello Task '" &
                        TaskID.Image (TaskID.Current_Task) &
                        "'!");
         Rcvd_Msg := Test.Echo.Echo_String (Echo_Object, Sent_Msg);

Printing result

Put_Line ("I said : " & CORBA.To_Standard_String (Sent_Msg));
         Put_Line ("The object answered : " & CORBA.To_Standard_String (Rcvd_Msg));
      end if;
   end Run_Test;
 
 begin
   Try :
   declare
      ORB_Id        : ORB.ORBid    := ORB.To_CORBA_String ("ORB");
      ORB_Argumente : ORB.Arg_List := ORB.Command_Line_Arguments;
   begin
      ORB.Init (
         ORB_Indentifier => ORB_Id,
         Argv            => ORB_Argumente);
 
      Run_Client :
      declare
         Meta_Echo : MEcho.Ref := Get_Meta_Echo;
      begin
         if Test.Meta_Echo.Is_Nil (Meta_Echo) then
            Put_Line ("cannot invoke on a nil meta reference", PolyORB.Log.Error);
         else
            Run_Test (Meta_Echo);
            Run_Test (Meta_Echo);
            Run_Test (Meta_Echo);
         end if;
      end Run_Client;
   end Try;
 exception
   when An_Exception : CORBA.Transient =>
      declare
         Member : CORBA.System_Exception_Members;
      begin
         CORBA.Get_Members (
            From => An_Exception,
            To   => Member);
         Put_Line (
            Ada.Exceptions.Exception_Information (An_Exception),
            PolyORB.Log.Error);
         Put_Line (
            "received exception transient, minor" &
            CORBA.Unsigned_Long'Image (Member.Minor) &
            ", completion status: " &
            CORBA.Completion_Status'Image (Member.Completed),
            PolyORB.Log.Error);
      end;
   when An_Exception : others =>
      Put_Line (
            Ada.Exceptions.Exception_Information (An_Exception),
            PolyORB.Log.Error);
 end Client;

[edit] See Also

[edit] Wiki

[edit] Object Management Group

[edit] Authors and Contributors

This Wikibook has been written by:

Personal tools