mario::konrad
programming / C++ / sailing / nerd stuff
CORBA Tutorial: Connecting 3 ORBs
© 2004 / Mario Konrad

Introduction

There are many ORB implementation out there, as well as simple examples how to utilize them. Most of the examples show the basiscs, many show also advanced features. This tutorial shows how to implement a distributed application with three different ORBs involved.

This is not a tutorial about basics of CORBA. This is a tutorial about how to put several ORBs to work together. For an introduction into CORBA or a particular ORB, read one of the many books or the documentation of the ORB of your choise.

As mentioned above this tutorial uses three ORBs:

Please see Links for links to the resources on the net. We will see how to implement and start each of them as server as well as client, not at the same time of course.

Please note that this tutorial is not for beginners in programming, experience at least in C++ and basic knowledge about CORBA are expected. You will need experience in compiling source code, running command prompts and reading error message from the compiler (if anything goes wrong). Examples are held in C++ and Java. There are other CORBA bindings but not covered by this tutorial.

Implementations

This chapter shows for every used ORB the implementations for client and server. The distributed object we will going to use is defined in the file:

hello.idl:

interface Hello
{
    string say_hello(in string client);
};

The interface is not very complex. The intention is that the client sends a string (its identification) to the server (parameter client), the server prints it out (stdout) and send back it own identification. This behaviour will be implemented in the following sections, one for each ORB.

omniORB 4.0.3

Overview

There are four files, implementing client and server:

File Description
client.cpp The client program
server.cpp The server program
Hello_impl.h The class delcaration of the implementation for the distributed object
Hello_impl.cpp The implementation of the class for the distributed object

Dependencies

The following picture shows the dependencies of files, generated and existing.

Files

This subsection shows the source codes of all files needed to build the client and server programs.

Hello_impl.h:

#ifndef __HELLO_IMPL_H__
#define __HELLO_IMPL_H__

#include "hello.hh"

class Hello_impl : public POA_Hello
{
    public:
        virtual char * say_hello(const char * client);
};

#endif

Hello_impl.cpp:

#include "Hello_impl.h"
#include <iostream>

using namespace std;

char * Hello_impl::say_hello(const char * client)
{
    cout << "omniORB C++ server: " << client << endl;
    char * server = CORBA::string_alloc(32);
    strncpy(server, "omniORB C++ server", 32);
    return server;
}

client.cpp:

#include "hello.hh"
#include <iostream>
#include <CORBA.h>
#include <Naming.hh>

using namespace std;

int main(int argc, char ** argv)
{
    try {
        // init ORB
        CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);

        // resolve service
        Hello_ptr hello = 0;
        try {
            CORBA::Object_var ns_obj = orb->resolve_initial_references("NameService");
            if (!CORBA::is_nil(ns_obj)) {
                CosNaming::NamingContext_ptr nc = CosNaming::NamingContext::_narrow(ns_obj);
                CosNaming::Name name;
                name.length(1);
                name[0].id = CORBA::string_dup("TestServer");
                name[0].kind = CORBA::string_dup("");
                CORBA::Object_ptr obj = nc->resolve(name);
                if (!CORBA::is_nil(obj)) {
                    hello = Hello::_narrow(obj);
                }
            }
        } catch (CosNaming::NamingContext::NotFound &) {
            cerr << "not found" << endl;
        } catch (CosNaming::NamingContext::InvalidName &) {
            cerr << "invalid name" << endl;
        } catch (CosNaming::NamingContext::CannotProceed &) {
            cerr << "cannot proceed" << endl;
        }

        if (!CORBA::is_nil(hello)) {
            char * server = hello->say_hello("omniORB C++ client");
            cout << "answer from: " << server << endl;
            CORBA::string_free(server);
        }

        // destroy ORB
        orb->destroy();
    } catch (CORBA::UNKNOWN) {}
}

server.cpp:

#include "Hello_impl.h"
#include <iostream>
#include <CORBA.h>
#include <Naming.hh>

using namespace std;

int main(int argc, char ** argv)
{
    try {
        // init ORB
        CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);

        // init POA
        CORBA::Object_var poa_obj = orb->resolve_initial_references("RootPOA");
        PortableServer::POA_var poa = PortableServer::POA::_narrow(poa_obj);
        PortableServer::POAManager_var manager = poa->the_POAManager();

        // create service
        Hello_impl * service = new Hello_impl;

        // register within the naming service
        try {
            CORBA::Object_var ns_obj = orb->resolve_initial_references("NameService");
            if (!CORBA::is_nil(ns_obj)) {
                CosNaming::NamingContext_ptr nc = CosNaming::NamingContext::_narrow(ns_obj);
                CosNaming::Name name;
                name.length(1);
                name[0].id = CORBA::string_dup("TestServer");
                name[0].kind = CORBA::string_dup("");
                nc->rebind(name, service->_this());
                cout << argv[0] << ": server 'TestServer' bound" << endl;
            }
        } catch (CosNaming::NamingContext::NotFound &) {
            cerr << "not found" << endl;
        } catch (CosNaming::NamingContext::InvalidName &) {
            cerr << "invalid name" << endl;
        } catch (CosNaming::NamingContext::CannotProceed &) {
            cerr << "cannot proceed" << endl;
        }

        // run
        manager->activate();
        orb->run();

        // clean up
        delete service;

        // quit
        orb->destroy();
    } catch (CORBA::UNKNOWN) {
        cerr << "unknown exception" << endl;
    } catch (CORBA::SystemException &) {
        cerr << "system exception" << endl;
    }
}

Build

Sure, there is a Makefile but this subsection shows what steps are necessary to build the client and server programs manually.

$ omniidl -bcxx hello.idl
$ g++ -c client.cpp -I$OMNIORB_HOME/include -I$OMNIORB_HOME/include/omniORB4
$ g++ -c server.cpp -I$OMNIORB_HOME/include -I$OMNIORB_HOME/include/omniORB4
$ g++ -c Hello_impl.cpp -I$OMNIORB_HOME/include -I$OMNIORB_HOME/include/omniORB4
$ g++ -c helloSK.cc -I$OMNIORB_HOME/include -I$OMNIORB_HOME/include/omniORB4
$ g++ -o client client.o helloSK.o -L$OMNIORB_HOME/lib -lomnithread -lomniORB4
g++ -o server server.o helloSK.o Hello_impl.o -L$OMNIORB_HOME/lib -lomnithread -lomniORB4

MICO 2.3.7

Overview

There are four files, implementing client and server:

File Description
client.cpp The client program
server.cpp The server program
Hello_impl.h The class delcaration of the implementation for the distributed object. Exactly the same as for omniORB
Hello_impl.cpp The implementation of the class for the distributed object

Those seem to be the same files as for omniORB. Although the names and the logic are the same there are minor differences.

Dependencies

The following picture shows the dependencies of files, generated and existing.

Files

This subsection shows the source codes of all files needed to build the client and server programs.

Hello_impl.h:

#ifndef __HELLO_IMPL_H__
#define __HELLO_IMPL_H__

#include "hello.hh"

class Hello_impl : public POA_Hello
{
    public:
        virtual char * say_hello(const char * client);
};

#endif

Hello_impl.cpp:

#include "Hello_impl.h"
#include <iostream>

using namespace std;

char * Hello_impl::say_hello(const char * client)
{
    cout << "MICO C++ server: " << client << endl;
    char * server = CORBA::string_alloc(32);
    strncpy(server, "MICO C++ server", 32);
    return server;
}

client.cpp:

#include "hello.hh"
#include <iostream>
#include <CORBA.h>
#include <mico/CosNaming.h>

using namespace std;

int main(int argc, char ** argv)
{
    try {
        // init ORB
        CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);

        // resolve service
        Hello_ptr hello = 0;
        try {
            CORBA::Object_var ns_obj = orb->resolve_initial_references("NameService");
            if (!CORBA::is_nil(ns_obj)) {
                CosNaming::NamingContext_ptr nc = CosNaming::NamingContext::_narrow(ns_obj);
                CosNaming::Name name;
                name.length(1);
                name[0].id = CORBA::string_dup("TestServer");
                name[0].kind = CORBA::string_dup("");
                CORBA::Object_ptr obj = nc->resolve(name);
                if (!CORBA::is_nil(obj)) {
                    hello = Hello::_narrow(obj);
                }
            }
        } catch (CosNaming::NamingContext::NotFound &) {
            cerr << "not found" << endl;
        } catch (CosNaming::NamingContext::InvalidName &) {
            cerr << "invalid name" << endl;
        } catch (CosNaming::NamingContext::CannotProceed &) {
            cerr << "cannot proceed" << endl;
        }

        if (!CORBA::is_nil(hello)) {
            char * server = hello->say_hello("MICO C++ client");
            cout << "answer from: " << server << endl;
            CORBA::string_free(server);
        }

        // destroy ORB
        orb->destroy();
    } catch (CORBA::UNKNOWN) {}
}

server.cpp:

#include "Hello_impl.h"
#include <iostream>
#include <CORBA.h>
#include <mico/CosNaming.h>

using namespace std;

int main(int argc, char ** argv)
{
    try {
        // init ORB
        CORBA::ORB_ptr orb = CORBA::ORB_init(argc, argv);

        // init POA
        CORBA::Object_var poa_obj = orb->resolve_initial_references("RootPOA");
        PortableServer::POA_var poa = PortableServer::POA::_narrow(poa_obj);
        PortableServer::POAManager_var manager = poa->the_POAManager();

        // create service
        Hello_impl * service = new Hello_impl;

        // register within the naming service
        try {
            CORBA::Object_var ns_obj = orb->resolve_initial_references("NameService");
            if (!CORBA::is_nil(ns_obj)) {
                CosNaming::NamingContext_ptr nc = CosNaming::NamingContext::_narrow(ns_obj);
                CosNaming::Name name;
                name.length(1);
                name[0].id = CORBA::string_dup("TestServer");
                name[0].kind = CORBA::string_dup("");
                nc->rebind(name, service->_this());
                cout << argv[0] << ": server 'TestServer' bound" << endl;
            }
        } catch (CosNaming::NamingContext::NotFound &) {
            cerr << "not found" << endl;
        } catch (CosNaming::NamingContext::InvalidName &) {
            cerr << "invalid name" << endl;
        } catch (CosNaming::NamingContext::CannotProceed &) {
            cerr << "cannot proceed" << endl;
        }

        // run
        manager->activate();
        orb->run();

        // clean up
        delete service;

        // quit
        orb->destroy();
    } catch (CORBA::UNKNOWN) {
        cerr << "unknown exception" << endl;
    } catch (CORBA::SystemException &) {
        cerr << "system exception" << endl;
    }
}

Build

There is a Makefile, but this subsection shows how to build the client and server programs manually.

$ idl --poa --no-boa --hh-suffix hh hello.idl
$ g++ -c client.cpp -I$MICO_HOME/include
$ g++ -c server.cpp -I$MICO_HOME/include
$ g++ -c Hello_impl.cpp -I$MICO_HOME/include
$ g++ -c hello.cc -I$MICO_HOME/include
$ g++ -o client client.o hello.o -L$MICO_HOME/lib -lmico2.3.7 -lmicocoss2.3.7
$ g++ -o server server.o hello.o Hello_impl.o -L$MICO_HOME/lib -lmico2.3.7 -lmicocoss2.3.7

Java SDK 1.4.2

Overview

There are three files, implementing client and server. This version needs one less because Java combines header (.h) and definition (.cpp/.cc) files into one (.java).

File Description
client.java The client program
server.java The server program
Hello_impl.java The class of the implementation for the distributed object.

Dependencies

The following picture shows the dependencies of files, generated and existing.

Files

This subsection shows the source codes of all files needed to build the client and server programs.

Hello_impl.java:

import java.util.*;

public class Hello_impl extends HelloPOA {
    public java.lang.String say_hello(java.lang.String client) {
        System.out.println("Java Server: " + client);
        return "Java Server";
    }
}

client.java:

import java.util.*;

public class client {
    public static void main(String [] args) {
        org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
        if (orb == null) System.exit(-1);
        try {
            // obtain service from naming server
            org.omg.CORBA.Object ns_obj = orb.resolve_initial_references("NameService");
            org.omg.CosNaming.NamingContext nc
                = org.omg.CosNaming.NamingContextHelper.narrow(ns_obj);
            org.omg.CosNaming.NameComponent [] path
                = { new org.omg.CosNaming.NameComponent("TestServer", "") };
            org.omg.CORBA.Object obj = nc.resolve(path);
            Hello hello = HelloHelper.narrow(obj);

            // use service
            String server = hello.say_hello("Java client");
            System.out.println("answer from: " + server);

            // destroy
            orb.destroy();
        } catch (org.omg.CORBA.ORBPackage.InvalidName exception) {
            exception.printStackTrace(System.out);
        } catch (org.omg.CosNaming.NamingContextPackage.NotFound exception) {
            exception.printStackTrace(System.out);
        } catch (org.omg.CosNaming.NamingContextPackage.CannotProceed exception) {
            exception.printStackTrace(System.out);
        } catch (org.omg.CosNaming.NamingContextPackage.InvalidName exception) {
            exception.printStackTrace(System.out);
        } catch (org.omg.CORBA.COMM_FAILURE exception) {
            exception.printStackTrace(System.out);
        } catch (Exception exception) {
            exception.printStackTrace(System.out);
        }
    }
}

server.java:

import java.util.*;

public class server {
    public static void main(String [] args) {
        org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
        if (orb == null) System.exit(-1);
        try {
            // init POA
            org.omg.CORBA.Object poa_obj = orb.resolve_initial_references("RootPOA");
            org.omg.PortableServer.POA poa
                = org.omg.PortableServer.POAHelper.narrow(poa_obj);
            org.omg.PortableServer.POAManager manager = poa.the_POAManager();

            // create service
            org.omg.CORBA.Object service = poa.servant_to_reference(new Hello_impl());

            // register within naming service
            try {
                org.omg.CORBA.Object ns_obj = orb.resolve_initial_references("NameService");
                if (ns_obj != null) {
                    org.omg.CosNaming.NamingContext nc
                        = org.omg.CosNaming.NamingContextHelper.narrow(ns_obj);
                    org.omg.CosNaming.NameComponent [] name
                        = new org.omg.CosNaming.NameComponent[1];
                    name[0] = new org.omg.CosNaming.NameComponent("TestServer", "");
                    nc.rebind(name, service);
                    System.out.println("Server 'TestServer' bound");
                }
            } catch (Exception exception) {
                exception.printStackTrace(System.out);
            }

            // run
            manager.activate();
            orb.run();

            // destroy
            orb.destroy();
        } catch (org.omg.CORBA.UNKNOWN exception) {
            exception.printStackTrace(System.out);
        } catch (org.omg.CORBA.SystemException exception) {
            exception.printStackTrace(System.out);
        } catch (Exception exception) {
            exception.printStackTrace(System.out);
        }
    }
}

Build

There is a Makefile, but this subsection shows how to build the client and server programs manually.

$ $JAVA_HOME/bin/idlj -fall hello.idl
$ javac *.java

Running the Program

Overview

This chapter shows how to run the programs in different configurations. However, there are many combinations but not all are shown. We have three naming servers (one from each ORB), three servers and three clients.

The following examples are shown below:

The best choice is to run Naming Server, Server and Client in separate shells.

If you like to build all necessary party using the provided Makefile, you will have to define three environment variables:

Instead of defining those variables you may specify them at the command prompt while starting the make tool:

$ make JAVA_HOME=... OMNIORB_HOME=... MICO_HOME=...

Probably you will need to specify additional search paths, one to the binaries of the two ORBs (omniORB, MICO) and one to the Java SDK (which contains its own ORB).

Example 1

Naming Server: omniORB (Default port: 2809)

$ omniNames -logdir /tmp -start

Server: omniORB

$ ./server -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: omniORB

$ ./client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: MICO

$ ./client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: Java

$ java client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Example 2

Naming Server: omniORB (Default port: 2809)

$ omniNames -logdir /tmp -start

Server: MICO

$ ./server -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: omniORB

$ ./client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: MICO

$ ./client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: Java

$ java client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Example 3

Naming Server: MICO

$ nsd -ORBIIOPAddr inet:localhost:2809

Server: omniORB

$ ./server -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: omniORB

$ ./client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Client: MICO

$ ./client -ORBInitRef NameService=corbaloc::localhost:2809/NameService

Conclusion

The Naming Service depends on the implementation.

The server side is pretty much always the same.

The client side is pretty much always the same.

All ORBs have their own extensions and additional features. This is shown mostly in their handling of the services. Server and Client are, as long as they are programmed according to the CORBA standard, independent. As shown in the examples above, they are interchangeable.

Download

All files provided by this page are free to modify, distribute or to use them in any kind you like. Use them on your own risk.