Spreading bullshits

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...


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
      Generated_Sentence : String := Simple_Generator.Sentence;
      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 PortableServer.POA.Helper;
with PortableServer.POAManager;
with CorbaCBSG.CBSG.Impl;
-- 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
      -- 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;
      -- Initiliaze our bus under the name ORB
      CORBA.ORB.Init (CORBA.ORB.To_CORBA_String ("ORB"), Argv);
	 -- 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;
         -- We get the Root POA
         Root_POA := PortableServer.POA.Helper.To_Local_Ref
	      (CORBA.ORB.To_CORBA_String ("RootPOA")));
	 -- We start it up
           (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
	      & CORBA.To_Standard_String (CORBA.Object.Object_To_String (Ref))
	      & "'");
         --  and a corbaloc
	      & 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.
         Put_Line ("ORB main loop terminated!");
   when E : others =>
        ("CBSG server raised " & Ada.Exceptions.Exception_Information (E));
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


if you want the server to listen on another address than, 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 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;
   CORBA.ORB.Initialize ("ORB");
   if Argument_Count not in 1 .. 2 then
        ("usage: client <IOR_string_from_server>");
   end if;
   --  Get a reference on the distributed object through its IOR or corbaloc
     (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");
   end if;
   Rcvd_Bullshits := CorbaCBSG.CBSG.createSentence(Bullshit_Generator);
   Put_Line ("The generator said : " & CORBA.To_Standard_String (Rcvd_Bullshits));
   when E : CORBA.Transient =>
         Memb : CORBA.System_Exception_Members;
         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 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@'
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; 
#include <iostream.h>
int main(int argc, char** argv) 
      //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
      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@'
The generator said : The granular low hanging fruit incentivises the steering committee.

Depending on the version of OmniORB, it could be necessary to include -lomnithread on the compilation command line.
And that's it !
Well, I promised high availability and tutti quanti but finally, I will keep it for the next time :)
Hope it pleased you.


[1] But just read interfaces, you would get a bullshit overdose

[2] This second parameter quite obscur is not used by Gautier, so caution :D


2. Le par Fred

@LolcatLovah :

Packaging this should not be a problem in FreeBSD ports... I wonder if I won't propose it :D

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.

La discussion continue ailleurs