Netscape Internet Service Broker for Java: Programmer's Guide, Version 1.0

[Contents] [Previous] [Next]

Chapter 8
Defining CORBA Interfaces With Java

ISB for Java incorporates features, collectively known as Caffeine, which make the product easier to work with in a Java environment. This chapter describes how to use the 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:

About Caffeine

Following are the ISB for Java features, collectively known as Caffeine, which ease Java development.

Working with the java2iiop Compiler

The 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.

  1. Compile the service using javac.

  2. Write client code. The code is the same whether you are using Caffeine or IDL; for an example, see MsgClient.java.

  3. Compile client code using javac.

  4. Start the service.

  5. Start the client.

A Caffeine Example

Developing with Caffeine is almost exactly the same as developing with IDL. The key difference is that you define interfaces in a Java file instead of in an IDL file. Techniques for coding implementations of interfaces, clients, and services are the same whether you are using IDL or Java.

This example begins with the Hello interface shown below. The file 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
package SendMsg;
public interface Hello extends org.omg.CORBA.Object {
    public String sayHello();
};
The Hello interface is implemented below in HelloImpl.java. The implementation is the same whether you are using Caffeine or IDL.

Hello.java implements the Hello interface.

// HelloImpl.java
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!";
  }
}
The following code example implements the service. This code is the same whether you are using Caffeine or IDL.

// Using Enterprise Server's ORB
// 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 following code example shows the client connecting to the service. This code is the same whether you are using Caffeine or IDL.

The client applet.

// Using Enterprise Server's ORB
// 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();
    }
}
To build this example, do the following:

javac Hello.java

  1. java2iiop Hello

  2. javac HelloImpl.java

  3. javac MsgServer.java

  4. javac MsgClient.java
Next, start the service in the usual way:

prompt> java -DDISABLE_ORB_LOCATOR MsgService
Finally, use one of the following techniques to start the client.

Table 1:
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.

Java Type IDL/IIOP Type
Package

Module

boolean

boolean

char

char

byte

octet

String

string

short

short

int

long

long

long long

float

float

double

double

org.omg.CORBA.Any

any

org.omg.CORBA.TypeCode

TypeCode

org.omg.CORBA.Principal

Principal

org.omg.CORBA.Object

Object

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:

If a class meets all of the requirements, the compiler maps it to an IDL struct; otherwise, the compiler maps it to an extensible struct. The following example shows a simple Java class that meets all of the 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.

An Extensible Struct Example

This section providing several code samples showing how to use extensible structs. The extensible struct code samples show the ability to pass these data types (extensible structs) and that, when passed, they retain their values. They behave like any other CORBA data type and they have an additional advantage: with extensible structs you can go beyond what you can do with IDL and pass arbitrary Java serializable objects.

The code samples provided here are simple ones; you could write a much more complicated data structure that has a tree, or a complicated linked list, or a skip list. Using extensible structs, you could pass any of these complicated data structures by value. The code sample below shows a fairly simple class that cannot be defined in IDL. The class has a constructor to help construct linked lists and a 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:

Next is the ListUtility interface, shown below. The interface has all the attributes of a Caffeine-enabled interface: It is a public interface that extends org.omg.CORBA.Object. The ListUtility interface defines these methods:

The ListServer class, shown below, is a standard server class. It extends the skeleton, has a constructor, and has implementations of all of the methods (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
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();
  }
}
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 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
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));
    }
  }
}
To build this example, do the following:

javac ListUtility.java

  1. java2iiop ListUtility

  2. javac ListClient.java

  3. javac ListServer.java
Here is the output.

The List output.

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 }

Extensible Structs and GIOP Messages

The following section is a discussion of an advanced topic and is provided for developers writing code for non-ISB ORBs who want their code to understand and interpret extensible structs as implemented in ISB. Please be advised that if GIOP formats change, the way ISB works with GIOP messages will also change and your applications may need to be modified.

When messages are sent with an interface that extends 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:


[Contents] [Previous] [Next]

Last Updated: 02/04/98 13:47:30


Copyright © 1997 Netscape Communications Corporation

Any sample code included above is provided for your use on an "AS IS" basis, under the Netscape License Agreement - Terms of Use