Developing CORBA Applications in Java

1. DEVELOPING CORBA APPLICATIONS

In this section, we shall describe the basic steps needed to develop a distributed application using Java IDL. The following are basic steps used to develop CORBA applications:

  • Write an interface using IDL
  • Mapping IDL interface to Java
  • Implementing the interface
  • Writing the server
  • Writing the client

We shall consider our Calculator application in this regard. This application was developed earlier using RMI technology. Let us now develop using Java-IDL and understand their relative advantages and disadvantages. This application, in spite of its simplicity, uses all the tasks required to develop almost any CORBA program that uses static invocation.

1.1. Write an interface using IDL

Our Calculator object has a single method that takes two integers and returns their sum. The interface for this object in IDL (stored in a file Calculator.idl) looks like this:

//Calculator.idl

module CalculatorApp {

interface Calculator {

long add(in long a, in long b);

};

};

A CORBA module is a container for related interfaces and declarations. It corresponds to a package in Java. The IDL compiler translates a module to a package statement in the Java code. A CORBA interface is similar to Java interface that describes the service of an object. Our calculator object provides a single service (method) that takes two integers and returns their sum.

1.2. Mapping IDL interface to Java

To develop an application using Java, this IDL interface is translated to Java code using Java’s idij compiler. The idij compiler generates a set of Java files based on option(s) passed to it. We use the following command to generate files required to both client and server:

idij -fall Calculator.idl

This generates six files CalculatorOperations.java, Calculator.java, CalculatorPOA.java, _CalculatorStub.java, CalculatorHelper.java and CalculatorHolder.java. The generated files provide standard functionality as follows:

  •  CalculatorOperations.java

This is a pure Java interface corresponding to CORBA interface and is shared by both the stubs and skeletons. It looks like this:

package CalculatorApp;

/**

* CalculatorApp/CalculatorOperations.java .
* Generated by the IDL-to-Java compiler (portable), version “3.2”
* from Calculator.idl
* Wednesday, February 5, 2014 11:58:17 AM PST

*/

public interface CalculatorOperations {

int add (int a, int b);

} // interface CalculatorOperations

It is easy to understand how the IDL statements map to the generated Java statements.

  •  Calculator.java

This is a Java interface that extends CalculatorOperations and thus corresponds to CORBA interface. It is derived from org.omg.CORBA.Object and ensures the required CORBA functionality. It looks like this:

package CalculatorApp;

/**

* CalculatorApp/Calculator.java .
* Generated by the IDL-to-Java compiler (portable), version “3.2”
* from Calculator.idl
* Wednesday, February 5, 2014 11:58:17 AM PST

*/

public interface Calculator extends CalculatorOperations, org.omg.CORBA.Object, org.omg.CORBA.portable.IDLEntity

{

} // interface Calculator

  •  CalculatorPOA.java

This is the server-side skeleton class. It provides CORBA functionality at the server-side, hence extends org.omg.PortableServer.Servant, and implements the org.omg.CORBA.portable. InvokeHandler interface and the CalculatorOperations interface.

  •  CalculatorStub.java

This class is the client-side stub. It provides CORBA functionality for the client, hence extends

org.omg.CORBA.portable.ObjectImpl and implements the Calculator interface.

  •  CalculatorHelper.java

It provides additional functionality, notably the narrow() method which casts a CORBA object reference to its proper type. It is also responsible to read/write data type to CORBA streams, and insert and extract the data type from Anys.

  •  CalculatorHolder.java

This final class, as its name implies, holds a public Calculator instance. This is used when the IDL type is an out or an inout parameter. Since these facilities are not directly available in Java, this holder class provides operations to simulate them by delegating to the methods in the Helper class for reading and writing.

1.3. Implementing the interface

To implement the interface, we write a Java class called a servant, which inherits from a class called the servant base, which is optionally generated by the idlj compiler. The servant base class is the CORBA type-specific interface between the ORB and the servant. It unmarshals incoming request, invokes servant methods, marshals results, and instructs the ORB to send the result back to the client ORB.

In our case, we write a servant class SimpleCalculator extending CalculatorPOA to provide CORBA functionality. Here is our implementation class (SimpleCalculator.java):

// SimpleCalculator.java import CalculatorApp.*;

class SimpleCalculator extends CalculatorPOA {

public int add(int x, int y) {

System.out.println(”Received: ” + a + ” and ” + b);

int result = a + b;

System.out.println(”Sent: ” + result);

return result;

}

}

A servant class defines methods of the interface it implements. Our SimpleCalculator servant defines one, the add() method. This adds two integers passed to it and returns the result. It also prints some messages to track method invocation and response.

1.4. Writing the server

The trickiest part of any CORBA-based application is to create a servant instance and export it to the ORB so that the other application can invoke methods on the servant. The Object Request Broker (ORB) is represented in Java by org.omg.CORBA.ORB class. So, we create an instance of ORB by calling its static init() method as follows:

ORB orb = ORB.init(args, null);

The command line arguments (args) are passed to the init() method so that we can set some ORB properties (such as host and port, the naming service running on) at runtime. Now, we need a Portable Object Adapter (POA). We get a reference to the existing POA, the root POA as follows:

POA rootpoa = POAHelper.narrow(orb.resolve_initial_references(”RootPOA”));

We then activate the associated POA manager using the following statement:

rootpoa.the_POAManager().activate();

The activated POA manager enables the associated POA to start processing requests. We then create an instance of the servant that performs the actual operation as follows:

SimpleCalculator cal = new SimpleCalculator();

The object is then registered (activated) with the POA and an Interoperable Object Reference (IOR) is obtained. There are many ways to do this. The typical one uses servant_to_reference() method as follows:

org.omg.CORBA.Object ref = rootpoa.servant_to_reference(cal);

This essentially activates the servant and returns an id encapsulating all necessary information required to refer to the servant. This id is used by the clients for method invocation. The invocation request eventually comes to the underlying ORB which consults the associated POA to map the object reference to the servant. These two steps may be explicitly specified as follows:

byte[] id = rootpoa.activate_object(cal);

org.omg.CORBA.Object ref = rootpoa.id_to_reference(id);

This is also equivalent to:

org.omg.CORBA.Object ref = cal._this(orb);

To invoke the add() method this remote servant, client somehow need this IOR. This IOR may be transferred physically to the client and reconverted to the object type using narrow() method of CalculatorHelper class. Alternatively, the server may bind this IOR to the naming service from where the client can get the IOR. We use the latter mechanism as it does not require the IOR to be transferred explicitly.

1.4.1. Binding IOR to naming service

To get the root context of the name server, we use the following piece of code:

org.omg.CORBA.Object objRef = orb.resolve_initial_references(”NameService”);

The resolve_initial_references() method returns a generic org.omg.CORBA.Object, which is then converted to the naming context (represented by NamingContextExt class) type using helper class as follows:

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

This refers to the root of the naming service and can be used to register IOR by a specified name as an array of NameComponent. Let us use a single string “Calculator” as the name of our calculator servant.

String name = “Calculator”;

NameComponent path[] = ncRef.to_name( name );

We register the IOR with this name as follows:

ncRef.rebind(path, ref);

We finally enable the ORB to wait for invocation requests:

orb.run();

The following is the complete code for the server (CalculatorServer.java):

// CalculatorServer.java import org.omg.CosNaming.*;

import org.omg.CosNaming.NamingContextPackage.*;

import org.omg.CORBA.*;

import org.omg.PortableServer.*;

public class CalculatorServer {

public static void main(String args[]) {

try{

// create and initialize the ORB ORB orb = ORB.init(args, null);

// get a reference to rootpoa POA rootpoa =

POAHelper.narrow(orb.resolve_initial_references(”RootPOA”));

// activate the POAManager rootpoa.the_POAManager().activate();

// create servant

SimpleCalculator cal = new SimpleCalculator();

// get IOR corresponding to the servant

org.omg.CORBA.Object ref = rootpoa.servant_to_reference(cal);

// get the root naming context org.omg.CORBA.Object objRef = orb.resolve_initial_references(“NameService”);

// Convert it to the proper type

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

//this the name of the servant String name = “Calculator”;

NameComponent path[] = ncRef.to_name( name );

// register the IOR with the name service ncRef.rebind(path, ref);

System.out.println(”CalculatorServer ready and waiting …”);

// wait for invocations from clients orb.run();

} catch (Exception e) { e.printStackTrace();

}

}

}

1.5. Writing the Client

Like server, client also creates and initializes an instance of ORB:

ORB orb = ORB.init(args, null);

Here, the command line arguments (args) are also passed to the init() method so that we can set some ORB properties (such as host and port, the naming service running on) at runtime. Before invoking a method on the remote servant, client first needs to have an IOR to that servant. The IOR may be obtained from the naming service by providing the name of the servant. The root of the naming service is obtained as follows:

org.omg.CORBA.Object objRef = orb.resolve_initial_references(”NameService”);

It is then converted to the proper type using helper class as follows:

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

The client then obtains the IOR of the calculator servant as follows:

String name = “Calculator”;

org.omg.CORBA.Object ref = ncRef.resolve_str(name);

This IOR is then converted to our Calculator type using narrow() method of CalculatorHelper class as follows:

Calculator calc = CalculatorHelper.narrow(ref);

Method invocation is the easiest part and looks exactly like local invocation. All the complications of marshalling parameters to the wire, routing them to the server-side ORB, unmarshalling, and forwarding to the servant are completely transparent to the client.

int x =2, y = 3;

int result = calc.add(x, y);

The following is the complete code for the client (caicuiatorClient.java):

import CalculatorApp.*;

import org.omg.CosNaming.*;

import org.omg.CosNaming.NamingContextPackage.*;

import org.omg.CORBA.*;

public class CalculatorClient {

public static void main(String args[]) {

try{

// create and initialize the ORB ORB orb = ORB.init(args, null);

// get the root naming context org.omg.CORBA.Object objRef = orb.resolve_initial_references(”NameService”);

// Convert it to the proper type

NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);

// get an IOR from naming service String name = “Calculator”;

org.omg.CORBA.Object ref = ncRef.resolve_str(name);

System.out.println(ref);

Calculator calc = CalculatorHelper.narrow(ref);

int x =2, y = 3;

System.out.println(”Sent + x + ” and ” + y);

int result = calc.add(x, y);

System.out.println(”Received : ” + result);

} catch (Exception e) {

e.printStackTrace();

}

}

}

2. COMPILING APPLICATIONS

Create a directory (say server) and place all server-related files, namely Caicuiator.idi, simpieCaicuiator.java and CaicuiatorServer.java. Use the following command to compile IDL file:

idij -faii Caicuiator.idi

Now, use the following command to compile all the server-related files:

javac *.java

Now, create a directory (client), probably on a different computer. Place all client-related files, namely Caicuiator.idi and CaicuiatorCiient.java. Use the following command to compile the IDL file:

idij -faii Caicuiator.idi

Now, use the following command to compile all the client-related files:

javac *.java

3. RUNNING THE APPLICATION

To run the application, a name service is required.

The server uses this name service to bind reference to the servant implementing Calculator interface. This reference is used by the client for method invocation. Java is shipped with two applications tnameserv, a transient naming service, and orbd, which is a daemon process containing a Bootstrap Service, a Transient Naming Service, a Persistent Naming Service, and a Server Manager. We shall use orbd in our example. The orbd runs by default on port 900, though -ORBinitiaiPort option may be used to override the default port. We use the port number 6789.

Open a terminal in any computer (we assume that the IP address of this computer is 172.16.4.248) in the network and use the following command to start orbd on port 67 8 9.

orbd -ORBinitiaiPort 6789

Alternatively, the transient name service tnameserv may also be used as follows:

tnameserv -ORBinitiaiPort 6789

Note that only persistent binding in the tnameserv is the initial naming context. The rest of the namespace is lost if tnameserv halts and restarts.

Now, open a terminal in a computer in the network and use the following command to start the server:

java CalculatorServer -ORBInitialHost 172.16.4.248 -ORBinitiaiPort 6789

To start the client, open a terminal in another computer in the network and use the following command:

java CalculatorClient -ORBInitialHost 172.16.4.248 -ORBinitiaiPort 6789

A sample output is shown in Figure 27.1:

Figure 27.1: Calculator application using Java IDL (i) Server (ii) Client

Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.

Leave a Reply

Your email address will not be published. Required fields are marked *