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 vPolyORB 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 CSBG 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 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 csbg. The file csbg.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 wainting for a client, let's code it. First, a small simple Ada client as everything is reay 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

csbg.hh and csbg.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.

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.

Notes

[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