Orbix Programmer's Guide C++ Edition


Chapter 2

First Application

The chapter describes how to develop a distributed application using Orbix. An example application illustrates the steps involved in the development process. These include defining an IDL interface, implementing this interface in C++, and developing a C++ client application.

This chapter describes the basic programming steps required to create Orbix objects, write server programs that expose those objects, and write client programs that access those objects.

This chapter illustrates the programming steps using an example named BankSimple. In this example, an Orbix server program implements two types of objects: a single object implementing the Bank interface, and multiple objects implementing the Account interface. A client program uses these clearly-defined object interfaces to create and find accounts, and to deposit and withdraw money.

On Windows and UNIX, the source code for the example described in this chapter is available in the demos\banksimple directory of your Orbix installation. On OS/390, the location of the source code is documented in orbixhlq.DEMOS.README(BANKSIMP), where orbixhlq represents your installation's high-level qualifier. This source code may differ slightly from the code published in this guide. On OpenVMS, the source code for the example is available in the orbix_root:[demos.banksimple] directory.

Developing a Distributed Application

To develop an Orbix application, you must perform the following steps:

  1. Identify the objects required in your system and define public interfaces to those objects using CORBA Interface Definition Language (IDL).
  2. Compile the IDL interfaces.
  3. Implement the IDL interfaces using C++ classes.
  4. Write a server program that creates instances of the implementation classes.
  5. Write a client program that accesses the server object.
  6. Compile the client and server.
  7. Run the application

Defining IDL Interfaces

Defining IDL interfaces to your objects is the most important step in developing an Orbix application. These interfaces define how clients access objects regardless of the location of those objects on the network.

An interface definition contains attributes and operations. Attributes allow clients to get and set values on the object. Operations are functions that clients can call on an object.

For example, the following IDL from the BankSimple example defines two interfaces for objects representing a bank application. The interfaces are defined inside an IDL module to prevent clashes with similarly-named interfaces defined in subsequent examples.

The interfaces to the BankSimple example are defined in IDL as follows:

// IDL
// In file banksimple.idl

  1.     module BankSimple {
    
      typedef float CashAmount; 
    
    
  1.       interface Account;
  2.         interface Bank {
          Account create_account (in string name); 
          Account find_account (in string name); 
        };
    
    
  3.         interface Account {
          readonly attribute string name;
          readonly attribute CashAmount balance; 
    
    
  4.           void deposit (in CashAmount amount);
          void withdraw (in CashAmount amount);  
        };
    };
    

This code is explained as follows:

  1. An IDL module is equivalent to a C++ namespace, and groups the definitions into a common namespace. Using a module is not mandatory, but is good practice.
  2. This is a forward declaration to the Account interface. It allows you to refer to Account in the Bank interface, before actually defining Account.
  3. The Bank interface contains two operations: create_account() and find_account(), allowing a client to create and search for an account.
  4. The Account interface contains two attributes: name and balance; both are readonly. This means that clients can get the balance or name, but cannot directly set them. If the readonly keyword is omitted, clients can also set these values.
  5. The Account interface also contains two operations: deposit() and withdraw(). The deposit() operation allows a client to deposit money in the account. The withdraw() operation allows a client to withdraw money from the account.

The parameters to these operations are labelled with the IDL keyword in. This means that their values are passed from the client to the object. Operation parameters can be labelled as in, out (passed from the object to the client) or inout (passed in both directions).

Compiling IDL Interfaces

You must compile IDL definitions using the Orbix IDL compiler. Before running the IDL compiler, ensure that your configuration is correct.

Setting Up Configuration for the IDL Compiler

You should ensure that the environment variable IT_CONFIG_PATH is set to the location of iona.cfg, the root Orbix configuration file.

UNIX

On UNIX, if iona.cfg is in directory /local/iona, perform the following steps:

  1. Under sh enter:
        % IT_CONFIG_PATH=/local/iona
        % export IT_CONFIG_PATH
    

    or under csh enter:

        % setenv IT_CONFIG_PATH /local/iona
  2. Set the environment variable LD_LIBRARY_PATH to include the location of the Orbix lib directory in a similar manner.

Windows

On Windows, if iona.config is in directory C:\iona\config, enter the following at the DOS prompt:

    set IT CONFIG PATH = C:\iona\config

OS/390

On OS/390, you can specify the IT_CONFIG_PATH environment variable using ENVAR in the list of runtime options preceding the arguments to any Language Environment program. For example:

  //STEP1 EXEC PGM=progname.
  //    PARM='ENVAR("IT_CONFIG_PATH=TEST.PARMS(ORBIXCFG)")
  //        /arguments'

Refer to the Orbix for OS/390 Administrator's Guide for full details about running Orbix on OS/390.

OpenVMS

On OpenVMS, the IT_CONFIG_PATH is a system logical, which is set to orbix.root: [config.your_host]. You can override this, for example:

$define IT_CONFIG_PATH orbix_root: [config.some_other_host].

Running the IDL Compiler

The IDL compiler checks the validity of the specification and generates C++ code that allows you to write client and server programs.

Windows and UNIX

To compile the Bank and Account interfaces defined in file banksimple.idl, run the IDL compiler as follows:

    idl [options] banksimple.idl

The -B compiler option produces BOAImpl classes for the server. Refer to Appendix A, "Orbix IDL Compiler Options" for a complete list of IDL compiler options.

OS/390

On OS/390, you can run the IDL compiler in batch or as a TSO command. All the JCL procedures that are supplied by IONA are stored in orbixhlq.PROCS. The JCL to run the IDL compiler in batch is as follows:

//STEP1 EXEC PROC=ORXI,
//      INTERFACE=BANKSIMP,
//      IDLPARMS='-B',
//      IDL=orbixhlq.DEMOS.IDL,
//      HH=output.pds.hh,
//      STUBS=output.pds.stubs

The TSO command to run the IDL compiler on OS/390 is as follows:

CALL 'orbixhlq.LOAD(IDL)' '-B orbixhlq.DEMOS.IDL(BANKSIMP)' ASIS

You must pass a fully-qualified data set name as an argument to the IDL compiler. The IDL compiler reads the input from this PDS and then writes the generated C++ files to it.

OpenVMS

On OpenVMS, the syntax for the complier command is:

$idl [options] banksimple.idl

Output from the IDL Compiler

The IDL compiler produces three C++ files that communicate with Orbix:

  1. A common header file containing declarations used by both client and server mode. This header file should be included in all client and server programs.
  2. A source file to be compiled and linked with servers (object skeleton code).
  3. A source file to be compiled and linked with clients (client stub code).

These source files contain C++ definitions that correspond to your IDL definitions. These C++ definitions allow you to write C++ client and server programs.

By default, these files are named as follows:
File Windows UNIX OS/390 OpenVMS
Header file banksimple.hh banksimple.hh output.pds.hh(
BANKSIM)
banksimple.hh
Client stub code banksimpleC.cpp banksimpleC.C output.pds.stubs(
BANKSIMC)
banksimpleC.cc
Server skeleton code banksimpleS.cpp banksimpleS.C output.pds.stubs(
BANKSIMS)
banksimpleS.cc

The Client Stub Code

The files banksimple.hh and banksimple.client.cxx define the C++ code that a client uses to access a Bank object. This code is termed the client stub code. For example, the banksimple.hh file for the BankSimple IDL includes a class to represent Bank and Account objects from a client's point of view.

The IDL declarations for the Account interface include the C++ definitions in the following code extract:

    // C++
    // In file banksimple.hh

    // Automatically generated by the IDL compiler.
    class Account: public virtual CORBA::Object  {
    public:
      // CORBA support functions and error handling are 
      // omitted here for clarity       virtual char* name ()         throw (CORBA::SystemException);       virtual CashAmount balance ()         throw (CORBA::SystemException);       virtual void deposit (CashAmount amount)         throw (CORBA::SystemException);       virtual void withdraw (CashAmount amount)         throw (CORBA::SystemException);     };

The environment argument (the last argument passed to each method) is omitted here.

This class represents the IDL Account interface in C++ allowing C++ clients to treat Account objects like any other C++ object. The readonly name and balance attributes map to member functions of the same name. The deposit() and withdraw() operations map to C++ member functions with equivalent parameters.

The Object Skeleton Code

The files banksimple.hh and banksimple.server.cxx define the C++ code that allows a server program to implement IDL interfaces and accept operation calls from clients to objects. This code is known as the object skeleton code. These server-side skeletons receive CORBA calls and pass them onto application code. When implementing a server using the BOAImpl approach, you inherit from a BOAImpl class generated by the IDL compiler.

For the Account interface the BOAImpl class includes the following C++ definitions:

    // C++
    // In file banksimple.hh

    // Automatically generated by IDL compiler.
    class AccountBOAImpl: public virtual Account  {
    public:
      virtual char*  name () 
        throw (CORBA::SystemException) = 0;
      virtual CashAmount balance () 
        throw (CORBA::SystemException) = 0;
      virtual void deposit (CashAmount amount) 
        throw (CORBA::SystemException) = 0;
      virtual void withdraw(CashAmount amount)
        throw (CORBA::SystemException) = 0;
    };

To implement the Account interface, you must inherit from this class and override the pure virtual functions that represent IDL operations with application code.

Implementing the IDL Interfaces

This example uses the CORBA BOAImpl approach to implementing an IDL interface. It uses two classes to implement the Bank and Account IDL interfaces in C++: BankSimple_BankImpl and BankSimple_AccountImpl. These classes inherit the IDL compiler-generated BankSimple::BankBOAImpl and BankSimple::AccountBOAImpl classes. These base classes provide all the Orbix functionality. All that remains is to override the abstract member functions that represent the IDL operations.

For example, the code for BankSimple_BankImpl is as follows:

// C++
// In file BankSimple\banksimple_bankimpl.h
// Implementation class for the Bank IDL interface.
...
  1. class BankSimple_BankImpl : public virtual BankSimple::BankBOAImpl
    {
      public:
        // Mapped IDL operations.
    
  1.     virtual BankSimple::Account_ptr
          create_account(const char*  name, CORBA::Environment&);
        virtual BankSimple::Account_ptr
          find_account( const char*  name, CORBA::Environment&);
        // C++ constructor and destructor.
    
  2.     BankSimple_BankImpl();
        virtual ~BankSimple_BankImpl();
    
      protected:
        static const int MAX_ACCOUNTS; 
    
  3.     BankSimple::Account_var* m_accounts;
    };
    

This code is explained as follows:

  1. Inheriting from the BOAImpl class generated by the IDL compiler provides Orbix functionality for the server objects.
  2. Operations defined in IDL are implemented by corresponding operations in C++. The IDL Account type is represented by an Account_ptr.
  3. The constructor and destructor are normal C++ functions that can be called by server code. Only IDL functions can be called remotely by clients.
  4. The accounts created by the bank are stored in an array of Account_var. These are like pointers; for more information on Account_var, refer to "CORBA Object References" on page 40.

You can implement the member functions of BankSimple_BankImpl as follows:

// C++
// In file banksimple_bankimpl.cxx

#include "banksimple_bankimpl.h"
#include "banksimple_accountimpl.h"

  1. const int BankSimple_BankImpl::MAX_ACCOUNTS = 1000;
    BankSimple_BankImpl::BankSimple_BankImpl() :
    m_accounts(new BankSimple::Account_var[MAX_ACCOUNTS]) {
    // Make sure all accounts are nil.
      for (int i = 0; i < MAX_ACCOUNTS; ++i){
        m_accounts[i] = BankSimple::Account::_nil();
      }
    }
    
    BankSimple_BankImpl::~BankSimple_BankImpl() { 
      delete [] m_accounts;
    }
    
    // Add a new account.
    BankSimple::Account_ptr BankSimple_BankImpl::create_account 
    (const char*  name, CORBA::Environment& ) {
    
      int i = 0;
      for ( ; i < MAX_ACCOUNTS && !CORBA::is_nil(m_accounts[i]); ++i)
      {}
      if (i < MAX_ACCOUNTS){
    
  1.     m_accounts[i] = new BankSimple_AccountImpl(name, 0.0);
        cout << "create_account: Created account with name: "
           << name << endl;
    
  2.     return BankSimple::Account::_duplicate(m_accounts[i]);
      }
      else{
        cout << "create_account: failed, no space left!" << endl;
    
  3.     return BankSimple::Account::_nil();
      }
    }
    
    // Find a named account.
    BankSimple::Account_ptr BankSimple_BankImpl::find_account
    (const char*  name, CORBA::Environment& ) {
    
      int i = 0;
      for (  ; i < MAX_ACCOUNTS &&( CORBA::is_nil(m_accounts[i]) ||
          strcmp(name, m_accounts[i]->name()) != 0); ++i)
      { }
    
      if (i < MAX_ACCOUNTS){
        cout << "find_account: found account named" << name << endl;
        return BankSimple::Account::_duplicate(m_accounts[i]);
      }
      else{
        cout << "find_account: no account named" << name << endl;
        return BankSimple::Account::_nil();
      }
    }
    

The code is explained as follows:

  1. The maximum number of accounts that the bank can handle in this simple implementation is set as a constant of 1000.
  2. New accounts are created with a balance of zero.
  3. When an Account reference is returned from create_account() and find_account() operations, it must be duplicated. According to CORBA memory management rules, this reference is released by the caller.
  4. If an account cannot be created, nil is returned.

Refer to the banksimple\demos directory of your Orbix installation for the corresponding code for BankSimple_AccountImpl.

Writing an Orbix Server Application

To write a C++ program that acts as an Orbix server, perform the following steps:

  1. Initialize the server connection to the Orbix ORB, and to the Basic Object Adaptor (BOA).
  2. Create an implementation object. This is done by creating instances of the implementation classes.
  3. Allow Orbix to receive and process incoming requests from clients.

This section describes each of these programming steps in turn.

Initializing the ORB

Because Orbix uses the standard OMG IDL to C++ mapping, all servers and clients must call CORBA::ORB_init() to initialize the ORB. This returns a reference to the ORB object. The ORB methods defined by the standard can then be invoked on this instance.

  // C++
  // In file server.cxx
  ...  
  try { 
    ...
    // Initialize the ORB.
    CORBA::ORB_var orb = CORBA::ORB_init(argc,argv,"Orbix");
    ...
  }
  catch (const CORBA::SystemException& e) {
    cout << "Unexpected exception" << e << endl;
  }

In this code sample, the argc parameter refers to the number of arguments in argv. The argv parameter is a sequence of configuration strings used if "Orbix" is a null string; the string "Orbix" identifies the ORB. Refer to the Orbix Reference Guide for more information on CORBA::ORB_init().

Orbix raises a C++ exception to indicate that a function call has failed. All CORBA exceptions derive from CORBA::Exception. Many Orbix functions (for example, ORB_init()) and all IDL operations may raise a CORBA system exception, of type CORBA::SystemException.

You must use C++ try/catch statements to handle exceptions, as illustrated in the preceding code sample. In the remainder of this chapter, try/catch statements are omitted for clarity.

Creating an Implementation Object

To create an implementation object, you must create an instance of your implementation class in your server program. Typically a server program creates a small number of objects in its main() function, and these objects may in turn create further objects. In the BankSimple example, the server creates a single bank object in its main() function. This bank object then creates accounts when create_account() is called by the client.

For example, to create an instance of BankSimple::Bank in your server main() function, do the following:

  // C++
  // In file server.cxx

  #include "banksimple_bankimpl.h"  
  int main ( ... ) {
    ...      
    // Create a bank implementation object.
    BankSimple::Bank_var my_bank = new BankSimple_BankImpl;
    ...
  }

A server program can create any number of implementation objects for any number of IDL interfaces.

Note that implementation object has a name that uniquely identifies it to the server. This name is called the "marker" (discussed more in `Making Objects Available in Orbix" on page 159). The above code does not explicitly set the marker for the Bank implementation object, hence the ORB picks an unused random name. In general, you always need to explicitly set the marker from your implementation objects (see `Making Objects Available in Orbix" on page 159).

Receiving Client Requests

When a server instantiates an Orbix object (for example, one inheriting from the BOAImpl class), it is automatically registered with Orbix as a distributed object. To make objects available to clients, the server must call the Orbix function CORBA::BOA::impl_is_ready() to complete its initialization and to process operation calls from clients.

You can code a complete server main() function as follows:

// C++ 
// In file server.cxx

#include "banksimple_bankImpl.h"
#include "banksimple_accountImpl.h"
#include <it_demo_nsw.h>

// Server mainline.
int main (int argc, char*  argv[]) {
  try {
    // Use standard demo server options.
  1.     IT_Demo_ServerOptions serveropt("IT_Demo/BankSimple/Bank");
        ...
    
  1.     CORBA::ORB_var orb = CORBA::ORB_init(argc, argv, "Orbix");
        CORBA::BOA_var boa = orb->BOA_init(argc, argv, "Orbix_BOA");
    
        // Set diagnostics.
        orb->setDiagnostics(serveropt.diagnostics());
    
        // Set server name.
    
  2.     orb->setServerName(serveropt.server_name());    
    
    
  3.     // Indicate server should not quit while clients
        // are connected.
        boa->setNoHangup(1);  
        // Set up Naming Service Wrappers (NSW). 
    
  4.     IT_Demo_NSW ns_wrapper;
  5.     ns_wrapper.setNamePrefix(serveropt.context());
  6.     const char* bank_name = "BankSimple.Bank";
        ...  
        // Create a bank implementation object.
    
  7.     BankSimple::Bank_var my_bank = new BankSimple_BankImpl;
    
    
  8.     // Register server object with the Naming Service.
        if (serveropt.bindns()) {
          cout << "Binding objects in the Naming Service" << endl;
          ns_wrapper.registerObject(bank_name, my_bank);
        }
    
        // Server has completed initialization, wait for 
        // incoming requests.
    
  9.     boa->impl_is_ready( (char*)serveropt.server_name(),
                              serveropt.timeout());
    
        // impl_is_ready() returns only when Orbix times-out 
        // an idle server.
        cout << "server exiting" << endl;
      }
      catch (const CORBA::Exception& e) {
        cerr << "Unexpected exception" << e << endl;
        return 1;
      }
      return 0;
    };
    

This code is explained as follows:

  1. Create the standard server options for use throughout the demonstration and set the server name to IT_Demo/BankSimple/Bank. The Orbix demos\demolib directory contains the standard server and client options used by the Bank series examples in this book.
  2. Initialize the ORB and BOA. The ORB object provides functionality common to both clients and servers. The BOA (Basic Object Adapter) object is derived from the ORB and provides additional server-side functionality.

    The ORB and the BOA are different views of the same ORB API--this object is also available via the global variable CORBA::Orbix. However, use of this variable is not CORBA-defined and is discouraged.

  3. Set the server name using setServerName(serveropt.server_name()). This is required by Orbix before exporting object references.
  4. Create a Naming Service Wrapper (NSW) object. To simplify the use of the Naming Service, a Naming Service Wrapper is provided. This hides the low-level detail of the CORBA Naming Service. Refer to "Using the Naming Service in Orbix Example Applications" on page 168 for details of the Naming Service wrapper functions.
  5. Define a name prefix that is used for subsequent operations.
  6. BankSimple.Bank is the name that the bank object is known by in the Naming Service.
  7. The created BankSimple instance is my_bank. This object implements an instance of the IDL interface Bank. This is called directly from client applications using the CORBA standard Internet Inter-ORB Protocol (IIOP).
  8. The server now registers its objects in the Naming Service using the Naming Service wrapper function registerObject().
  9. The CORBA::BOA::impl_is_ready() operation is called to complete server initialization. This takes a server name and a timeout value as parameters. You can specify any name for your server; however, the name should match the name used to register the server in the Implementation Repository, and the argument used to call setServerName().

    The timeout value indicates the period of time, in milliseconds, that the impl_is_ready() call should block for while waiting for an operation call to arrive from a client. If no call arrives in this period, impl_is_ready() returns. If a call arrives, Orbix calls the appropriate member function on the implementation object and the timeout counter starts again from zero.

Writing an Orbix Client Application

To write a C++ client program to an Orbix object, you must perform the following steps:

  1. Initialize the client connection to the ORB.
  2. Get a reference to an object.
  3. Invoke attributes and operations defined in the object's IDL interface.

This section describes each of these steps in turn.

Initializing the ORB

All clients and servers must call CORBA::ORB_init() to initialize the ORB. This returns a reference to the ORB object. The ORB methods defined by the standard can then be invoked on this instance.

CORBA Object References

A CORBA object reference identifies an object in your system. When an object reference enters a client address space, Orbix creates a proxy object that acts as a local representative for the remote implementation object. Orbix forwards operation invocations on the proxy object to corresponding functions in the implementation object.

Consider an object reference as a pointer that can point to an object in a remote server process. Object references to an object of interface X are represented by a type X_ptr, which behaves like a normal C++ pointer.

An object reference requires some memory in the client (the memory needed by the proxy object), so you must release each reference when finished by calling CORBA::release(). The CORBA::release() method releases the client memory used by the object reference--it does not affect the remote server object.

For interface X, the IDL compiler also generates a smart pointer class called X_var that automates memory management. X_var behaves just like X_ptr, except it releases the reference when it goes out of scope, or if a new reference is assigned.

Getting a Reference to an Object

The flexible CORBA-defined way to obtain object references is to use the standard CORBA Naming Service. The CORBA Naming Service allows a name to be bound to an object and allows that object to be found subsequently by resolving that name within the Naming Service.

A server that holds an object reference can register it with the Naming Service, giving it a name that can be used by other components of the system to find the object. The Naming Service maintains a database of bindings between names and object references. A binding is an association between a name and an object reference. Clients can call the Naming Service to resolve a name, and this returns the object reference bound to that name. The Naming Service provides operations to resolve a name, to create new bindings, to delete existing bindings, and to list the bound names.

A name is always resolved within a given naming context. The naming context objects in the system are organized into a graph, which may form a naming hierarchy, much like that of a file system. The following sample code shows how the client uses the Naming Service wrapper functions to obtain an object reference:

    // C++
    // In file client.cxx
    ...
    // Naming Service Setup.
    // Create a Naming Service Wrapper object.
    IT_Demo_NSW ns_wrapper;
  1.     ns_wrapper.setNamePrefix(clientopt.context());
        
        // Get CORBA object. 
        // Specify the object name in the Naming Service.
    
  1.     const char* object_name = "BankSimple.Bank";
     
        // Get a reference to the required object from the NSW.
    
  2.     CORBA::Object_var obj = ns_wrapper.resolveName(object_name);
    
        // Narrow the object reference.
    
  3.     BankSimple::Bank_var bank = BankSimple::Bank::_narrow(obj);
        if (CORBA::is_nil(bank)) {
          cerr << "Object \"" << object_name 
              << "\"in the Naming Service" << endl
              << "\tis not of the expected type."<< endl;       return 1;     }          // Start client menu loop
  4.       BankMenu main_menu(bank);
          main_menu.start();
        }
      ...
    }
    

This code is described as follows:

  1. Define a name prefix used by the Naming Service wrapper object for subsequent operations.
  2. BankSimple.Bank is the name by which the bank object is known in the Naming Service.
  3. The method nswrapper::resolveName() retrieves the object reference from the Naming Service placed there by servers. The object_name parameter is the name of the object to resolve. This must match the name used by the server when it calls registerObject().
  4. The return type from resolveName() is of type CORBA::Object. You must call _narrow() to safely cast down from the base class to the Bank IDL class, before you can make invocations on remote Bank objects. The client stub code generated for every IDL class contains the _narrow() function definition for that class.
  5. This creates and runs a main menu for Bank clients. This menu enables you to find or create accounts by calling the appropriate C++ member function on the object reference.

Invoking IDL Attributes and Operations

To access an attribute or an operation associated with an object, call the appropriate C++ member function on the object reference. The client-side proxy redirects this C++ call across the network to the appropriate member function of the implementation object.

The main BankSimple client program calls a simple interactive menu. This enables you to call IDL operations on a Bank. The following code extracts show the code called when you choose to create or find an account:

// C++
// In file bankmenu.cxx

void BankMenu::do_create() throw(CORBA::SystemException) {

  cout << "Enter account name: " << flush;
  CORBA::String_var name = IT_Demo_Menu::get_string();
  
  1.   BankSimple::Account_var account = m_bank->create_account(name);
      
      // Start a sub-menu with the returned account reference.
      AccountMenu sub_menu(account);
      sub_menu.start();
    }
    
    // do_find -- calls find account and runs account menu.
    
    void BankMenu::do_find throw (CORBA::SystemException) {
    
      cout << "Enter account name: " << flush;
    
  1.   CORBA::String_var name = IT_Demo_Menu::get_string();
      
      BankSimple::Account_var account = m_bank->find_account(name);
        AccountMenu sub_menu(account)
      sub_menu.start();
    }
    

This code is explained as follows:

  1. m_bank is a Bank_var--a C++ helper class automatically generated by the IDL compiler from the Bank interface. This is used like a normal C++ pointer to call IDL operations just like C++ operations.
  2. The String_var name variable is used for the account name entered. The caller is not responsible for releasing the memory--String_var automatically does this when it goes out of scope.

Use the C++ arrow operator (->) to access the operations defined in IDL through a BankSimple::Bank_var object. Call those member functions using normal C++ calls and test for errors using C++ exception handling.

Compiling the Client and Server

To build the client and server, you must compile and link the relevant C++ files with the Orbix library. On UNIX, this is liborbix; on Windows, this is ITMi.lib. On OpenVMS, this is liborbix.olb. These files are available in the Orbix lib directory.


Note:   For demonstration-specific functionality, you must also include libdemo.a on UNIX and demolib.lib on Windows.

Compiling the Client

To build the client application, compile and link the following C++ files, and the Orbix library:

client.cxx is the source file for the client main() function.

Compiling the Server

To build the server application, compile and link the following C++ files, and the Orbix library.

server.cxx is the source file for the server main() function.

The Orbix demos/banksimple directory includes a makefile that compiles and links the bank client and server demonstration code.

To build the executables, type one of the following in the demos\banksimple directory of your Orbix installation:
Windows >nmake
UNIX %make
OpenVMS $mms

On OS/390, JCL is provided to build and run the demo in ORBIXhlq.DEMOS.BUILD.JCL.

Running the Application

To run the application, do the following:

  1. Run the Orbix daemon process (orbixd) on the server host.
  2. Register the server in the Orbix Implementation Repository.
  3. Run the client program.

Running the Orbix Daemon

Before a client can access a server, the server must be registered with the Orbix daemon. Before running the Orbix daemon, ensure that the environment variable IT_CONFIG_PATH is set as described in "Setting Up Configuration for the IDL Compiler" on page 28.

Windows and UNIX

You can run the Orbix daemon on the server host by typing orbixd at the command line or using the Start menu on Windows.

OS/390

On OS/390, the daemon can be run as a batch job or a started task. Sample JCL is supplied in orbixhlq.JCL(ORBIXD).

OpenVMS

On OpenVMS, the daemon should be started using the command:

$orbixd a em on start

Registering the Server

The Implementation Repository is the component of Orbix that stores information about servers available in the system. Before running your application, you must register your server in the Implementation Repository.

Windows and UNIX and OpenVMS

To register the server(s), use either the Server Manager GUI tool or run the Orbix putit command on the server host as follows:

putit server_name server_executable

OS/390

To register the server(s), you can execute utilities either by TSO call commands or the Orbix ISPF panels. For example:

CALL orbixhlq.LOAD(PUTIT)'server_name execution_jcl_location' ASIS

On all platforms, server_name is the name of your server passed to impl_is_ready().

If a server binds names in the Naming Service, you may need to run it once to allow it to set up the name bindings. Details of how to do this depend on the server used. The demonstrations provide a makefile that do the necessary server registration and set up names in the Naming Service.

To register the server, type one of the following:
Windows > nmake register
UNIX % make register
OpenVMS $mms register

On OS/390, the details for each demonstration are documented in a member of orbixhlq.DEMOS.README.

Running the Client

When a client binds to an object in a server registered in the Implementation Repository, the Orbix daemon automatically launches the server executable file. Consequently, you can run the client without running the server in advance.

Before running the client, ensure that the environment variable IT_CONFIG_PATH is set as described in "Setting Up Configuration for the IDL Compiler" on page 28.

Windows and UNIX

Run the example client by entering client at the command-line prompt. The client displays a text menu allowing you to choose the actions you want to take, and then prompts you for the necessary information. The server outputs messages when it processes incoming calls. You can see these messages by looking at the application shell window launched by the Orbix daemon.

OS/390

Run the example client using the following TSO command:

CALL orbixhlq.DEMOS.LOAD(BANKCLNT) 
'ENVAR("IT_CONFIG_PATH=orbixhlq.PROCS(ORBIXCFG)")/'

The client displays a text menu allowing you to choose the actions you want to take, and then prompts you for the necessary information. The server outputs messages when it processes incoming calls. You can view these messages by looking at the SYSPRINT output.

OpenVMS

On OpenVMS, run the example client by entering mcr []client.exe at 
the command line prompt. The client displays a text menu that 
allows you to choose the actions you want to take and then prompts 
you for the necessary information. The server outputs messages 
when it processes incoming calls. These messages are logged to the 
orbix$log:process_name.log file. You can identify the name of the 
process your server is running by typing observ at the command line 
prompt.

Summary of Programming Steps

To develop a distributed application with Orbix, do the following:

  1. Identify the objects required in your system and define the public interfaces to those objects using the CORBA Interface Definition Language (IDL).
  2. Compile the IDL interfaces.
  3. Implement the IDL interfaces with C++ classes.
  4. Write a server program that creates instances of the implementation classes. This involves:
    1. Initializing the ORB.
    2. Creating initial implementation objects.
    3. Allowing Orbix to receive and process incoming requests from clients.
  5. Write a client program that accesses the server objects. This involves:
    1. Initializing the ORB.
    2. Getting a reference to an object.
    3. Invoking object attributes and operations.
  6. Compile the client and server.
  7. Run the application. This involves:
    1. Running the Orbix daemon process.
    2. Registering the server in the Implementation Repository.
    3. Running the client.


support@iona.com
Copyright © 2000, IONA Technologies PLC.