Servlet Inter-communication with CORBA


(来源:http://java.sun.com)

with code concept contributed by Channasubbanna Prasad
(April 2001)

A Common Object Request Broker Architecture (CORBA) object can be invoked from a Web browser using CGI scripts or applets. The CGI solution is slow and inconvenient for two reasons:

  1. A new process is forked for every request.
  2. CGI scripts are stateless, and this is a problem if you want to develop, for example, a CORBA-based online shopping system.
In order for an applet to invoke a CORBA object, the browser must have an ORB or it must be compatible with JavaTM 2, Standard Edition v1.3, otherwise the applet will not run. Also, for security reasons, applets can only communicate back to the host they were downloaded from, and therefore if the CORBA object is located on another server then the applet solution will not work.

This article gives a brief overview of CORBA, then discusses servlets and demonstrates how servlets can communicate with CORBA servers. The example in this article use JavaIDL and Tomcat. If you want to see how a CORBA client can be implemented as a stand-alone application or as an applet, see Getting Started with JavaIDL.

The CORBA Programming Model

The Common Object Request Broker Architecture (or CORBA) is an industry standard specification developed by the Object Management Group (OMG) to aid in creating and using distributed objects. The software that implements the specification is called the Object Request Broker (or ORB), which is the heart of CORBA, as you can see from Figure 1.


Figure 1: A request from a client to an object implementation

There are a couple of things to note about CORBA architecture and its computing model:

  • A CORBA-based system is a collection of objects that isolates the requesters of services (clients) from the providers of services (servers) by a well-defined encapsulating interface.
  • All requests are managed by the ORB. In other words, every invocation (whether it is local or remote) of a CORBA object is passed to an ORB, as shown in Figure 1 above.

The interface between the client and server is simply a contract that specifies what kind of services are provided by the server and how they can be used by the client. CORBA interfaces are specified in a special definition language known as the Interface Definition Language (IDL). Through IDL, a particular object implementation tells its potential clients what operations are available and how they should be invoked.

From IDL definitions, CORBA objects are mapped into different programming languages like C, C++, Java, and Smalltalk. This means once you define an interface to objects in IDL, you are free to implement the object using any suitable programming language that has IDL mapping. Consequently, if you want to use that object, you can use any programming language to make remote requests to the object.

The development life cycle of CORBA-based systems is touched on throughout this article.

The Servlet Programming Model

The HTTP protocol is a request-response application protocol in which the parameters of the request must be set before the request is sent. For example, the input values in a fill-out form will be sent as part of the request when the form is submitted.

Similar to CGI, servlets support a request and response programming model. When a client sends a request to the server, the server sends the request to the servlet. The servlet then constructs a response that the server sends back to the client. Unlike CGI scripts, however, servlets run within the same process as the HTTP server.

When a client request is made, the service method is called and passed a request and response object. The servlet first determines whether the request is a GET or POST operation. It then calls one of the following methods: doGet or doPost. The doGet method is called if the request is GET, and doPost is called if the request is POST. Both doGet and doPost methods take request (HttpServletRequest) and response (HttpServletResponse).

Invoking a CORBA object from a Servlet

Once you understand basics of servlets and CORBA, you can look at an example of a simple calculator. The service:

  • Accepts two numbers
  • Multiplies them together
  • Sends the result to the client
The client in this case is a servlet that acts as a CORBA client, and the two numbers come from an HTML form handled by a servlet.

Defining the IDL Interface

The first step in developing a CORBA service is defining an IDL interface that specifies the type of operations the server will support. In this case, the operations are of a mathematical nature -- adding numbers, subtracting numbers, multiplying numbers, and so on. For the sake of simplicity, this next example only considers the multiplication operation. As an exercise, you can modify the code by adding more operations of your own.

Code sample 1 shows the IDL interface for the Multiplication object. Note that the multiply operation has three parameters. The first two are the input of the two numbers to be multiplied, and the third parameter is the output holder (the result of the multiplication). Therefore, the first two parameters are declared in (for input), and the third parameter is declared out (for output).

Code sample 1: Arith.idl

  
module Arith {  
  
   interface Multiplication {  
      void multiply(in double x,   
         in double y, out double result);  
   };  
  
};  

Implementing the Interface

Implementing the multiply operation is straightforward as you can see from the MultiplyServant class in Code sample 2. Note that the class extends the _MultiplicationImplBase class. This is an abstract class and it is inherited by the MultiplyServant to specify that this servant is a CORBA object.

Code sample 2: MultiplyServant.java

  
class MultiplyServant extends _MultiplicationImplBase {  
   public void multiply(double x, double y,   
      org.omg.CORBA.DoubleHolder result) {  
      result.value = x * y;  
    
   }  
}  

One important thing to note from Code sample 2 is the third argument of the multiply method, which is of type DoubleHolder. The reason for this is that the third argument of the multiply method in the IDL interface in Code sample 1 declared as out. In order to support the out parameter passing mode, it requires the use of an additional class. The holder class name is the Java name (with its initial letter capitalized) to which the type is mapped, with an appended "Holder" (in this case DoubleHolder). The third argument in the IDL interface is declared double and this will be mapped to a Java double. Therefore, the name of the holder class is DoubleHolder.

Each holder class has a default constructor and a public instance member. The default constructor sets the value field to the default value for the type as defined by Java: in this case it is 0 for numeric types.

To understand why holder classes are needed, consider the following operation:

void op(out double x);

In Java if you have x = 10.0, then op(x) is not allowed to change the value of x. But the out parameter passing mode means that the actual value of the parameter will be changed.

To solve this problem, holder classes are used like this:

  
double x = 10.0;  
DoubleHolder myx;  
myx.value = x;  
op(myx);  
x = myx.value;  

Developing the CORBA Server

The next step is implementing the CORBA server. The MathServer class implements a CORBA server that does the following:

  • Initializes the ORB
  • Creates a MultiplyServant object
  • Registers the object in the CORBA Naming Service (COS Naming)
  • Prints a status message
  • Waits for incoming client requests

The MathServer is shown in Code sample 3.

Code sample 3: MathServer.java

  
import org.omg.CosNaming.*; // naming service  
import org.omg.CosNaming.NamingContextPackage.*;  
import org.omg.CORBA.*;  
import java.util.*;  
import Arith.*; // the package containing the stubs and skeletons  
  
/**  
 * Math Server   
 *  
 * @author Qusay H. Mahmoud [email protected]>  
 */  
  
public class MathServer {  
   public static void main(String args[]) {  
      try{  
         // Create and initialize the ORB  
         ORB orb = ORB.init(args, null);  
        
         // Create the servant and register // it with the ORB  
         MultiplyServant mulRef =   
            new MultiplyServant();  
         orb.connect(mulRef);  
        
         // Get the root naming context  
         org.omg.CORBA.Object objRef =   
            orb.resolve_initial_references("NameService");  
         NamingContext ncRef =   
            NamingContextHelper.narrow(objRef);  
        
         // Bind the object reference in naming  
         NameComponent nc =   
            new NameComponent("Multiply", " ");  
         NameComponent path[] = {nc};  
         ncRef.rebind(path, mulRef);  
         System.out.println("server started....");  
        
         // Wait for invocations from clients  
         java.lang.Object sync =   
            new java.lang.Object();  
         synchronized(sync){  
           sync.wait();  
         }  
      } catch(Exception e) {  
         System.err.println("ERROR: " + e);  
         e.printStackTrace(System.out);  
      }    
   }  
}  

Once the MathServer has an ORB, it can register the CORBA service. It uses the COS Naming Service specified by OMG and provided with Java IDL to do the registration. It starts by getting a reference to the root of the naming service. This returns a generic CORBA object. To use it as a NamingContext object, it must be narrowed down (in other words, cast) to its proper type, and this is done using the statement:

NamingContext ncRef = NamingContextHelper.narrow(objRef);

The ncRef object is now an org.omg.CosNaming.NamingContext. You can use it to register a CORBA service with the naming service using the rebind method.

Developing the Client

Instead of developing a standalone client application to invoke the CORBA object, you can develop a Web application and a servlet that acts as a CORBA client.

In this example, an HTML form is displayed in a browser with two fields and a button. The HTML source is shown in Code sample 4.

Code sample 4: request.html <html>
<head>
<title>Servlet <->CORBA </title>
</head>
<body bgcolor="#ffffcc" text="#006666">
<center>
<h3>Servlet <==> CORBA Communication</h3> </center>
<form action="..\servlet\MathServlet" method="POST">
<center>
Number 1: <input type=text size=10 name="firstnum">
<br>
Number 2: <input type=text size=10 name="secondnum">
<input type=submit value="MultiplyNumbers">
</center>
</form>
</body>

The request method used in this example is POST, and the form is handled by a servlet called MathServlet. When Code sample 4 is loaded in a browser, it looks similar to Figure 2.


Figure 2: HTML form loaded in a browser

Now it is time to develop a servlet that will act as a CORBA client. A sample implementation, MathServlet is shown in Code sample 5. In the init method, some initializations such as instantiating and initializing the ORB, and getting a reference to the naming service, for example, are done. Once a reference to the naming service has been obtained, it can be used to access the naming service and find other services (for example the Multiply service). When the Multiply service is found, the multiply method is invoked in doPost.

Code sample 5: MathServlet.java

  
import java.io.*;  
import java.text.*;  
import java.util.*;  
import javax.servlet.*;  
import javax.servlet.http.*;  
import org.omg.CORBA.*;  
import org.omg.CosNaming.*;  
import Arith.*; // the package containing // the stubs and skeletons  
  
/**  
 * Math servlet as a CORBA client  
 *  
 * @author Qusay H. Mahmoud [email protected]>  
 */  
  
public class MathServlet extends HttpServlet {  
  
   Multiplication mulRef = null;   
   NamingContext ncRef = null;  
   NameComponent nc = null;  
  
   public void init(ServletConfig config) throws ServletException {  
      super.init(config);  
       
      try {  
         String args[] = null;  
         // 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");  
         ncRef = NamingContextHelper.narrow(objRef);  
        
         // Resolve the object reference in naming  
         nc = new NameComponent("Multiply", " ");     
            
      } catch(Exception e) {  
         System.out.println("ERROR : " + e);  
         e.printStackTrace(System.out);  
      }    
   }  
  
   public void doPost(HttpServletRequest request,  
                      HttpServletResponse response)  
        throws IOException, ServletException {  
  
      response.setContentType("text/html");  
      PrintWriter out = response.getWriter();  
      
      out.println("<html>");  
      out.println("<body>");  
      out.println("<head>");  
  
      out.println("<title>MathServlet</title>");  
      out.println("</head>");  
      out.println("<body bgcolor=\"#ffffcc\" text=\"#006666\">");  
  
	  
      String firstnumber = request.getParameter("firstnum");  
      String secondnumber = request.getParameter("secondnum");  
      double x = 0.0;  
      double y = 0.0;  
         
      if (firstnumber != null && secondnumber != null) {  
         org.omg.CORBA.DoubleHolder z = new DoubleHolder();  
  
         try {  
            x = (Double.valueOf(firstnumber)).doubleValue();            
            y = (Double.valueOf(secondnumber)).doubleValue();  
         } catch (NumberFormatException nfe) {}  
  
         try {  
            NameComponent path[] = {nc};  
            mulRef = MultiplicationHelper.narrow(ncRef.resolve(path));  
            mulRef.multiply(x, y, z);  
         } catch (Exception e) {}  
            out.println("<h3>The operation "+ x + "   
               * "+y+ " = "+z.value+"</h3>");  
  
      } else {  
         out.println("Null.");  
      }  
      out.println("</body>");  
      out.println("</html>");  
   }  
}  

Testing the application

You can compile and test the application as follows:
  • The server side:
    1. Compile the Arith.idl interface using the idlj compiler:
      prompt> idlj -fall Arith.idl
      This generates client stubs and server skeletons in a directory with the same name as the IDL module in Code sample 1 (Arith). Note that bot, the MathServer and the MathServlet must import that package: import Arith.*;

    2. Compile the MathServer class:
      prompt> javac MathServer.java

    3. Start the naming service: by default it runs on port 900. Check the next section to see how to run the naming service on a different port number.
      prompt> tnameserv

  • The client side:
    1. Compile the MathServlet. If you have Tomcat installed in c:\jakarta-tomcat, you compile like this:
      prompt> javac -classpath c:\jakarta-tomcat\lib\servlet.jar MathServlet.java

    2. Deploy the servlet. Copy it to the right location on a Web server, such as Tomcat.

    3. Load the request.html document into a Web browser, enter two numbers and click on MultiplyNumbers.

Note: You may want to modify the location of the MathServlet in the request.html form in the action section so that it points to the location of the servlet.

If everything goes well, you should see something similar to Figures 3 and 4.


Figure 3: Entering numbers in the form


Figure 4: CORBA object invoked by MathServlet

Running the application on two machines

In the example above, you see how to run the application on a single machine. But in some cases it may make more sense to run the application on two machines: one machine for the Web server and the servlet, and another machine for CORBA. There are several properties that can be used to facilitate this, two of them are: ORBInitialPort and ORBInitialHost.

If you like, you can start the naming service on a different port number as follows:

prompt> tnameserver -ORBInitialPort 1500

Now, when you start the CORBA server, you must inform it of the new port number for the naming service:

prompt> java -ORBInitialPort 1500 servername

And, when you start the client, you must instruct it as to where to find the naming service and the server:

prompt> java -ORBInitialHost servermachine -ORBInitialPort 1500 clientname

Alternatively these options can be specified at the code level using properties. So instead of initializing the ORB as:

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

It can be initialized specifying that the CORBA server machine (called turing) and the naming service's port number (to be 1500) as follows:

  
Properties props = new Properties();  
props.put("org.omg.CORBA.ORBInitialHost",   
   "turing");  
props.put("orb.omg.CORBA.ORBInitialPort",  
    "1500");  
ORB orb = ORB.init(args, props);  

Conclusion

In Web-based CORBA applications, servlets can be key components in leveraging Java and other Internet technologies. This article has shown how to invoke a CORBA object from an HTML form using a servlet that acts as a CORBA client. Invoking a CORBA object from a servlet makes sense as your CORBA-based applications for the Web would be compatible with all browsers, as long as they support HTML forms.

For more information

About the author

Qusay H. Mahmoud provides Java consulting and training services. Qusay has published dozens of articles on Java, and is the author of Distributed Programming with Java (Manning Publications, 1999).