Par Fred le dimanche 24 mars 2013, 22:32 - Développement - Lien permanent

I decided today to write a small cute tutorial to create the best of the best for scalability , for highly distributed and high reliability while being multi-platform, multi-language and potentially multi-paradigm... Well, the two first ones, ok but for the last, I don't really know and finally, I don't give a damn :D At this point, with the number of buzzwords I used, your trollmeter should explode !! :D But, from now, I won't tell you anything else, you'll have to read what follows !!

What would be better to do the whole thing than web services ?

Well, it's simple, something else than web service

This other thing is an old stuff nobody wants to use anymore because it's too complicated, too heavy to set up and to code, here is CORBA !!



Well, after thinking about it, it's not a real CORBA tutorial because there are a lot on Internet but it's more a documented example for a small app done with PolyORB for server-side and OmniORB for the C++ client-side.





The dispenser

It's as good as a movie title but what are we distributing ?

As I didn't want to build a whole app to distribute, I decided to use an existing application helping everyone on an almost daily basis. Here is the Corporate Bullshit Generator from my Ada friend Gautier.

For those who don't know already, this application provides excellent sentences to use in every good meeting with commercials, strategy managers and so on.

Let's go on the project page in order to download the source code[1].





Small analysis of bullshits

To do some CORBA, we have to know what to distribute. The central package in CBSG is Corporate_Bullshit. It's a generic package to define the end of line characters and the sentence to use to simulate a dialog[2].

So, what do we see ?



function Sentence return String; function Workshop return String; function Short_Workshop return String; function Financial_Report return String;

Four functions allowing to create a string containing the set of bullshits . Let's be crazy, we will provide an interface for all of them.

In CORBA, it's quite simple, we will get the following IDL file :

module CorbaCBSG { interface CBSG { string createSentence ( ) ; string createWorkshop ( ) ; string createShortWorkshop ( ) ; string createFinancialReport ( ) ; } ; } ;

Here, I preferred to use writing conventions in use for C++, Java and others rather than the ones used in Ada in order not to scare the coder that would prefer to write the client with one of these languages

Well, finally, it's piece of cake ! :D



But this is only a first example...





Generation

Let's go for the generation and implementation for server-side part.

For this reason, I recommand to create a directory corba containing a directory idl and directories Ada and C++. The whole thing should be placed inside a directory cbsg. The file cbsg.idl defined will fit inside the idl directory.

Let's make serious things :

fred@coruscant :~/cbsg/corba/Ada $ iac -i ../idl/cbsg.idl

Eleven files are created. Only corbacbsg-cbsg-impl.ads and corbacbsg-cbsg-impl.adb will be interesting, the others are only CORBA plumbing. One important point though, the i option will overwrite your implementation if it exists already and also the spec if it contains specific fields.

In the spec file (ads file), there's nothing to change. In fact, our CORBA object doesn't hold a specific state as the CBSG does.

But, it's interesting to read this file as the reader will notice that generated methods does not use the standard String type we can find in Ada but a CORBA specific type. Our CORBA object will do the translation.

As you're really smart, I'll only give the implementation for createSentence function and how to declare the generic package Corporate_Bullshit, you'll manage to generalize on your own :

package Simple_Generator is new Corporate_Bullshit ( Paragraph => "" , Dialog_Mark => "" ) ; -------------------- -- createSentence -- -------------------- function createSentence ( Self : not null access Object ) return CORBA. String is Generated_Sentence : String := Simple_Generator. Sentence ; begin return CORBA. To_CORBA_String ( Generated_Sentence ) ; end createSentence;

Now that we've defined the behaviour of our object, it's time to statup the CORBA bus and expose an instance of our brand new class.

For now, we won't use one of the most used CORBA service, the Name Service. Our server will only expose an IOR, reference to our object.

For this server, the echo example provided with PolyORB will be enough :

with Ada. Exceptions ; with Ada. Text_IO ; use Ada. Text_IO ; with CORBA. Impl ; with CORBA. Object ; with CORBA. ORB ; with PortableServer. POA . Helper ; with PortableServer. POAManager ; with CorbaCBSG. CBSG . Impl ; with PolyORB. CORBA_P . CORBALOC ; -- Allow to specify to PolyORB how to behave from a tasking point of view with PolyORB. Setup . No_Tasking_Server ; pragma Warnings ( Off, PolyORB. Setup . No_Tasking_Server ) ; procedure Server is begin declare -- Allows to get the paramters such as those defined in the CORBA standard like InitialRef Argv : CORBA. ORB . Arg_List := CORBA. ORB . Command_Line_Arguments ; begin -- Initiliaze our bus under the name ORB CORBA. ORB . Init ( CORBA. ORB . To_CORBA_String ( "ORB" ) , Argv ) ; declare -- The PortableObjectAdapter is where we register our objects Root_POA : PortableServer. POA . Local_Ref ; -- We declare a reference for our object Ref : CORBA. Object . Ref ; -- And its implementation Obj : constant CORBA. Impl . Object_Ptr := new CorbaCBSG. CBSG . Impl . Object ; begin -- We get the Root POA Root_POA := PortableServer. POA . Helper . To_Local_Ref ( CORBA. ORB . Resolve_Initial_References ( CORBA. ORB . To_CORBA_String ( "RootPOA" ) ) ) ; -- We start it up PortableServer. POAManager . Activate ( PortableServer. POA . Get_The_POAManager ( Root_POA ) ) ; -- We create a reference in order to expose it Ref := PortableServer. POA . Servant_To_Reference ( Root_POA, PortableServer. Servant ( Obj ) ) ; -- We display the IOR Put_Line ( "'" & CORBA. To_Standard_String ( CORBA. Object . Object_To_String ( Ref ) ) & "'" ) ; New_Line; -- and a corbaloc Put_Line ( "'" & CORBA. To_Standard_String ( PolyORB. CORBA_P . CORBALOC . Object_To_Corbaloc ( Ref ) ) & "'" ) ; -- Launch the server. CORBA.ORB.Run is supposed to never return, -- print a message if it does. CORBA. ORB . Run ; Put_Line ( "ORB main loop terminated!" ) ; end ; end ; exception when E : others => Put_Line ( "CBSG server raised " & Ada. Exceptions . Exception_Information ( E ) ) ; raise ; end Server;

Even if this code seems complex, it's always the same each time we will expose an object in the bus.

We just have to compile :

fred@coruscant :~/cbsg/corba/Ada $ mkdir obj fred@coruscant :~/cbsg/corba/Ada $ gnatmake server.adb -D obj -aI../.. `polyorb-config`

If everything went well, we just have to start the server

fred@coruscant :~/cbsg/corba/Ada $ ./server 'IOR:010000001700000049444c3a436f726261434253472f434253473a312e30000002000000000000005c000000010102000a0000003132372e302e302e3100f1761b0000002f30303030303030313154643637306239663031326565316538300001000000010000001c0000000100000001000100000000000001010002000000010101000201010003004f503c000000010100000c0000003139322e3136382e332e32007a3300001b0000002f30303030303030313154643637306239663031326565316538300000000000' 'corbaloc:iiop:1.2@127.0.0.1:30449//000000011Td670b9f012ee1e80'

if you want the server to listen on another address than 127.0.0.1, we just have to put it in polyorb.conf which must be at the place from which you run the server.



Waiter, please

Now that our server is waiting for a client, let's code it. First, a small simple Ada client as everything is ready for it. Once again, the echo example will be enough to create our client

with Ada. Command_Line ; with Ada. Text_IO ; with CORBA. ORB ; with CorbaCBSG. CBSG ; with PolyORB. Setup . Client ; pragma Warnings ( Off, PolyORB. Setup . Client ) ; procedure Client is use Ada. Command_Line ; use Ada. Text_IO ; use type CORBA. String ; Rcvd_Bullshits : CORBA. String ; Bullshit_Generator : CorbaCBSG. CBSG . Ref ; begin CORBA. ORB . Initialize ( "ORB" ) ; if Argument_Count not in 1 .. 2 then Put_Line ( "usage: client <IOR_string_from_server>" ) ; return ; end if ; -- Get a reference on the distributed object through its IOR or corbaloc CORBA. ORB . String_To_Object ( CORBA. To_CORBA_String ( Ada. Command_Line . Argument ( 1 ) ) , Bullshit_Generator ) ; -- check that the reference is correct if CorbaCBSG. CBSG . Is_Nil ( Bullshit_Generator ) then Put_Line ( "main : cannot invoke on a nil reference" ) ; return ; end if ; Rcvd_Bullshits := CorbaCBSG. CBSG . createSentence ( Bullshit_Generator ) ; Put_Line ( "The generator said : " & CORBA. To_Standard_String ( Rcvd_Bullshits ) ) ; exception when E : CORBA. Transient => declare Memb : CORBA. System_Exception_Members ; begin CORBA. Get_Members ( E, Memb ) ; Put ( "received exception transient, minor" ) ; Put ( CORBA. Unsigned_Long 'Image ( Memb. Minor ) ) ; Put ( ", completion status: " ) ; Put_Line ( CORBA. Completion_Status 'Image ( Memb. Completed ) ) ; end ; end Client;

As usual, compilation :

fred@coruscant :~/cbsg/corba/Ada $ gnatmake client -D obj -aI../.. `polyorb-config ` fred@coruscant :~/cbsg/corba/Ada $ ./client 'corbaloc:iiop:1.2@127.0.0.1:30449//000000011Td670b9f012ee1e80' The generator said : The business leaders embrace a growing execution by thinking outside of the box, whereas the steering committee delivers a top-down interoperability.

What to say other than superb !!



Ok, I promised a C++ client with OmniORB. As I always deliver the goods, here it is...

In the C++ directory, we begin by generating the needed files with the OmniORB IDL compiler.

fred@coruscant :~/cbsg/corba/C++ $ omniidl -bcxx ../idl/cbsg.idl

cbsg.hh and cbsg.cc get created. They contain at the same time stubs and skeletons. For the client-side, we will only use the stub part.

We create the client by taking ideas from the provided examples with OmniORB :



#include <cbsg.hh> #ifdef HAVE_STD #include <iostream> #include <fstream> using namespace std ; #else #include <iostream.h> #endif ////////////////////////////////////////////////////////////////////// int main ( int argc, char ** argv ) { try { //ORB Initialisation CORBA :: ORB_var orb = CORBA :: ORB_init ( argc, argv ) ; if ( argc ! = 2 ) { cerr << "usage: client <object reference>" << endl ; return 1 ; } //We create the CORBA object from the provided string CORBA :: Object_var obj = orb - > string_to_object ( argv [ 1 ] ) ; //We cast it to CBSG CorbaCBSG :: CBSG_var cbsgRef = CorbaCBSG :: CBSG :: _narrow ( obj ) ; //We check that the object exists if ( CORBA :: is_nil ( cbsgRef ) ) { cerr << "Can't narrow reference to type CBSG (or it was nil)." << endl ; return 1 ; } //And go, we call it CORBA :: String_var bullshit = cbsgRef - > createSentence ( ) ; cout << "The generator said : " << bullshit << endl ; //We stop the ORB orb - > destroy ( ) ; } catch ( CORBA :: TRANSIENT & ) { cerr << "Caught system exception TRANSIENT -- unable to contact the " << "server." << endl ; } catch ( CORBA :: SystemException & ex ) { cerr << "Caught a CORBA::" << ex._name ( ) << endl ; } catch ( CORBA :: Exception & ex ) { cerr << "Caught CORBA::Exception: " << ex._name ( ) << endl ; } catch ( omniORB :: fatalException & fe ) { cerr << "Caught omniORB::fatalException:" << endl ; cerr << " file: " << fe. file ( ) << endl ; cerr << " line: " << fe. line ( ) << endl ; cerr << " mesg: " << fe. errmsg ( ) << endl ; } return 0 ; }

We compile and test :

fred@coruscant :~/cbsg/corba/C++ $ g++ -o client client.cpp cbsgSK.cc -I. -lomniORB4 fred@coruscant :~/cbsg/corba/C++ $ ./client 'corbaloc:iiop:1.2@127.0.0.1:30449//000000011Td670b9f012ee1e80' The generator said : The granular low hanging fruit incentivises the steering committee.