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:
put this in a directory named "JRand".module JRand {interface JRand { long GetRand( in short nSeed ); };};
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:
File | Purpose |
JRand.java | . |
JRandHelper.java | . |
JRandHolder.java | . |
StubForJRand.java | . |
_JRandImplBase.java | Server side object that actually provides JRand functionality derrives from this. |
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.
package JRand; import org.omg.CORBA.*; public class JRandObj extends _JRandImplBase {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, indeedpublic int GetRand( short nSeed ) { return (int)(nSeed + 1); }};
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.
The server program can be divided into a number of subparts: roughly,
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 serviceOnce you save the above into a file named "Server.java", you should be able to compile it, then run it like so:
java Server
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