Keywords: doc

Next: Separating client and server Up: Sample Program Previous: Standalone program

## MICO application

Now we want to turn the standalone implementation from the previous section into a MICO application. Because CORBA objects can be implemented in different programming languages the specification of an object's interface and implementation have to be separated. The implementation is done using the selected programming language, the interface is specified using the so called Interface Definition Language (IDL). Basically the CORBA IDL looks like C++ reduced to class and type declarations (i.e., you cannot write down the implementation of a class method using IDL). Here is the interface declaration for our account object in CORBA IDL:

      interface Account {
          void deposit (in unsigned long amount);
          void withdraw (in unsigned long amount);
          long balance ();
      };

As you can see it looks quite similar to the class declaration in section 3.3.1. The `in` declarator declares `amount` as an input parameter to the `deposit()` and `withdraw()` methods. Usually one would save the above declaration to a file called `account.idl`.

The next step is to run this interface declaration through the IDL compiler that will generate code in the selected implementation programming language (C++ in our example). The MICO IDL compiler is called `idl` and is used like this:

      idl account.idl

The IDL compiler will generate two files: `account.h` and `account.cc` (see figure 3.2). The former contains class declarations for the base class of the account object implementation and the stub class a client will use to invoke methods on remote account objects. The latter contains implementations of those classes and some supporting code. For each interface declared in an IDL-file, the MICO IDL compiler will produce three C++ classes.

Figure 3.2: Creation process of a MICO application.

The three classes are depicted in figure 3.3 between the two dashed lines. The class `Account` serves as a base class. It contains all definitions which belong to the interface `Account`, like local declarations of user defined data structures. This class also defines a pure virtual function for each operation contained in the interface. The following shows a bit of the code contained in class `Account`:

      // Code excerpt from account.h
      class Account : virtual public CORBA::Object {
          ...
      public:
          ...
          virtual void deposit (CORBA::ULong amount) = 0;
          virtual void withdraw (CORBA::ULong amount) = 0;
          virtual CORBA::Long balance () = 0;
      }

The class `Account_skel` is derived from `Account`. It adds a dispatcher for the operations defined in class `Account`. But it does not define the pure virtual functions of class `Account`. The classes `Account` and `Account_skel` are therefore abstract base classes in C++ terminology. To implement the account object you have to subclass `Account_skel` providing implementations for the pure virtual methods `deposit()`, `withdraw()` and `balance()`.

The class `Account_stub` is derived from class `Account` as well. In contrast to class `Account_skel` it defines the pure virtual functions. The implementation of these functions which is automatically generated by the IDL-compiler is responsible for the parameter marshalling. The code for `Account_stub` looks like this:

      // Code excerpt from account.h and account.cc
      class Account;
      typedef Account *Account_ptr;

      class Account_stub : virtual public Account {
          ...
      public:
          ...
          void deposit (CORBA::ULong amount)
          {
             // Marshalling code for deposit
          }
          void withdraw (CORBA::ULong amount)
          {
             // Marshalling code for withdraw
          }
          CORBA::Long balance ()
          {
             // Marshalling code for balance
          }
      }

This makes `Account_stub` a concrete C++ class which can be instantiated. The programmer never uses the class `Account_stub` directly. Access is only provided through class `Account` as will be explained later.

Figure 3.3: Inheritance relationship between stub- and skeleton classes.

It is worthwile to see where the classes `Account` and `Account_skel` are derived from. `Account` inherits from `Object`, the base class for all CORBA objects. This class is located in the MICO library. The more interesting inheritance path is for `Account_skel`. `Account_skel` inherits from `MethodDispatcher`, a class located again in the MICO library. This class is responsible for dispatching a method invocation. It maintains a list of method dispatchers. The class `MethodDispatcher` inherits from `DynamicImplementation`. This class represents the interface to the dynamic skeleton interface (DSI) which is described by the CORBA standard in chapter [18.4.5]. This is a notably feature of MICO: the code generated by the MICO IDL compiler makes use of CORBAs DII and DSI.

Up until now we have written the interface of an account object using CORBA IDL, saved it as `account.idl`, ran it through the IDL compiler which left us with two files called `account.cc` and `account.h` that contain the class declarations for the account implementation base class (`Account_skel`) and the client stub (`Account_stub`). Figure 3.2 illustrates this. What is left to do is to subclass `Account_skel` (implementing the pure virtual methods) and write a program that uses the bank account. Here we go:

     1: #include "account.h"
     2:
     3: class Account_impl : virtual public Account_skel
     4: {
     5: private:
     6:   CORBA::Long _current_balance;
     7:
     8: public:
     9:   Account_impl()
    10:   {
    11:     _current_balance = 0;
    12:   };
    13:   void deposit( CORBA::ULong amount )
    14:   {
    15:     _current_balance += amount;
    16:   };
    17:   void withdraw( CORBA::ULong amount )
    18:   {
    19:     _current_balance -= amount;
    20:   };
    21:   CORBA::Long balance()
    22:   {
    23:     return _current_balance;
    24:   };
    25: };
    26:
    27:
    28: int main( int argc, char *argv[] )
    29: {
    30:   // ORB initialization
    31:   CORBA::ORB_var orb = CORBA::ORB_init( argc, argv, "mico-local-orb" );
    32:   CORBA::BOA_var boa = orb->BOA_init( argc, argv, "mico-local-boa" );
    33:
    34:   // server side
    35:   Account_impl* server = new Account_impl;
    36:   CORBA::String_var ref = orb->object_to_string( server );
    37:   cout << "Server reference: " << ref << endl;
    38:
    39:   //----------------------------------------------------------------------
    40:
    41:   // client side
    42:   CORBA::Object_var obj = orb->string_to_object( ref );
    43:   Account_var client = Account::_narrow( obj );
    44:
    45:   client->deposit( 700 );
    46:   client->withdraw( 250 );
    47:   cout << "Balance is " << client->balance() << endl;
    48:
    49:   // We don't need the server object any more. This code belongs
    50:   // to the server implementation
    51:   CORBA::release( server );
    52:   return 0;
    53: }

Lines 3-25 contain the implementation of the account object, which is quite similar to the implementation in section 3.3.1. Note that the class `Account_impl` inherits the from class `Account_skel`, which contains the dispatcher for this interface, via a virtual public derivation. Although the keyword `virtual` is not required in this case, it is a good practise to write it anyway. This will become important when interface inheritance is discussed in section 5.5.

The `main()` function falls into two parts which are seperated by the horizontal line (line 39): Above the separator is the server part that provides an account object, below the line is the client code which invokes methods on the account object provided by the server part. Theoretically the two parts could be moved to two seperate programs and run on two distinct machines and almost nothing had to be changed in the code. This will be shown in the next section.

In line 32 the MICO initialization function is used to obtain a pointer to the Object Request Broker (ORB) object--a central part of each CORBA implementation. Among others the ORB provides methods to convert object references into a string representation and vice versa. In line 35 an account object called `server` is instantiated. Note that it is not permitted to allocate CORBA objects on the run-time stack. This is because the CORBA standard prescribes that every object has to be deleted with a special function called `CORBA::release()`. Automatic allocation of an object would invoke its destructor when the program moves out of scope which is not permissible. In our little sample program the server object is deleted explicitly in line 51.

In line 36 the ORB is used to convert the object reference into a string that somehow has to be transmitted to the client (e.g., using Email, a name service or a trader). In our example client and server run in the same address space (i.e. the same process) so we can turn the string into an object reference back again in line 42. Line 43 uses the `Account::_narrow()` method to downcast the object reference to an `Account_var`. The rest of `main()` just uses the account object which was instantiated in line 35.

`Account_var` is a smart pointer to `Account` instances. That is an `Account_var` behaves like an `Account_ptr` except that the storage of the referenced object is automatically freed via the aforementioned `release()` function when the `Account_var` is destroyed. If you use `Account_ptr` instead you would have to use `CORBA::release()` explicitly to free the object when you are done with it (never use `delete` instead of `CORBA::release()`).

Assuming the above code is saved to a file called `account_impl.cc` you can compile the code like this:

      mico-c++ -I. -c account_impl.cc -o account_impl.o
      mico-c++ -I. -c account.cc -o account.o
      mico-ld -I. -o account account_impl.o account.o -lmico2.2.3

This will generate an executable called `account`. Running it produces the following output:

      Server reference: IOR:1200000069643a6d69636f2d6c6f63616c2d626f61310\
      1000000b77a0000160000001200000069643a6d69636f2d6c6f63616c2d626f6131
      Balance is 450

You can find the source code for this example in the `demo/account` directory within the MICO source tree.

Next: Separating client and server Up: Sample Program Previous: Standalone program