CORBA: JAVA & Visual Studio 2010 C++ CORBA communication with OmniOrb and Java ORBD


CORBA: JAVA & Visual Studio 2010 C++ CORBA communication with OmniOrb and Java ORBD

Downloading OmniOrb library files:

Omniorb can be downloaded and built from sources, as we will be using Windows, precompiled versions should be more comfortable to use. We are interested in the library files, so they can be downloaded from:

For windows 64 bits systems:

Configuration of OmniOrb in the registry will not be necessary and will save you lots of time. 

In this tutorial we will be using java's name service, OmniOrb's name service could be used as well, but it resulted much harder to configure. OmniOrb will be used to communicate with a client and servant both written in C++ using OmniOrb's libraries. Once the tutorial is done, communicating with a JAVA implementation should result simple.

Set your path to include:
C:\omniORB-4.1.6\bin\x86_win32;C:\omniORB-4.1.6\lib\x86_win32;

Our IDL:

For the shake of simplicity we will be using a very simple IDL:
interface Dummy
 {
   string SayHello(in string name);
 };

The chosen name for the file was dummy101.idl, and put into:
C:\VSProjects\Corba101\idl

To compile the IDL and generate the CORBA proxy and skeleton from the command line:
omniidl -bcxx dummy101.idl

Add the path to omniidl variable first. After compilation dummy101.hh and dummy101SK.cc should appear under that directory, they are the headers and skeleton code that allow remote access to the Dummy's implementation that will be coded on the server side.



How to Start JAVA's ORBD:

- Start JAVA's ORBD: we will be using the following command line arguments:

Command line:
start orbd -port 1050 -ORBInitialPort 9777

Alternatively Omniorb could be used aswell:

omniNames -start

Setting up Visual Studio Projects to use Omniorb Libraries:

Set Debug Command Line Parameters:
-ORBInitRef NameService=corbaloc::127.0.0.1:9777/NameService -ORBendPoint giop:tcp:127.0.0.1:1050 


In this example we will be using an IDL located in the folder C:\VSProjects\Corba101\idl, this folder has to be in the project's additional Include Directories so that the compiler locates the .h during compile time. Also OmniOrb's header files have to be included, in my installation they were at: C:\omniORB-4.1.6\include

Preprocessor Definitions: 
WIN32
_DEBUG
_CONSOLE
__WIN32__
__x86__
_WIN32_WINNT=0x0400
__NT__


Linker/General:
Add Omniorb's win32 libraries folder:
Linker/Input:
Add Ominorb's DEBUG libraries (use rt versions if you prefer building a Release).
ws2_32.lib
mswsock.lib
advapi32.lib
omniORB4_rtd.lib
omniDynamic4_rtd.lib
omnithread_rtd.lib




Add the header and skeleton that were generated from the idl:
Add dummy101.hh to Header Files section and dummy101SK.cc to Source Files.

Inform that the skeleton code will not be using precompiled headers. Left click onto it (the skeleton .cpp file) and select properties:

Compile... no problems should arise... Unless you are in a 64 bits system.
fatal error LNK1112: module machine type 'x64' conflicts with target machine type 'X86' if so, open properties and click on the Configuration Manager... button on the top right.


Build... rebulid solution: 
========== Rebuild All: 1 succeeded, 0 failed

[Note] Check if the arguments in Debug Command Line Parameters are still in place, if not, fill them as stated before.

DLL Not Found during debug/runtime:
Add the path to the DLLs (for example: PATH=%PATH%;C:\omniorb\omniORB-4.2.0\bin\x86_win32) in Configuration Properties/Debugging/Environment:



Coding the Servant:

Lets have a look at the autogenerated code we got from 
omniidl -bcxx dummy101.idl
The magic word here is POA: Point of Access

class POA_Dummy :
  public virtual _impl_Dummy,
  public virtual ::PortableServer::ServantBase
{
public:
  virtual ~POA_Dummy();

  inline ::Dummy_ptr _this() {
    return (::Dummy_ptr) _do_this(::Dummy::_PD_repoId);
  }
};

this class extends _impl_Dummy... lets have a look at it too:
class _impl_Dummy :
  public virtual omniServant
{
public:
  virtual ~_impl_Dummy();

  virtual char* SayHello(const char* name) = 0;
  
public:  // Really protected, workaround for xlC
  virtual _CORBA_Boolean _dispatch(omniCallHandle&);

private:
  virtual void* _ptrToInterface(const char*);
  virtual const char* _mostDerivedRepoId();
  
};

This class is abstract, we want an instance of a POA_Dummy, so when extending it we will have to give an implementation of the SayHello method, the remote method defined in the IDL. Let's provide the desired implementation in the writen-by-us Dummy_i.h:

#pragma once
#include "dummy101.hh"
#include <sstream>
#include <string>

class Dummy_i : public POA_Dummy,
        public PortableServer::RefCountServantBase
{
public:
Dummy_i(){};
virtual ~Dummy_i(){};
virtual char *SayHello(const char* name)
{
std::stringstream ss;
ss << "Hello, "<<name<<".";
std::string s = ss.str();
char *result=(char*)s.c_str();
return CORBA::string_dup(result);
};
};

OK, I know inline code looks bad, but it helps the example to be smaller, so it is welcome here.

All we need here is code to:
1) Locate the Name Service
2) Bind the Server to the Name Service
3) Run the server so it accepts client requests.

The code I insert here does all that, it leaves a lot of space for oop reusability, so if you decide to refactor it, please post it, I will be glad to add it to the article.

// Corba101.cpp : Defines the entry point for the console application.
//

#include "StdAfx.h"
#include <stdlib.h>
#include <iostream>
#include <string>
#include <assert.h>
#include <signal.h>

                                                                             
#include "Dummy_i.h"

using namespace std;
                                                                                
int main(int argc, char** argv)
{
  // --------------------------------------------------------------------------
  // Start CORBA server:
  // --------------------------------------------------------------------------
  cout<<"Welcome, initialising CORBA"<<endl;                                                                             
  try {
    //------------------------------------------------------------------------
    // 1) Initialize ORB
    // 2) Get reference to root POA
    // 3) Bind to name service
    // 4) Initialize servant object
    //------------------------------------------------------------------------
                                                                                
    //------------------------------------------------------------------------
    // Initialize CORBA ORB - "orb"
    //------------------------------------------------------------------------
cout<<"Corab Servant Initialising Arguments:"<<endl;
    for (int i=0;i<argc;i++)
{
cout<<argv[i]<<endl;
}
cout<<"--------------------------------------"<<endl;
CORBA::ORB_var orb = CORBA::ORB_init(argc, argv); 
    cout<<"Corba initialised"<<endl;                                                                            
    //------------------------------------------------------------------------
    // Servant must register with POA in order to be made available for client
    // Get reference to the RootPOA.
    //------------------------------------------------------------------------
    CORBA::Object_var obj = orb->resolve_initial_references("RootPOA");
    PortableServer::POA_var _poa = PortableServer::POA::_narrow(obj.in());
    cout<<"Servant Registered"<<endl;                                                                             
    //------------------------------------------------------------------------
    // Operations defined in object interface invoked via an object reference.
    // Instance of CRequestSocketStream_i servant is initialized.
    //------------------------------------------------------------------------
    Dummy_i* myDummyService = new Dummy_i;
    cout<<"Interface implementation created"<<endl;                                                                             
    //------------------------------------------------------------------------
    // ObjectId_var class defined in poa.h
    // typedef String_var ObjectId_var; CORBA_ORB.h
    // ???????
                                                                                
    //------------------------------------------------------------------------
    // Servant object activated in RootPOA.
    // (Object id used for various POA operations.)
    //------------------------------------------------------------------------
    PortableServer::ObjectId_var myDummyService_oid
                                = _poa->activate_object(myDummyService);
    cout<<"POA Activated"<<endl;                                                                             
    //------------------------------------------------------------------------
    // Obtain object reference from servant and register in naming service(??)
    //------------------------------------------------------------------------
    CORBA::Object_var SA_obj = myDummyService->_this();
    cout<<"Reference to servant obtained"<<endl;                                                                              
    //------------------------------------------------------------------------
    // Obtain a reference to the object, and print it out as string IOR.
    //------------------------------------------------------------------------
    CORBA::String_var sior(orb->object_to_string(SA_obj.in()));
    cerr << "'" << (char*)sior << "'" << endl;
    cout<<"IOR Obtained"<<endl;                                                                              
    //========================================================================
    // Bind (rebind) object (orb) to name (SA_obj)
    //========================================================================
                                                                                
    //------------------------------------------------------------------------
    // Bind object to name service as defined by directive InitRef
    // and identifier "NameService" in config file omniORB.cfg.
    //------------------------------------------------------------------------
cout<<"--------->Binding to name service"<<endl;
    CORBA::Object_var initServ=orb->resolve_initial_references("NameService");
    if (!CORBA::is_nil(initServ)) {
    cout<<"NameService found"<<endl;    
} else
{
cout<<"NameService not found"<<endl;
}
assert(!CORBA::is_nil(initServ));
    //------------------------------------------------------------------------
    // narrow this to the naming context
    //------------------------------------------------------------------------
cout<<"Narrowing Name Context"<<endl; 
    CosNaming::NamingContext_var rootContext;
rootContext = CosNaming::NamingContext::_narrow(initServ);
cout<<"Narrow"<<endl;
    assert(!CORBA::is_nil(rootContext));
    cout<<"Narrowing was suscessfull"<<endl;                                                                              
    //------------------------------------------------------------------------
    // Bind to CORBA name service. Same name to be requested by client.
    //------------------------------------------------------------------------
cout<<"Binding"<<endl; 
    CosNaming::Name name;
    name.length(1);
    name[0].id=CORBA::string_dup("Dummy1");
    rootContext->rebind (name,SA_obj.in());
    cout<<"Bound as"<<"Dummy1"<<endl;                                                                              
    //========================================================================
                                                                                
    myDummyService->_remove_ref();
                                                                                
    //------------------------------------------------------------------------
    // Activate the POA manager
    //------------------------------------------------------------------------
    PortableServer::POAManager_var pmgr = _poa->the_POAManager();
    pmgr->activate();
    cout<<"POA Activated"<<endl;                                                                            
    //------------------------------------------------------------------------
    // Accept requests from clients
    //------------------------------------------------------------------------
    cout<<"Accepting requests from clients"<<endl;
orb->run();
                                                                                
    //------------------------------------------------------------------------
    // If orb leaves event handling loop.
    // - currently configured never to time out (??)
    //------------------------------------------------------------------------
    orb->destroy();
    cout<<"Servant destroyed"<<endl;                                                                           
    free(name[0].id); // str_dup does a malloc internally
getchar();
  }
                                                                                
  catch(CORBA::SystemException&) {
    cerr << "Caught CORBA::SystemException." << endl;
getchar();
  }
  catch(CORBA::Exception&) {
    cerr << "Caught CORBA::Exception." << endl;
getchar();
  }
  catch(omniORB::fatalException& fe) {
    cerr << "Caught omniORB::fatalException:" << endl;
    cerr << "  file: " << fe.file() << endl;
    cerr << "  line: " << fe.line() << endl;
    cerr << "  mesg: " << fe.errmsg() << endl;
getchar();
  }
  catch(...) {
    cerr << "Caught unknown exception." << endl;
getchar();
  }
                                                                                
  return 0;
}




Run it, remember to previously launch the orbd, in my case I use Java's orbd:
start orbd -port 1050 -ORBInitialPort 9777


Coding the Client:

This class captures the results of locating the servant in the Name Service and then instancing a remote interface to it in the class field: m_remoteInterface. After the binding process it will be possible to invoke the remote method by using example2.m_remoteInterface->SayHello("your name here");

Again, the code would benefit from a refactoring, almost everything is done at the constructor... ugly. So if you bring the code to a better state , please post it.

CorbaClientForDummy.h:
#include <iostream>
#include <fstream>
#include "dummy101.hh"
                                                                                
using namespace std;
                                                                                
class CorbaClientForDummy {
public:
   CorbaClientForDummy(int argc,char** argv);
   ~CorbaClientForDummy();                                                                               
                                                                                
   // CORBA ORB
   CORBA::ORB_var             m_orb;
   CORBA::Object_var      m_nameService;
   CosNaming::Name m_corbaCosName;
   CosNaming::NamingContext_var m_nameContext;

   CORBA::Object_var          m_corbaref;     // Resolved id to object reference                                                                               
   // Resolved and narrowed CORBA object for proxy calls
   DummyRef      m_remoteInterface;
};
                                                                                
class DS_ServerConnectionException{
public:
   DS_ServerConnectionException() { cerr << "CORBA COMM_FAILURE" << endl; };
};
                                                                                
class DS_SystemException{
public:
   DS_SystemException() { cerr << "CORBA Exception" << endl; };
};
                                                                                
class DS_FatalException{
public:
   DS_FatalException() { cerr << "CORBA Fatal Exception" << endl; };
};
                                                                                
class DS_Exception{
public:
   DS_Exception() { cerr << "Exception" << endl; };
};  

CorbaClientForDummy.cpp:
#include "StdAfx.h"
#include <stdlib.h>
#include <iostream>
#include <string>
#include <assert.h>
#include <signal.h>
#include "dummy101.hh"
#include "CorbaClientForDummy.h"


                                                                                
CorbaClientForDummy::CorbaClientForDummy(int argc,char**argv)
{
  try {
cout<<"Corab Client Initialising Arguments:"<<endl;
    for (int i=0;i<argc;i++)
{
cout<<argv[i]<<endl;
}
cout<<"--------------------------------------"<<endl;
    //------------------------------------------------------------------------
    // Initialize ORB object.
    //------------------------------------------------------------------------
    //int    argc=0;       // Dummy variables to support following call.
    //char** argv=0;
    //CORBA::ORB_var orb = CORBA::ORB_init(argc, argv);
this->m_orb= CORBA::ORB_init(argc, argv);
    cout<<"orb inited"<<endl;                                                     
    //------------------------------------------------------------------------
    // Bind ORB object to name service object.
    // (Reference to Name service root context.)
    //------------------------------------------------------------------------
cout<<"getting name service"<<endl; 
    //CORBA::Object_var obj = orb->resolve_initial_references("NameService");
this->m_nameService = this->m_orb->resolve_initial_references("NameService");
assert (!CORBA::is_nil(this->m_nameService.in()));
                                                                                
    //------------------------------------------------------------------------
    // Narrow this to the naming context (Narrowed reference to root context.)
    //------------------------------------------------------------------------
cout<<"Narrowing"<<endl; 
    //CosNaming::NamingContext_var nc =
    //                    CosNaming::NamingContext::_narrow(obj.in());
this->m_nameContext =  CosNaming::NamingContext::_narrow(this->m_nameService.in());
assert (!CORBA::is_nil(this->m_nameContext));
    cout<<"narrow done"<<endl; 
                                                                                
    //------------------------------------------------------------------------
    // The "name text" put forth by CORBA server in name service.
    // This same name ("DataServiceName1") is used by the CORBA server when
    // binding to the name server (CosNaming::Name).
    //------------------------------------------------------------------------
    cout<<"binding to Dummy1"<<endl; 
    
/*CosNaming::Name _corbaCosName;
    _corbaCosName.length(1);
    _corbaCosName[0].id=CORBA::string_dup("Dummy1");*/
this->m_corbaCosName.length(1);
this->m_corbaCosName[0].id=CORBA::string_dup("Dummy1");
                                                                                
    //------------------------------------------------------------------------
    // Resolve "name text" identifier to an object reference.
    //------------------------------------------------------------------------
cout<<"Resolving Dummy1"<<endl; 
    //CORBA::Object_var obj1 = nc->resolve(_corbaCosName);
this->m_corbaref= this->m_nameContext->resolve(this->m_corbaCosName);
    assert(!CORBA::is_nil(this->m_corbaref));
    cout<<"narrowing Dummy1"<<endl; 
                                                                                
    //m_Data = Dummy::_narrow(obj1.in());
this->m_remoteInterface= Dummy::_narrow(this->m_corbaref.in());

    if (CORBA::is_nil(this->m_remoteInterface))
    {
       cerr << "IOR is not an SA object reference." << endl;
    }

cout<<"Dummy1 prepared."<<endl;
  }
  catch(CORBA::COMM_FAILURE& ex) {
    cerr << "Caught system exception COMM_FAILURE -- unable to contact the "
         << "object." << endl;
    throw DS_ServerConnectionException();
    return;
  }
  catch(CORBA::SystemException& ) {
    cerr << "Caught a CORBA::SystemException." << endl;
    throw DS_SystemException();
    return;
  }
  catch(CORBA::Exception& ex) {
    cerr << "Caught CORBA::Exception." << endl;
    throw DS_Exception();
    return;
  }  catch(omniORB::fatalException& fe) {
    cerr << "Caught omniORB::fatalException:" << endl;
    cerr << "  file: " << fe.file() << endl;
    cerr << "  line: " << fe.line() << endl;
    cerr << "  mesg: " << fe.errmsg() << endl;
    throw DS_FatalException();
    return;
  }
  catch(...) {
    cerr << "Caught unknown exception." << endl;
    throw DS_Exception();
    return;
  }
  return;
}
                                                                                
CorbaClientForDummy::~CorbaClientForDummy()
{
   
}
                                                                                
int main(int argc, char** argv)
{
  cout<<"Welcome, initialising CORBA CLIENT"<<endl;  
  CorbaClientForDummy cdummy(argc,argv);

  cout<<cdummy.m_remoteInterface->SayHello("Ramon Talavera")<<endl;
  getchar();
 return 0;
}

Connecting to a C++ servant from JAVA (Netbeans)

This is the easy part, compile the idl with jidl Dummy101.idl and copy-paste the generated sources to your JAVA project.



public class SimpleCorbaClient implements DummyOperations {

    private Dummy remote_interface;

    /**
     * @param remote_interface the remote_interface to set
     */
    public void setRemote_interface(Dummy remote_interface) {
        this.remote_interface = remote_interface;
    }

    @Override
    public String SayHello(String name) {
        return remote_interface.SayHello(name);
    }
}

And now the main:
import org.omg.CORBA.ORB;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;

public class SimpleCorbaClientMain {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        
        SimpleCorbaClient client=new SimpleCorbaClient();
        
        try {
            
            ORB orb = ORB.init(args, null);

            
            org.omg.CORBA.Object objRef =
                    orb.resolve_initial_references("NameService");
           
            
            NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

            
            String name = "Dummy1";
            client.setRemote_interface(DummyHelper.narrow(ncRef.resolve_str(name)));

            System.out.println("Obtained a handle for remote interface. ");
            System.out.println(client.SayHello("Ramon Talavera"));
           

        } catch (Exception e) {
            System.out.println("ERROR : " + e);
            e.printStackTrace(System.out);
        }
    }
}

Launch the program using the following parameters: (Netbeans: Project Properties/Run/Arguments)
-ORBInitialPort 9777 -ORBInitialHost localhost



Run it from the project, not by launching the java file directly as in that case arguments are overriden.

Results:

Obtained a handle for remote interface. 
Hello, Ramon Talavera.






The code examples can be downloaded as 2 visual studio 2010 projects and a Netbeans JAVA project:

Download: Ramon Talavera's Visual Studio 2010 C++ and CORBA Tutorial with OmniOrb Libraries Example Projects
Download: Java Corba Client to C++ Servant Netbeans Project
Email me: ramon.talavera@gmail.com

Enjoy yor coding. CORBA is cool
See you next time!

Ramón Talavera

Comments

Popular posts from this blog

Qt Signals and Slots, Connecting and Disconnecting

Vaadin 7: Detect Enter Key in a TextField

JAVA JPA WITH HIBERNATE AND H2 Tutorial