Defining CORBA interfaces with Java

Distributed application development is a feature of JBuilder Enterprise.

This chapter explains how to use the java2iiop and java2idl compilers (also called the Caffeine compiler) to generate client stubs and servants from interface definitions written in Java instead of IDL.

After you generate stubs and skeletons from interface definitions written in Java, follow the steps outlined in "Exploring CORBA-based distributed applications in JBuilder".

This chapter contains the following topics:

Other information about using Caffeine and mapping primitive and complex data types can be found in the VisiBroker for Java Programmer's Guide.


Caffeine

VisiBroker incorporates features, collectively known as Caffeine, which make the product Web-friendly and easy to work with in a Java environment. These features include:

If you are going to design CORBA interfaces in Java, you must use java2iiop as the compiler to produce the client stubs and servants. When designing CORBA interfaces in Java, you do not need to create an IDL file, in fact, the generated IDL file cannot be modified or compiled. You would generate an IDL file from a Java interface only to

Accessing the java2iiop or java2idl compiler in JBuilder

To access the java2iiop or java2idl compiler in the JBuilder development environment,

  1. Make sure JBuilder is set up correctly by following the steps outlined in "Setting up JBuilder for CORBA applications".
  2. In an existing project, create or select the Java interface file in the project pane. For an example of a Java interface definition, see the sample in the /inprise/vbroker/examples/rmi-iiop/ directory of your VisiBroker installation.
  3. Right-click on the Java interface file, select Properties from the context menu.
  4. Check Generate IIOP Interface or Generate IDL under VisiBroker Settings. Select OK.
  5. Right-click on the Java interface file in the project pane and select Make to compile the interface definition and generate IIOP or IDL interface files.

Once the Java interface definition file has been compiled, you can use JBuilder to generate a multi-tier CORBA application from the IDL file. See the topic "Generating IDL interfaces from Java" in this chapter. The next section discusses working with the java2iiop compiler.

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, but the advantage is that you can define them in Java rather than IDL. The compiler reads Java byte code-it does not read source code or Java files but reads class files. The compiler then generates IIOP-compliant stubs and skeletons needed to do all the marshalling and communication required for CORBA.

When you run the java2iiop compiler, it generates the same files as if you had written the interface in IDL. Primitive data types like the numeric types (short, int, long, float, and double), string, CORBA objects or interface objects, Any's, type codes - all of these are understood by the java2iiop compiler and mapped to corresponding types in IDL.

When using the Caffeine compiler 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. (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 all of the marshalling elements, readers and writers which enable you to use the interface for remote calls. An example of a Java interface definition file is shown in the VisiBroker sample located in the /inprise/vbroker/examples/rmi-iiop directory.

Note: For developers who are familiar with RMI (Remote Method Invocations), this is equivalent to a class extending the java.rmi.Remote interface.

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" in the VisiBroker for Java Programmer's Guide.

RMI vs. Caffeine

Another way to invoke methods on remote Java objects is to use RMI, provided by JavaSoft and others. VisiBroker for Java provides capabilities equivalent to RMI, but, in addition, VisiBroker allows you to create Java objects that communicate with all other objects that are CORBA compliant even if they are not written in Java. For an example of a Java interface that describes an RMI remote interface, see the sample file /inprise/vbroker/examples/rmi-iiop/bank/Account.java. For more information on developing with RMI, see "Exploring Java RMI-based distributed applications in JBuilder."

Tutorial: Generating IIOP interfaces running java2iiop

We will use pieces of the example files located in the /inprise/vbroker/examples/rmi-iiop/ directory to demonstrate how to define an interface in Java and have it compile into IIOP-compliant stubs and servants using the java2iiop compiler. For the rmi-iiop sample to compile, the Bank directory must exist, and the source directory must be above the Bank directory (as in the example application). The files needed for this example are:

Follow these steps to generate the IIOP interfaces:

  1. Select File|Close All to close all existing projects.

  2. Create a new project.
    1. Select File|New Project from the JBuilder menu.
    2. Enter the project name and directory.
    3. Optionally, click Next to enter the Title, Author, Company, and Description fields.
    4. Click Finish.

  3. Create the new Java interface definition file.
    1. Right-click the project JPX file.
    2. Select Add Files/Packages.
    3. Enter the file with a .java extension in the File Name field, for example, AccountManager.java.
    4. Click OK. A dialog asks if you want to create the file, click OK.
    5. Double-click the file. It displays, with the Source tab selected.

  4. Add Java interface code to this file by typing it directly into the Source window. An example of such a file is:

    package Bank;
    
    public interface AccountManager extends org.omg.CORBA.Object,
    	AccountManagerOperations {
    }
    

  5. In the project pane, right-click on the Java interface file. Select Properties from the context menu.

  6. Check the Generate IIOP field in the VisiBroker tab of the Build page, then click OK.

  7. Right-click on the Java interface file, and select Make from the context menu.

The preceding steps generate the Java byte code that is used as input to the java2iiop compiler, and generates the client stubs and servants.

The java2iiop compiler generates all of the usual auxiliary files, like Helper and Holder classes. For more information about the generated files, see the "Code Generation" topic in the VisiBroker for Java Programmer's Guide. 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 the "Generated Classes" chapter in the VisiBroker for Java Reference Manual.

Completing the development process

The VisiBroker for Java Programmer's Guide includes more information, including sample code and sample applications, for completing the development process using VisiBroker for Java. In general, after generating stubs and skeletons, create your client and server classes. Follow these steps:

  1. Use the skeleton class to create an implementation of server object; for an example, see Code Sample 2.
  2. Compile your server class by right-clicking on the server file in the project pane and selecting Make.
  3. Write client code; for an example, see Code Sample 3.
  4. Compile the client code by right-clicking on the client file in the project pane and selecting Make.
  5. Start the VisiBroker Smart Agent by selecting it from the Tools menu. For this option to be available, you must have set up JBuilder as described in "Setting up JBuilder for CORBA applications".
  6. Start server objects by right-clicking on the server file in the project pane and selecting Run.
  7. Start the client by right-clicking on the client file in the project pane and selecting Run.

Important: The examples shown in this chapter assume that a VisiBroker Smart Agent is running. Make sure you have a Smart Agent running by selecting Tools|VisiBroker Smart Agent. For more information on the Smart Agent, see the VisiBroker for Java Programmer's Guide.

Using rmi-iiop

The rmi-iiop example has a simple AccountManager interface to create an account, (re)open an account, and get a list of accounts. The account that is created contains operations to get the name on the Account, and to set and get the balance in the account. A third Java class, AccountData, is used to represent the information that is used to create accounts. This example also illustrates the use of RMI over IIOP, and how we can use RMI over IIOP to pass native Java classes (those not mapped from IDL) across the wire in remote method calls. It also illustrates the use of an RMI-style remote interface and how one can use create CORBA servers, but use RMI clients to access the CORBA servers. To run and view the example, refer to the file rmi-iiop_java.html in the /inprise/vbroker/examples/rmi-iiop/ directory of your VisiBroker installation.

To create the Server interface,

  1. Create the file.
    1. Right-click the project JPX file.
    2. Select Add Files/Packages.
    3. Enter the file with a .java extension in the File Name field, such as Server.java.
    4. Click OK. A dialog asks if you want to create the file, click OK.
    5. Double-click the file. It displays, with the Source tab selected.

  2. Add the Java code to this file by typing it directly into the Source window. An example of such code follows:

Code Sample 2   Implementing the Server interface.
CODE:
import org.omg.PortableServer.*;

public class Server {

  public static void main(String[] args) {
    try {
      // Initialize the ORB.
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
      // get a reference to the root POA
      POA rootPOA =
	    POAHelper.narrow(orb.resolve_initial_references("RootPOA"));

      // Create policies for our persistent POA
      org.omg.CORBA.Policy[] policies = {        rootPOA.create_lifespan_policy(LifespanPolicyValue.PERSISTENT) 
      };
      // Create myPOA with the right policies
      POA myPOA = rootPOA.create_POA( "rmi_bank_poa",
	    rootPOA.the_POAManager(), policies );
      // Create the servant
      AccountManagerImpl managerServant = new AccountManagerImpl();
      // Decide on the ID for the servant
      byte[] managerId = "RMIBankManager".getBytes();
      // Activate the servant with the ID on myPOA
      myPOA.activate_object_with_id(managerId, managerServant);

      // Activate the POA manager
      rootPOA.the_POAManager().activate();

      System.out.println(myPOA.servant_to_reference(managerServant)
	  	 + " is ready.");
      // Wait for incoming requests
      orb.run();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }

}

Code Sample 3 shows the client, as usual, binding to the server. The example client first creates all of the accounts with the appropriate balances. It does this by creating AccountData objects for each account and passing them to the AccountManager to create the accounts. It then confirms that the balance is correct on the created account. Next, the client queries the AccountManager for a list of all of the accounts, and credits $10 to each account, verifying the new balance is accurate.

To create the Client interface,

  1. Create the file.
    1. Right-click the project JPX file.
    2. Select Add Files/Packages.
    3. Enter the file with a .java extension in the File Name field, for example, Client.java.
    4. Click OK. A dialog asks if you want to create the file, click OK.
    5. Double-click the file. It displays, with the Source tab selected.

  2. Add Java code to this file by typing it directly into the Content pane. An example of Client.java follows:

Code Sample 3   Testing the AccountManager implementation.
CODE:
// Client.java

public class Client {

  public static void main(String[] args) {
    try {
      // Initialize the ORB.
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args,null);
      // Get the manager Id
      byte[] managerId = "RMIBankManager".getBytes();
      // Locate an account manager. Give the full POA name
	  //  and the servant ID.
      Bank.AccountManager manager = 
	     Bank.AccountManagerHelper.bind(orb, "/rmi_bank_poa",
		 managerId);
      // Use any number of argument pairs to indicate name,
	  //  balance of accounts to create
      if (args.length == 0 || args.length % 2 != 0) {
        args = new String[2];
        args[0] = "Jack B. Quick";
        args[1] = "123.23";

      }
      int i = 0;
      while (i < args.length) {
        String name = args[i++];
        float balance;
        try {
          balance = new Float(args[i++]).floatValue();
        } catch (NumberFormatException n) {
          balance = 0;
        }
        Bank.AccountData data = new Bank.AccountData(name, balance);
        Bank.Account account = manager.create(data);
        System.out.println("Created account for " + name + " with
		  opening balance of $" + balance);
      } 
    
      java.util.Hashtable accounts = manager.getAccounts();
    
      for (java.util.Enumeration e = accounts.elements(); e.hasMoreElements();) {
        Bank.Account account =
	Bank.AccountHelper.narrow((org.omg.CORBA.Object)e.nextElement());
        String name = account.name();
        float balance = account.getBalance();
        System.out.println("Current balance in " + name
		    + "'s account is $" + balance);
        System.out.println("Crediting $10 to " + name
		    + "'s account.");
        account.setBalance(balance + (float)10.0);
        balance = account.getBalance();
        System.out.println("New balance in " + name
		    + "'s account is $" + balance);
      }
    } catch (java.rmi.RemoteException e) {
      System.err.println(e);
    }
  }
}

To build this type of application, do the following:
  1. Setup JBuilder as described in "Setting up JBuilder for CORBA applications".

  2. Complete the steps in "Generating IIOP interfaces running java2iiop" to create a new project and define the interface definition in Java.

  3. Make sure you have a Smart Agent running by selecting Tools|VisiBroker Smart Agent.

  4. Compile the project by selecting Project|Make Project.

  5. Start the server by right-clicking on Server.java, and selecting Run from the context menu.

  6. Start the client by right-clicking on Client.java, and selecting Run from the context menu.

Mapping of primitive date types

Client stubs generated by java2iiop handle the marshalling of the Java primitive data types that represent an operation request so that they may be transmitted to the object server. When a Java primitive data type is marshalled, it must be converted into an IIOP-compatible format. For more information, please read the VisiBroker for Java Programmer's Guide.

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.

Note: The java2iiop compiler does not support overloaded methods on Caffeine interfaces.

Interfaces which do not extend org.omg.CORBA.Object are mapped to an extensible struct.

Arrays

Another complex data type that may 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:

The java2iiop compiler looks at a class, and checks to see if it meets all of these requirements. If the class does meet all of the requirements, the compiler maps it to an IDL struct; if it does not meet all of the requirements, it maps it to an extensible struct. For more information about extensible structs, see the VisiBroker for Java Programmer's Guide.


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.

The use of extensible structs is a VisiBroker 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.

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

Note: 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. For more information, including sample code and a sample application, refer to the VisiBroker for Java Programmer's Guide.

For more information, see "Defining CORBA Interfaces With Java" in the VisiBroker for Java Programmer's Guide.


Generating IDL interfaces from Java (using java2idl)

IDL is the foundation of CORBA applications. IDL interfaces define a component's boundaries. You can use IDL to define a component's attributes, the exceptions it raises, and the methods its interface supports.

You can generate IDL interfaces from Java interfaces defined in a .java file, using the java2idl compiler. Using a Java interface to describe a CORBA object has some limitations. All Java primitive types can be used. Java objects, however, can be used to define the interface only if the object implements java.io.Serializable.

The following steps discuss generating IDL interfaces from a Java interface definition, and then creating the application using JBuilder.

  1. Select File|Close to close all existing projects.

  2. Create a new project.
    1. Select File|New Project from the JBuilder menu.
    2. Enter the Project Name and Project Directory Name.
    3. Optionally, click Next to enter the Title, Author, Company, and Description fields.
    4. Click Finish.

  3. Create a new Java interface definition file, or browse to an existing file.
    1. Right-click the project JPX file.
    2. Select Add Files/Packages.
    3. Enter the file with a .java extension in the File Name field, for example, AccountManager.java, or browse to an existing file.
    4. Click OK. A dialog asks if you want to create the file, click OK.
    5. Double-click the file. It displays, with the Source tab selected.

  4. Enter the Java interface code in the Content pane. An example of such code would be:

    // a Java interface file
    package Bank;
    
    public interface AccountManager extends org.omg.CORBA.Object,
    	AccountManagerOperations {
    }
    
    
  5. Right-click on the Java interface file, select Properties from the context menu.

  6. Check Generate IIOP and Generate IDL from the VisiBroker tab of the Build page. Select OK.

  7. Right-click on the Java interface file in the project pane and select Make to compile the interface definition and generate IIOP interface files and IDL files. The IDL file is generated, as are the client stubs and servants. Click the plus sign to the left of the Java interface file to display the generated files.

From this point on, the steps for creating a distributed application are the same as described in "Exploring CORBA-based distributed applications in JBuilder".