Java Programming, Lecture Notes 632, Revised 08/22/99.
Students in Prof. Baldwin's Advanced Java Programming classes at ACC will be responsible for knowing and understanding all of the material in this lesson beginning with the spring semester of 1999.
This lesson was originally written on October 26, 1998. The sample program was tested using the JDK 1.2beta4 download package. The purpose of this lesson is to illustrate the use of stringified object references with JavaSoft's Java IDL and CORBA using a very simple example program.
Before embarking on this lesson, you need to study and understand all of the previous lessons on CORBA and Java. This lesson builds directly upon those lessons. Unless you understand the material in those lessons, you probably won't understand what is going on here.
Using CORBA, a client machine anywhere in the world has the capability of invoking methods on an object on a cooperating server anywhere in the world, and the programs at the two ends can be developed using different programming languages. One of those programming languages is Java.
Special code is required in the program on the client side to get a reference to the remote object. Once the client code has a reference to the remote object, the invocation of methods on the remote object is very similar to the invocation of methods on local objects.
The code on the server side must define the class and instantiate the remote object of that class. Beyond this, special code is required on the server side to make a reference to the object available to clients so that methods of the object can be invoked remotely.
Both the client code and the server code must have access to an IDL file that declares the methods that can be invoked remotely. This is the key to the whole process and the glue that holds it all together. The client and the server must also agree on how the server will provide an object reference to the client.
The sample programs in previous lessons have made use of a name service for the communication of an object reference from a server to a client. However, that is only one of the possible ways that such communication can be accomplished. In particular, it doesn't matter how the client gets the reference to the servant object, once it has that reference, it can invoke methods on the object.
Another standard method for communicating object references from the server to the client, supported by both CORBA and Java, is the use of stringified object references. A stringified object reference is an object reference that has been converted to an ordinary (but rather cryptic) string of characters. Java provides a pair of methods to be used for this purpose. One method can be used by the server to convert the object reference to the string. The other method can be used by the client to convert the string back to an object reference. While in its stringified state, the object reference can be communicated from the server to the client using any reliable means of communicating string information.
The sample program in this lesson uses a common disk file to communicate the string from the server to the client. The server writes the string in the file, and the client reads the string from the file. Obviously, to use this approach, both the server and the client must have access to the file. However, given the network programming capabilities of Java, it is a relatively easy task for a program running on one machine to write a file on another machine where it can be read by a program running on still another machine.
The sample program in this lesson is initially set up to cause the client and the server to run in separate processes on the same machine. It wouldn't be too difficult to modify the program to run the client and server on different machines while writing and reading the file on a machine that is accessible to both the server and the client.
CORBA makes it possible for a method in an object running under one virtual machine to invoke a method in an object running under a different virtual machine just as if it were a method in a local object. The two virtual machines can be running as different processes on the same hardware machine or they can be running on different machines connected by a TCP/IP network.
This is a Java/CORBA application that makes it possible for code in a client object running under one JVM to access a method in a servant object running under a different JVM on the same machine to get the date and time. Unlike previous similar programs however, this program does not use the name service. Rather, the server writes a file containing an object reference and the client reads that file to get the reference to the remote object.
The application requires three separate source files and one utility program. The source files are:
The utility program is:
idltojava.exe- a program that converts the IDL file to several java files, producing stub and skeleton source files and some other source files used by the client and the server.
Note that since the name service is not used, the program named tnameserv is not used with this sample program as it has been used in the past. This actually results in a program that is somewhat simpler and easier to understand because some of the fairly cryptic Java code required to make use of the name service is replaced by simple file I/O code.
As you are aware by now, I prefer to make the entire process semi-automatic by encapsulating the process in an MS-DOS batch file. If you are not running under Windows or NT, you will need to figure out how to produce an equivalent script file for the platform that you are running on.
Note that in this case, the use of a batch file to manage the overall process can lead to problems. In particular, since the client and the server run in separate processes, and both are started running by the batch file, the client can sometimes try to read the file containing the object reference before it has been successfully written by the server. If you experience this problem, simply remove the statement from the batch file that starts the client program, and start it manually after the server program has written its output file to the disk. This is not a fundamental problem with using stringified object references. It is simply a problem with using a single batch file to start both the server and the client.
I have a folder on my computer that contains the following four source files:
Only the last three files are required. The batch file is optional, but is useful for managing the whole process (with the caveat described earlier).
Initially, this folder doesn't have any sub folders. However, execution of the program idltojava creates a package to contain the source files that it generates. By default, it makes that package reside in a sub folder of the folder from which it is executed. To avoid cluttering up my disk with generated files (or more properly to make it easier to find and delete the generated files), I forced the package to be in a tree that begins with a folder named junk. As a result, a junk folder is automatically generated. The junk folder contains another folder named TheDateApp that is the actual package name.
The IDL file is the glue that holds the system of programs together and makes it possible for code in a client to invoke methods in a remote object on a basis that is both platform and language independent. This was discussed in some detail in the earlier lessons.
Except for the name (Corba05.idl), the IDL file for this application is similar to IDL files from previous lessons. A listing of the file is provided near the end of this lesson, and is also shown below for convenience.
/*File Corba05.idl **********************************************************/ module TheDateApp{ interface TheDate{ string getTheDate(); };//end interface TheDate };//end module TheDateApp |
The batch file for this application is named Corba05.bat. The important parts of the file are shown below. The most significant change in this batch file relative to previous versions is that it does not contain an invocation of the program named tnameserv as used in previous programs. This is because the client and server programs don't use the name service to communicate the reference to the remote object in this version of the program.
rem File Corba05.bat idltojava -fno-cpp -p junk Corba05.idl javac Corba05*.java start java Corba05Server java Corba05Client |
The name of the file containing the server code is Corba05Server.java.
The code for a CORBA server or a CORBA client includes quite a lot of code that is very similar from one application to the next. This is the code that is necessary to get everything initialized so that all the pieces work together in a cooperative manner. In the previous lessons, I used the name service to accomplish and support this requirement. This lesson takes a different approach.
I will discuss the source code for the server in fragments, and for the most part will only discuss that code that is different from the previous approach. A complete listing of the program is provided near the end of this lesson.
All code and import statements involving the name service were omitted from this version.
The first fragment shows the class definition for the servant class named TheDateServant. This class includes the method named getTheDate() that satisfies the requirements of the IDL file. The use of CORBA makes it possible for remote clients to invoke this method on an object of this class.
This class definition is essentially unchanged from previous lessons, and is provided here simply for your convenience.
class TheDateServant extends _TheDateImplBase{ public String getTheDate(){ return new Date() + "\n"; }//end getTheDate() }//end TheDateservant class |
The remainder of the server program is contained in the main() method of the class named Corba05Server. The next fragment begins at the point where the ORB for the server is created and initialized.
Since this program does not use a name service, much of the complexity regarding the identification of the computer where the name service is running and the port that it is listening on doesn't exist in this program. In fact, the entire issue of where the client and server code is running tends to become moot provided that the server can find a way to provide a stringified object reference to the client. That object reference contains all of the information that the client's ORB needs to support communications between the client code and the methods of the servant object.
Of course, the complexity that is eliminated by not using the name service is replaced by a different type of complexity involving the need for the server to make the stringified object reference available to the client.
ORB orb = ORB.init(args, null); |
The following code, which instantiates a servant object and registers it with the server's ORB, is unchanged from previous versions. It is repeated here for completeness.
TheDateServant theDateRef = new TheDateServant(); orb.connect(theDateRef); |
The following fragment is new to this program. This is the method named object_to_string() of the ORB class that is provided by Java and used to convert an object reference to a string (to create a stringified object reference).
String stringObjRef = orb.object_to_string(theDateRef); |
The following box contains the string produced by the invocation of this method for this program on my computer. It was necessary for me to manually insert line breaks to force it to fit within the width of the page. This material is provided here simply to illustrate that a great deal of information is encapsulated in this string, not necessarily in a format that is intended for human consumption.
IOR:000000000000001b49444c3a54686544617465417 |
The following fragment shows the code used by the server program to write the stringified object reference to a disk file named junk.txt in the user's home directory. There shouldn't be anything here that you don't already understand. Again I present this code here for completeness. I also present it here to serve as the launching pad for a discussion of the overall process.
This particular code, as written in this sample program, causes this program to be generally confined to run in separate processes on a single computer. The stringified object reference is written into a file in the user's home directory. We will see later that the client program reads the file from the user's home directory. This implies that the same user executes both the server program and the client program. Although it may be possible on some systems for the same user to execute the server program and the client program on different computers, this is probably not the norm for which CORBA is intended.
Therefore, in order to use this program on two or more different computers in a network, it will be necessary for you to modify the following code so that the stringified object reference is written into a disk file that is accessible by the client program. The client program will need to know in advance where that file is stored and what it is named. You can probably accomplish this by using the network programming features of Java. If you are unfamiliar with those features, you will find several lessons on network programming in these tutorials.
String theFile = System.getProperty("user.home") + System.getProperty("file.separator") + "junk.txt"; FileOutputStream fileOutputStream = new FileOutputStream(theFile); PrintStream printStream = new PrintStream(fileOutputStream); printStream.print(stringObjRef); printStream.close(); |
The remaining code in the server program is straightforward and very similar to code in previous lessons, so I won't discuss it further here.
The file containing the client code is named Corba05Client.java.
I will discuss the source code for the client in fragments, and for the most part will only discuss that code that is different from the approach in previous lessons. A complete listing of the program is provided near the end of this lesson.
Again, as with the server program, note that all code and import statements involving the name service were omitted from this version. All of the code that I will discuss in this section is contained in the main() method of the class named Corba05Client.
The first fragment shows the code to create and initialize the ORB for the client. As with the server, since there is no name service involved, a very simple version of the init() method of the ORB class can be used in this case. The various uses of this method have been discussed in detail in previous lessons.
ORB orb = ORB.init(args, null); |
The next code fragment shows that the client code reads the stringified object reference from a text file named junk.txt in the user's home directory. In this simple example, this is very straightforward. However, I must emphasize the fact that the client code must know how to find and read the file containing the reference to the remote object.
In a network situation, this could take on a variety of different approaches. For example, on a Local Area Network (LAN), it may be no more difficult than having both the server code and the client code specify a path to a network drive when writing and later reading the file containing the object reference. Of course, they would both have to know the path, and would have to know the name of the file.
In an Internet situation, it might be possible for the server code to write the file on a corporate FTP server where it could be later accessed by the client code.
However, you manage it, the server code must be able to make the stringified object reference available to the client code.
The "bottom line" is that the following fragment reads the string that was deposited in the text file by the server, and saves that string in an object of type String named stringObjRef.
String filename = System.getProperty("user.home") + System.getProperty("file.separator") +"junk.txt"; FileInputStream fileInputStream = new FileInputStream(filename); DataInputStream dataInputStream = new DataInputStream(fileInputStream); String stringObjRef = dataInputStream.readLine(); |
The following fragment uses Java's string_to_object() method of the ORB class to convert the stringified object reference back to a generic CORBA object reference.
org.omg.CORBA.Object obj = orb.string_to_object(stringObjRef); |
As you are already aware, in order to use this generic CORBA object, it is necessary to cast or narrow it into an object of the correct type. This is accomplished by the following fragment that is essentially the same as code that you have seen on several previous occasions.
TheDate theDateRef = TheDateHelper.narrow(obj); |
At this point, the object reference can be used by the client to invoke methods on the remote object as shown in the following simple code fragment.
String theDate = theDateRef.getTheDate(); System.out.println(theDate); |
The remaining client code is essentially what you have seen before so I won't discuss it further. A complete listing of the program is provided later.
The following is a brief summary of the steps involved in creating a Java/CORBA software system involving remote objects and stringified object references.
Complete listings for the IDL file, the batch file and the source code files that you must create are provided in this section. Listings are not provided for the Java source code files that are automatically generated by the idltojava program.
/*File Corba05.idl See Corba05Server.java for information on this program. **********************************************************/ module TheDateApp{ interface TheDate{ string getTheDate(); };//end interface TheDate };//end module TheDateApp |
.
rem File Corba05.bat rem See Corba05Server.java for description of program rem This program does not use a name service. rem Caution: Use of this batch file may result in a rem race condition that causes the client to try to read rem common file before it is produced by the server. echo off echo Convert the idl file to the required set of java files idltojava -fno-cpp -p junk Corba05.idl echo Compile all of the java files javac Corba05*.java echo Start the server as a new process. start java Corba05Server echo Start the client. java Corba05Client |
.
/*File Corba05Server.java This is a simple CORBA server that provides a servant object to return the date and time as a string. This version is similar to the Corba03 version except that this version uses a stringified object reference stored in a disk file instead of using the name service. **********************************************************/ import junk.TheDateApp.*; import org.omg.CORBA.*; import java.util.Date; import java.io.*; class TheDateServant extends _TheDateImplBase{ public String getTheDate(){ return new Date() + "\n"; }//end getTheDate() }//end TheDateservant class //=======================================================// public class Corba05Server { public static void main(String args[]){ try{ //Create and initialize the ORB ORB orb = ORB.init(args, null); //Create servant and register it with the ORB TheDateServant theDateRef = new TheDateServant(); orb.connect(theDateRef); //Convert the object reference to a string String stringObjRef = orb.object_to_string(theDateRef); //Write the stringified object ref to a file named // junk.txt in the user's home directory String theFile = System.getProperty("user.home") + System.getProperty("file.separator") + "junk.txt"; FileOutputStream fileOutputStream = new FileOutputStream(theFile); PrintStream printStream = new PrintStream(fileOutputStream); printStream.print(stringObjRef); printStream.close(); System.out.println("Server is running"); System.out.println("Obj ref stored in: " + System.getProperty("user.home") + System.getProperty("file.separator") + "junk.txt"); // wait for invocations from clients java.lang.Object sync = new java.lang.Object(); synchronized (sync) { sync.wait(); }//end synchronized block }catch (Exception e) { System.err.println("ERROR: " + e); e.printStackTrace(System.out); }//end catch block }//end main() }//end Corba05Server class |
.
/*File Corba05Client.java See Corba05Server.java for information on this program. **********************************************************/ import junk.TheDateApp.*; import org.omg.CORBA.*; import java.io.*; public class Corba05Client{ public static void main(String args[]){ try{ // create and initialize the ORB ORB orb = ORB.init(args, null); //Read the stringified object ref from a file named // junk.txt in the user's home directory String filename = System.getProperty("user.home") + System.getProperty("file.separator") +"junk.txt"; FileInputStream fileInputStream = new FileInputStream(filename); DataInputStream dataInputStream = new DataInputStream(fileInputStream); String stringObjRef = dataInputStream.readLine(); //Convert the stringified object reference to a // generic CORBA object reference org.omg.CORBA.Object obj = orb.string_to_object(stringObjRef); //Cast, or narrow the generic object reference to a // true object reference. TheDate theDateRef = TheDateHelper.narrow(obj); //Call the TheDate server object and print results String theDate = theDateRef.getTheDate(); System.out.println(theDate); //Delay program termination so that the console // won't disappear from the screen when running // under control of a batch file. int ch1 = '0'; System.out.println("Press Ctrl-z to terminate"); while( (ch1 = System.in.read() ) != -1); }catch (Exception e) { System.out.println("ERROR : " + e) ; e.printStackTrace(System.out); }//end catch block }//end main() method }//end Corba05Client class |
-end-