Before we go about explaining this example in detail, we will have a look at the general process of developing CORBA or distributed Java applications with JacORB. We'll follow this roadmap when working through the example. This example can be found in jacorb/demo/example1 which also contains a Makefile so that the development steps do not have to be carried out manually every time. Still, you should know what is going on.
As this document gives only a short introduction to JacORB programming and does not cover all the details of CORBA IDL, we recommend that you also look at the other examples in the jacorb/demo/ directory. These are organised so as to show how the different aspects of CORBA IDL can be used with JacORB.
The steps we will generally have to take are:
This example uses a very simple server. Its interface is given in server.idl.
// File: server.idl module example1 { typedef sequence < string > strings; interface server { string writeMessage(in string a1); string writeMessages(in strings a1); strings arryfy(in string a1, in long a2); }; };
Feeding this file into the idl compiler
$ idl2j -d ../../.. server.idl
produces a corresponding java interface in server.java which can then be compiled with javac. Note that the IDL compiler will produce a directory structure corresponding to the module structure in the IDL file, so it would have produced a subdirectory jacorb with a subdirectory demo (etc.) in the current directory had we not directed it to put this directory structure to ../../.. by using the -d switch with the compiler. As a result, all generated files that would have been in the jacorb/demo/example1 subdirectory are now put into the current directory. Also note that server inherits from org.omg.CORBA.CORBject. This is an empty interface which is used for the sole purpose of telling apart interfaces that were generated by the IDL compiler from those that were not. You will never need to write this line into interfaces developed by yourself.
// Automatically generated by idl2java from interface server package example1; public interface server extends org.omg.CORBA.CORBject { java.lang.String writeMessage(java.lang.String a1); java.lang.String writeMessages(java.lang.String[] a1); void arryfy1(java.lang.String a1, int a2, jacorb.Orb.SequenceOutHolder/*out*/ s); void arryfy2(java.lang.String a1, jacorb.demo.example1.StringArray5OutHolder/*out*/ s); }
The class which implements that interface is called serverImpl:
package example1; public class serverImpl implements server { public String writeMessage( String s ) { System.out.println("Message: " + s ); return s + " written"; } public String writeMessages( String[] s ) { for( int i = 0; i < s.length; i++) System.out.println("Message: " + s[i] ); return "string array written"; } public void arryfy1(java.lang.String a1, int a2, jacorb.Orb.SequenceOutHolder s) { String result [] = new String[a2]; for( int j = 0; j < a2; j++ ) result[j] = a1; s.set_value(result); } public void arryfy2(java.lang.String a1, jacorb.demo.example1.StringArray5OutHolder s) { int sz = s.size(); String result [] = new String[sz]; for( int j = 0; j < sz; j++ ) result[j] = a1; s.set_value(new StringArray5(result)); } }
Note that this class has not a single line of ORB-related code in it (save for the use of Holder classes to mimick the IDL out parameter passing mode). JacORB supports only what is known as the TIE-mechanism for binding skeleton code to an application class, so there is no need to inherit from any ORB class. As Java supports only single implementation inheritance, you will more often than not need to inherit from application defined superclasses, so the other approach (BOAImpl in Orbix parlance) will frequently be ruled out. That's why it's not supported in JacORB)
A client of this server relies only on the operation signatures in server.idl or server.java. What the client actually uses is not an instance of serverImpl, but a stub that is automatically generated from the Java interface (which in turn was generated from the IDL interface). It is type-correct to use the stub in place of the server since the stub also implements the interface server.
In order to generate the stub (and the server side skeleton), all you have to do now is type:
$ jgen server.class
This produces the two files serverStub.java and serverSkeleton.java. Alternatively, you can do a make all to compile and generate everything. To build everything anew cou can do a make clean; make all.
To actually start a serverImpl object which can be accessed remotely you have to instantiate it in a main method of some other class and register it with what is known as the Object Adapter. Here is the class remoteServer.java which does all that is necessary to activate a serverImpl object (the server code itself remains unchanged):
package example1; public class remoteServer { public static void main( String[] args ) { try { // initialize ORB and BOA org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(); org.omg.CORBA.BOA boa = orb.BOA_init(); // let the BOA create the server CORBA object org.omg.CORBA.Object server = boa.create( new serverImpl(), "IDL:example1/server:1.0"); // tell the BOA about it // (this named_obj_is_ready()-call is a proprietary // shortcut for calling boa.obj_is_ready() and // telling the name server to bind a name to // this object. boa.named_obj_is_ready( server, "server" ); // accept incoming requests boa.impl_is_ready(); } catch (Exception e ){ e.printStackTrace(); } } }
Finally, let's have a look at the client which invokes server operations. There are actually two versions of it: one uses only the standard CORBA interface, the other makes use of a few JacORB shortcuts. Let's look at both of these in turns. First, here's the compliant one:
package example1; import org.omg.CORBA.ORB; import org.omg.CosNaming.*; public class CorbaClient { public static void main( String[] args ) { try { // initialize the ORB and find the name service ORB orb = ORB.init(); NamingContext nc = NamingContextHelper.narrow( orb.resolve_initial_references("NameService") ); // find an object (standard COSS naming interface) NameComponent[] components = new NameComponent[1]; components[0] = new NameComponent("server","service"); server s = serverHelper.narrow(nc.resolve(components)); // make the call System.out.println( s.writeMessage( "hello World" )); } catch (jacorb.Orb.SystemException se){ se.printStackTrace(); } } }
Now here's the one which uses the more convenient, but proprietary interface to the naming service to locate an object.
package example1; import jacorb.Naming.NameServer; public class Client { public static void main( String[] args ) { if( args.length != 1 ) { System.out.println("Usage: client <message>"); System.exit(1); } try { // locate the server object using the Name Service server s = serverHelper.narrow( NameServer.locate("server")); // do something with it System.out.println( s.writeMessage( args[0] )); StringArray5OutHolder sh5 = new StringArray5OutHolder(); s.arryfy2( args[0], sh5 ); String a[] = sh5.value(); System.out.println( s.writeMessages( a )); for( int i = 0; i < a.length; i++) System.out.println("From example1: " + a[i] + " " + i ); } catch (jacorb.Orb.SystemException se){ se.printStackTrace(); } // catch (Exception e){} } }
After compiling everything we're now ready to actually run the server and the client on different (virtual) machines. Make sure the name server is running before starting either the server or the client. If it isn't, type something like:
$ ns ~/public_html/NS_Ref
where ~/public_html/NS_Ref
is the name of a locally writable file
which can be read by using the URL given in both the remote client and
server code. (This is to avoid using a well-known address for the name
server, so both client and server look up the location of the nameserver
via the URL and later communicate with it directly.)
You can now launch the server:
$ java jacorb.demo.example1.remoteServer
The client can be invoked on any machine you like:
$ java jacorb.demo.example1.Client hello
Running the client using the server stub after starting the server remotely produces the following output, which is exactly the same as if the server object was local:
Message: hello hello written From example1: hello 0 From example1: hello 1 From example1: hello 2 From example1: hello 3 From example1: hello 4