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:
java2idl
compiler to turn your Java code into IDL interfaces, thereby allowing you to generate client stubs in the language of your choice.
Other information about using Caffeine and mapping primitive and complex data types can be found in the VisiBroker for Java Programmer's Guide.
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:
The java2iiop compiler allows you to stay in an all-Java environment, if that's what you want. The java2iiop
compiler takes your Java interfaces and generates IIOP-compliant stubs and skeletons. Part of the advantage of using the java2iiop
compiler is that, through the use of extensible structs, you can pass Java serialized objects by value. This feature is unique to VisiBroker for Java. For more information, please read the VisiBroker for Java Programmer's Guide, or "Working with the java2iiop compiler" in this chapter.
The java2idl compiler turns your Java code into IDL, thereby allowing you to generate client stubs in the language of your choice. In addition, because this compiler maps your Java interfaces to IDL, you can re-implement Java objects in another programming language that supports the same IDL. For more information about this compiler, see "Commands" in the VisiBroker for Java Reference Manual, or "Generating IDL interfaces from Java" in this chapter.
Web Naming allows you to associate a URL with objects, allowing an object reference to be obtained by specifying a URL. For more information, see the VisiBroker for Java Programmer's Guide.
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
To access the java2iiop
or java2idl
compiler in the JBuilder development environment,
/inprise/vbroker/examples/rmi-iiop/
directory of your VisiBroker installation.
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.
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.
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.
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."
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:
Bank/Account.java
- The Java interface that describes the Account RMI remote interface
Bank/AccountData.java
- The Java class that is used to carry data to create an account
Bank/AccountManager.java
- The Java interface that describes the AccountManager CORBA interface
Bank/AccountManagerOperations.java
AccountImpl.java
- The servant that implements the Account interface
AccountManagerImpl.java
- The servant that implements the AccountManager interface
Client.java
- The client mainline
Server.java
- The server mainline
Follow these steps to generate the IIOP interfaces:
.java
extension in the File Name field, for example, AccountManager.java
.
package Bank; public interface AccountManager extends org.omg.CORBA.Object, AccountManagerOperations { }
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.
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:
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,
.java
extension in the File Name field, such as Server.java
.
Code Sample 2 Implementing the
Server
interface.
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,
.java
extension in the File Name field, for example, Client.java
.
Client.java
follows:
Code Sample 3 Testing the AccountManager implementation.
// 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); } } }
Server.java
, and selecting Run from the context menu.
Client.java
, and selecting Run from the context menu.
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.
This section discusses interfaces, arrays, Java classes, and extensible structs; it shows how the java2iiop
compiler can be used to handle complex data types.
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.
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.
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.
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.
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.
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.
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.
.java
extension in the File Name field, for example, AccountManager.java
, or browse to an existing file.
// a Java interface file package Bank; public interface AccountManager extends org.omg.CORBA.Object, AccountManagerOperations { }
From this point on, the steps for creating a distributed application are the same as described in "Exploring CORBA-based distributed applications in JBuilder".