Using Java to talk CORBA

Back To Mike's WebToys Page!

Steps in creating Java-CORBA system

  1. Define the interfaces in CORBA IDL
  2. Compile the CORBA IDL into Java using jidl
  3. Write/compile the server, then run it
  4. Write/compile the client, then run it

Define the interface in CORBA IDL

The CORBA Spec (Ver 2.2) is available from the Object Management Group (OMG), which is probably the best place to get the specification. For tutorial info on IDL, check out my COM tutorial. For this example, assume that we're working with JRand.idl, which looks like:

module JRand
{
interface JRand { long GetRand( in short nSeed ); };
};
put this in a directory named "JRand".


Compiling the IDL into Java

Invoke jidl on JRand.idl like so:
jidl --package JRand JRand.idl
This will create a subdirectory named JRand, and drop into it JRand.java, JRandHelper.java, JRandHolder.java, StubForJRand.java, and _JRandImplBase.java. The uses of the various classes are as follows:
FilePurpose
JRand.java.
JRandHelper.java.
JRandHolder.java.
StubForJRand.java.
_JRandImplBase.javaServer side object that actually provides JRand functionality derrives from this.


Write/compile the server

For this, you'll need at least two java classes: one for the application that starts up, registers it's object, and then waits for clients to connect, and another object to actually provide the JRand services. I suppose they could be the same, but it would be kinda weird. Plus, that one application could register multiple CORBA objects, so there's no real point.

The JRand Object

package JRand;

import org.omg.CORBA.*;

public class JRandObj extends _JRandImplBase
{
public int GetRand( short nSeed ) { return (int)(nSeed + 1); }
};
In this example, it's trivially simple. The key points to note are that the object inherits from _JRandImplBase, and that it doesn't need to (itself) do anything except what you wrote IDL to do. Very nice, indeed

The application has to do a bit more, which is shown/explained below. Note that the applicaton doesn't derive from anything. Also note that the call to impl_is_ready() is a blocking call.

Server Application

The server program can be divided into a number of subparts: roughly,

  1. Initialization (ORB and BOA)
  2. Served Object creation (in this case, the JRand server)
  3. IOR (Interoperable Object Reference) publishing
  4. CORBA event loop
package JRand;
import org.omg.CORBA.*;

public class Server
{
    public static void main( String args[] )
    {
    int i;
    //initialize the OR, BOA
    //these two steps comprise the Initialization phase.
    //It's important to note that you need to either import org.omg.CORBA.*
    //or specify that on the net couple of lines

    //at least for ORBacus, args will have to include -ORBnaming IOR
    //for the naming service to work.  A good way to pass this is through
    //the command line
    ORB orb = ORB.init( args, new java.util.Properties() );
    BOA boa = orb.BOA_init( args, new java.util.Properties() );

    //instantiate the object
    JRand jr = new JRandObj();
    
    // publish the object: this could be done
    //using the naming service, or through a known file (same machine only)
    //Note that IORs (Interoperable Object Reference) are part of the 
    //CORBA standard, and allows you to move a reference to an object
    //across ORBs.  The orb has an object_to_string(org.omg.CORBA.Object o)
    //method which 'stringifies' an object into one of these, and
    //a string_to_object(String) method which can convert it back.
    
    // blah blah blah

    //event loop
    boa.impl_is_ready(null);
    }
};


    // Publishing an object: CORBA Naming service
    //first, we try and get a reference to a naming service object 
    //by using resolve_initial_references, followed by an
    //an attempt to narrow the object to the appropriate class
    //(roughly the equivalent of COM's QueryInterface)
    org.omg.CosNaming.NamingContext nc;
    org.omg.CORBA.Object obj = null;
    try 
    {
	obj = orb.resolve_initial_references( "NameService" );
    }
    catch(  org.omg.CORBA.ORBPackage.InvalidName ex )
    {
        System.out.println( "nc not a NamingContext!\n"  + ex.getMessage());
	System.exit(1);
    }
    nc = org.omg.CosNaming.NamingContextHelper.narrow( obj );			

    //next we need to create an array of NameComponents,
    //where each NameComponent (except the last) is the name of a NameContext,
    //which is analagous to a directories.  The nth member of the array
    //is the actual name under which to publish the object.
    //The kind field is similar to the 
    //three letter extension for a file in DOS, the id to the filename.
    
    org.omg.CosNaming.NameComponent rgNc[] = new org.omg.CosNaming.NameComponent[1];
    rgNc[0] = new  org.omg.CosNaming.NameComponent();
    rgNc[0].id = "JRand";
    rgNc[0].kind = "";

    try 
    {   //this call actually publishes the object
        //in the naming context/name that we loaded above.
	//Then we do error-handling for a while
        nc.bind( rgNc, jr );
    }
    catch( org.omg.CosNaming.NamingContextPackage.CannotProceed ex )
    {
	System.out.println( ex.getMessage() );
    }
    catch( org.omg.CosNaming.NamingContextPackage.AlreadyBound ex )
    {
	System.out.println( ex.getMessage() );
    }
    catch(org.omg.CosNaming.NamingContextPackage.InvalidName  ex)
    {
	System.out.println( ex.getMessage() );
    }
    catch(org.omg.CosNaming.NamingContextPackage.NotFound ex)
    {
	System.out.println( ex.getMessage() );
    }
    //End of publishing through the naming service

Once you save the above into a file named "Server.java", you should be able to compile it, then run it like so:
java Server
If it doesn't run, make sure that you've got everything set up for Java packages correctly. Also make sure to pass the IOR on the command line if you're using the naming service


Writing the Client

Client programming in CORBA is somewhat more simple than server programming; since we're only using object provided by the server, we can create only one class. The essential phases of the client are:

import org.omg.CORBA.*;
import java.io.*;
import JRand.*;


public class  Client
{
    public static void main( String args[] )
    {

    org.omg.CORBA.Object obj = null;

    //Init orb, no boa needed
    ORB orb = ORB.init( args, new java.util.Properties() );
    
    //At this point, we somehow
    // obtain a reference to the object(s) we wish to use.  For
    // example, one could use the naming service
    // to obtain a previously published IOR
    // JRand jr = new JRand(), essentially

    //at this point, we
    // have an instance of the object that we can use, and so we
    // do actual work.
    short s = 10;
    int r = jr.GetRand( s );
    System.out.println( "Got " + r );
    }
};

    //One way to obtain
    //an IOR is to use the naming service to look for a
    //previously published object 
    org.omg.CosNaming.NamingContext nc;
    try
    {
        obj = orb.resolve_initial_references( "NameService" );
    }
    catch ( Exception ex )
    {
	System.out.println( ex.getMessage() );
    }

    nc = org.omg.CosNaming.NamingContextHelper.narrow( obj );
    if( null == nc )
    {
	System.out.println( "nc not a naming ctx!" );
    }
       
    //NameComponent array is filled in the
    //same as when the object was published
    org.omg.CosNaming.NameComponent rgNc[] = new org.omg.CosNaming.NameComponent[1];
    rgNc[0] = new org.omg.CosNaming.NameComponent();
    rgNc[0].id = "JRand";
    rgNc[0].kind = "";
    try 
    {
        obj = nc.resolve( rgNc );
    }
    catch( org.omg.CosNaming.NamingContextPackage.CannotProceed ex )
    {
       System.out.println( ex.getMessage() );
    }
    catch( org.omg.CosNaming.NamingContextPackage.InvalidName ex )
    {
       System.out.println( ex.getMessage() );
    }
    catch(  org.omg.CosNaming.NamingContextPackage.NotFound ex )
    {
       System.out.println( ex.getMessage() );
    }

    //Again, we do the QueryInterface-esque
    // object refinement to get the actual JRand object
    JRand jr = JRandHelper.narrow( obj );

At this point, all you've got to do is compile and run the client (assuming that the server is already running). The only other thing to keep in mind is that you may need to add ORB-specific Java classes -- for ORBacus, this is in the file OB.jar. If you have trouble getting stuff to run because Java "Can't find class Server/Client", it might be because of how you've configured things for java packages