java2iiop
compiler (also called the Caffeine compiler) to generate client stubs and service skeletons from interface definitions written in Java instead of IDL. This chapter also explains how to work with complex data types. In particular, it explains how to pass by value using extensible structs. This chapter includes the following major sections:
java2iiop
compiler lets you define interfaces and data types that can then be used as interfaces and data types in CORBA. The advantage is that you can define them in Java rather than IDL. The compiler reads Java bytecode: it does not read source code or java files, it reads compiled class files. The java2iiop
compiler then generates IIOP-compliant stubs and skeletons that do the marshalling and communication required for CORBA.
When you run java2iiop
, it generates files as if you had written the interface in IDL. Primitive data types like the numeric types (short, int, long, float, and double), String, Any, CORBA objects or interface objects, and typecodes are all understood by java2iiop
and mapped to corresponding types in IDL.
When using java2iiop
on Java interfaces, you define and mark them as interfaces to be used with remote calls. You mark them by having them extend the org.omg.CORBA.Object
interface. (For developers who are familiar with RMI (Remote Method Invocations), this is analogous to a class extending the java.rmi.Remote
interface. Caffeine provides capabilities equivalent to RMI, and allows you to create Java objects that communicate with all other objects that are CORBA-compliant, even if they are not written in Java.) The interface must also define methods that you can use in remote calls. When you run the compiler, it searches for these special CORBA interfaces. When one is found, it generates the marshalling elements (readers and writers) which enable you to use the interface for remote calls. For classes, the compiler follows other rules and maps the classes either to IDL structs or extensible structs. For more information about complex data types, see Mapping of Complex Data Types.
Figure 8.1 Development process when using java2iiop
.
Running java2iiop
Before using the java2iiop
compiler, generate Java bytecode (that is, compile a Java source file to create a class file) to input to java2iiop
. For example, the following command compiles a Java source file named CafMsg.java
and generates Java bytecode in a file named CafMsg.class
.
prompt>javac CafMsg.java
After generating bytecode, you can use the java2iiop
compiler to generate client stubs and server skeletons. The following example compiles the bytecode for CafMsg.class
. Note that the .class
extension is not used.
prompt>java2iiop CafMsg
The java2iiop
compiler generates all of the usual auxiliary files such as Helper and Holder classes. For more information, see Generated Files. The files generated by the java2iiop
compiler are the same as those generated by the idl2java
compiler. For more information about the generated files, see "Generated Classes'' in the Netscape Internet Service Broker for Java Reference Guide.
Completing the Development Process
After generating stubs and skeletons using the java2iiop
compiler, create classes for the client and the service. Follow these steps:
Implement the service. The code is the same whether you are using Caffeine or IDL; for an example, see MsgService.java.
javac
.
Hello.java
replaces the IDL file that would define this interface if you were not using Caffeine. Mark interfaces to be used with remote calls by having them extend the org.omg.CORBA.Object
interface.
Defining the Hello interface.
// Hello.java
The Hello interface is implemented below in
package SendMsg;
public interface Hello extends org.omg.CORBA.Object
{
public String sayHello();
};HelloImpl.java
. The implementation is the same whether you are using Caffeine or IDL.
Hello.java implements the Hello interface.
// HelloImpl.java
The following code example implements the service. This code is the same whether you are using Caffeine or IDL.
package SendMsg;
public class HelloImpl extends SendMsg._sk_Hello {
/** Construct a persistently named object. */
public HelloImpl(java.lang.String name) {
super(name);
}
/** Construct a transient object. */
public HelloImpl() {
super();
}
public java.lang.String sayHello() {
// implement operation...
System.out.println("Caf Client called sayHello.");
return "Caf Hello!";
}
}// Using Enterprise Server's ORB
The following code example shows the client connecting to the service. This code is the same whether you are using Caffeine or IDL.
// MsgService.java
public class MsgService {
public static void main (String[] args) {
try {
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
// Initialize the BOA.
org.omg.CORBA.BOA boa = orb.BOA_init();
// Create the Hello object.
SendMsg.HelloImpl hi = new SendMsg.HelloImpl("CaffeineExample");
// Export the newly created object.
boa.obj_is_ready(hi);
// String host = java.net.InetAddress.getLocalHost().getHostName();
String urlStr = "http://myHost/CaffeineExample";
// Or, if using Communicator's ORB, the urlStr would be:
// "http://myHost/NameService/CaffeineExample"
netscape.WAI.Naming.register(urlStr, hi);
System.out.println(hi + " is ready.");
// Wait for incoming requests
boa.impl_is_ready();
}
catch(org.omg.CORBA.SystemException e) {
System.err.println(e);
}
}
}
The client applet.
// Using Enterprise Server's ORB
To build this example, do the following:
// MsgClient.java
import netscape.WAI.Naming;
public class MsgClient extends java.applet.Applet {
public void init() {
try {
// Initialize the ORB.
org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init();
// Locate a message service.
String urlStr = "http://myHost/CaffeineExample";
// Or, if using Communicator's ORB, the urlStr would be:
// "http://myHost/NameService/CaffeineExample"
org.omg.CORBA.Object obj = Naming.resolve(urlStr);
SendMsg.Hello hi = SendMsg.HelloHelper.narrow(obj);
// Print a message.
System.out.println(hi.sayHello());
}
catch(org.omg.CORBA.SystemException e) {
System.err.println(e);
}
}
public static void main(String args[]) {
MsgClient mc = new MsgClient();
mc.init();
}
}javac Hello.java
prompt> java -DDISABLE_ORB_LOCATOR MsgServiceFinally, use one of the following techniques to start the client.
Command line |
prompt> java -DDISABLE_ORB_LOCATOR MsgClient
|
AppletViewer |
prompt> appletviewer MsgClient.html
|
Java-enabled browser |
http://myHost/projects/cafMsg/MsgClient.html
|
Mapping of Primitive Data Types
Client stubs generated by java2iiop
handle the marshalling of the Java primitive data types that represent an operation request so that they can be transmitted to the object server. When a Java primitive data type is marshalled, it must be converted into an IIOP-compatible format. The following table summarizes the mapping of Java primitive data types to IDL/IIOP types.
Mapping of Complex Data Types
This section discusses interfaces, arrays, Java classes, and extensible structs; it shows how the java2iiop
compiler can be used to handle complex data types.
Interfaces
Java interfaces are represented in IDL as CORBA interfaces and they must inherit from the org.omg.CORBA.Object
interface. When passing objects that implement these interfaces, they are passed by reference. The java2iiop
compiler does not support overloaded methods on Caffeine interfaces.
Arrays
Another complex data type that can be defined in classes is an array. If you have an interface or definitions that use arrays, the arrays map to CORBA unbounded sequences.
Mapping Java Classes
In Java classes, you can define arbitrary data types. Some arbitrary data types are analogous to IDL structures (also called structs). If you define a Java class so that it conforms to certain requirements, then the java2iiop
compiler maps it to an IDL struct. You can map Java classes to IDL structs if the class fits all of these requirements:
An example of a Java class that would map to an IDL struct.
//Java
final public class Address {
public string name;
public string street_address;
public short zipcode;
} Extensible Structs
Any Java class that does not meet all of the requirements listed above is mapped to an extensible struct. An extensible struct is an upwardly-compatible extension of CORBA structs. When you use extensible structs, objects are passed by value.
Pass by value is the ability to pass object state to another Java program. Assuming that a class definition of the Java object is present on the server side, the Java program can invoke methods on the cloned object that has the same state as the original object.
NOTE:
The use of extensible structs is an extension to the OMG IDL: there is an
additional keyword, extensible
. If you want to stay within pure CORBA, or
if you are going to port your code to other ORBs, you should use IDL structs
and not extensible structs. Extensible structs allow you to use classes that can
be defined in Java but cannot be defined in IDL because of CORBA limitations.
ISB uses Java serialization to pass classes in the form of extensible structs. Java serialization compresses a Java object's state into a serial stream of octets that can be passed on-the-wire as part of the request. Because of Java serialization, all the data types that are passed must be serializable; that is, they must implement java.io.serializable
.
Extensible structs allow data that use pointer semantics to be passed successfully. For example, defining a linked list in your application code is standard practice, yet there is no way to define a linked list in standard IDL. The solution to this problem is that you can define it by using extensible structs. When you pass extensible structs across address spaces, pointers are maintained.
toString
method which prints out the values of the list. With the mapping rules in mind, there are a few things which point out that this class will map as an extensible struct:
java.io.Serializable
.
// List.java
public class List implements java.io.Serializable {
private String _data;
private List _next;
public List(String data, List next) {
_data = data;
_next = next;
}
public String data() {
return _data;
}
public List next() {
return _next;
}
public void next(List next) {
_next = next;
}
public String toString() {
StringBuffer result = new StringBuffer("{ ");
for(List list = this; list != null; list = list.next()) {
result.append(list.data()).append(" ");
}
return result.append("}").toString();
}
}
org.omg.CORBA.Object
. The ListUtility interface defines these methods:
length
- This method computes the length of the list.
reverse
- This method returns a list that is in reverse order of items that were used as input.
sort
- This method sorts the list.// ListUtility.java
public interface ListUtility extends org.omg.CORBA.Object {
public int length(List list);
public List reverse(List list);
public List sort(List list);
}
length
, reverse
, and sort
). However, the List implementations in these code samples do not support circular lists.
The length
and reverse
methods are straightforward. The sort
method is implemented by doing a merge sort. The sort
method has a base case which calculates that if the length of the list is one or less, the list is already sorted. Otherwise, it splits the list in half, sorts each half of the list, and merges the two halves into a sorted list. To do the merge portion of the merge sort, the merge
method takes two sorted lists and merges them into a single sorted list.
The main
method in ListServer is standard: it initializes the ORB and BOA, instantiates ListServer, gets the object ready, and prints out that the object is ready.
Implementing the List interface.
// ListServer.java
The ListClient is also standard: it binds to the ListServer, in three separate short sections it creates lists, and, finally it calls the operations on each list. The first section prints out a list called "Hello World''. It prints out the length, reverse order, and the sorted version. To view an example of the output, see below. The next section is a slightly more complicated list--"This is a test"--and it does the same thing as the first list. The third section in the code reads in the file for
import org.omg.CORBA.ORB;
import org.omg.CORBA.BOA;
public class ListServer extends _sk_ListUtility {
ListServer(String name) {
super(name);
}
public int length(List list) {
int result = 0;
for( ; list != null; list = list.next()) {
result++;
}
return result;
}
public List reverse(List list) {
List result = null;
for( ; list != null; list = list.next()) {
result = new List(list.data(), result);
}
return result;
}
public List sort(List list) {
int length = length(list);
if(length <= 1) {
return list;
}
// split the list in half
List lhs = list;
for(int i = 0; i < length / 2 - 1; i++) {
list = list.next();
}
List rhs = list.next();
// this actually splits the lists
list.next(null);
// sort and merge the two halves
return merge(sort(lhs), sort(rhs));
}
public List merge(List lhs, List rhs) {
if(lhs == null) {
return rhs;
}
if(rhs == null) {
return lhs;
}
// figure out which side is next
List head;
if(lhs.data().compareTo(rhs.data()) < 0) {
head = lhs;
lhs = lhs.next();
}
else {
head = rhs;
rhs = rhs.next();
}
head.next(merge(lhs, rhs));
return head;
}
public static void main(String[] args) {
ORB orb = ORB.init();
BOA boa = orb.BOA_init();
ListUtility impl = new ListServer("demo");
boa.obj_is_ready(impl);
System.out.println(impl + " is ready.");
boa.impl_is_ready();
}
}ListUtility.java
. It reads it into a string and then breaks it into tokens. It breaks on punctuation and white space. It, too, prints the length, reverse order, and sorted version.
The Client Application for the list.
// ListClient.java
To build this example, do the following:
import org.omg.CORBA.ORB;
import java.io.*;
import java.util.*;
public class ListClient {
public static void main(String[] args) throws Exception {
ORB orb = ORB.init();
String host = args[0];
ListUtility lu =
ListUtilityHelper.narrow
(netscape.WAI.Naming.resolve("http://" + host + "/listutil"));
{
List list = new List("Hello", new List("World", null));
System.out.println("list: " + list);
System.out.println("length: " + lu.length(list));
System.out.println("reverse: " + lu.reverse(list));
System.out.println("sort: " + lu.sort(list));
}
{
List list = new List("this",
new List("is",
new List("a",
new List("test", null))));
System.out.println("list: " + list);
System.out.println("length: " + lu.length(list));
System.out.println("reverse: " + lu.reverse(list));
System.out.println("sort: " + lu.sort(list));
}
{
// read in the contents of a file
InputStream input = new FileInputStream("ListUtility.java");
byte[] bytes = new byte[input.available()];
input.read(bytes);
String text = new String(bytes);
List list = null;
// break the input into tokens (ignoring white space and punctuation)
StringTokenizer tokenizer = new StringTokenizer(text, " \n.;,/{}()");
while(tokenizer.hasMoreTokens()) {
list = new List(tokenizer.nextToken(), list);
}
System.out.println("list: " + list);
System.out.println("length: " + lu.length(list));
System.out.println("reverse: " + lu.reverse(list));
System.out.println("sort: " + lu.sort(list));
}
}
}javac ListUtility.java
list: { Hello World }
length: 2
reverse: { World Hello }
sort: { Hello World }
list: { this is a test }
length: 4
reverse: { test a is this }
sort: { a is test this }
list: { list List sort List public list List reverse List public list List
length int public Object CORBA omg org extends ListUtility interface public
java ListUtility }
length: 25
reverse: { ListUtility java public interface ListUtility extends org omg CORBA
Object public int length List list public List reverse List list public List
sort List list }
sort: { CORBA List List List List List ListUtility ListUtility Object
extends int interface java length list list list omg org public public public
public reverse sort }
org.omg.CORBA.Object
, a GIOP (General Inter-ORB protocol) message is created. The standard header is included in the message, as well as the arguments. Each argument is put into an octet sequence containing the Java serialization. An octet sequence is a standard CORBA type used in GIOP messages to pass complex data types. If you are working with communication protocols and understand Java serialization, you can take this grouping of bytes in the GIOP message and extract from it the arguments being passed.
Since an extensible struct is just a stream of octets (that is, an array of bytes), a developer working with a non-Netscape ORB can write code that does the following if an extensible struct is passed to that non-Netscape ORB:
Last Updated: 02/04/98 13:47:30