CORBA Programming
From Wikibooks, the open-content textbooks collection
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:
#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.
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.
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
#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
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
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.
-- <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 — 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
-- <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
- Wikipedia:CORBA
- Ada Programming
- Ada Programming/Libraries/Distributed/PolyORB
- ACE+TAO Opensource Programming Notes
[edit] Object Management Group
[edit] Authors and Contributors
This Wikibook has been written by:

