Apache SOAP and CORBA

SOAP is a great technology for integrating disparate systems. A SOAP interface can be added to existing applications in a number of ways, such as by wrapping an existing API or object interfaces.

This document provides the details of creating a simple CORBA client and server in Java, then creating an Apache SOAP service wrapper for the CORBA server that can then be accessed by any SOAP client. As an alternative to creating the wrapper by hand, the use of the Apache SOAP CORBA provider is also described.

The examples and descriptions below assume that Apache SOAP has been installed according to these instructions. To the extent the environment in which you work differs from the one described by those instructions, you will need to mentally adjust the text below.

Writing the CORBA code
Deploying the CORBA server
Writing the SOAP Service
Deploying the SOAP Service
Using the CORBA Provider
Resources

Writing the CORBA Code

For this document, I am reusing the "Hello, world" sample described in the J2SE SDK documentation. We start with this IDL file.

module HelloApp
{
  interface Hello
  {
    string sayHello();  // This line is the operation statement.
  };
};

This IDL file is compiled with the idlj utility: idlj -fall HelloWorld.idl. This creates a number of Java source files in the HelloApp subdirectory.

Next we write the CORBA "servant", an instance of which is created and registered with the ORB to handle method calls. This class extends an abstract base implementation created by the IDL compiler, providing an implementation of the sayHello method.

class HelloServant extends HelloApp._HelloImplBase {
  public String sayHello() {
    return "\nHello world!!\n";
  }
}

The CORBA server follows a standard pattern of initializing the ORB, creating a servant instance, registering the instance with the naming service, then staying alive without eating up CPU by synchronizing on an object monitor. The server supports the use of command-line arguments to change the host and/or port on which the naming service is listening. The default is localhost:900.

public class HelloServer {
  public static void main(String[] args) {
    try {
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);
      HelloServant servant = new HelloServant();
      orb.connect(servant);

      org.omg.CORBA.Object object =
                        orb.resolve_initial_references("NameService");
      org.omg.CosNaming.NamingContext root =
                        org.omg.CosNaming.NamingContextHelper.narrow(object);
      org.omg.CosNaming.NameComponent nc =
                        new org.omg.CosNaming.NameComponent("Hello", "");
      org.omg.CosNaming.NameComponent[] name = {nc};
      root.rebind(name, servant);

      java.lang.Object sync = new java.lang.Object();
      synchronized (sync) {
        System.out.println("Hello is ready and waiting");
        sync.wait();
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

The client also follows a standard pattern of initializing the ORB and using the naming service to locate an instance. As with the server, command-line arguments can be used to specify a non-default naming service host and port.

public class HelloClient {
  public static void main(String[] args) {
    try {
      org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(args, null);

      org.omg.CORBA.Object object =
                        orb.resolve_initial_references("NameService");
      org.omg.CosNaming.NamingContext root = 
                        org.omg.CosNaming.NamingContextHelper.narrow(object);
      org.omg.CosNaming.NameComponent nc = 
                        new org.omg.CosNaming.NameComponent("Hello", "");
      org.omg.CosNaming.NameComponent[] name = {nc};
      HelloApp.Hello hello = HelloApp.HelloHelper.narrow(root.resolve(name));
  
      System.out.println(hello.sayHello());
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
Return to top.

Deploying the CORBA Server

The first step in deploying the CORBA server is compiling it. I used a trivial ant build.xml file to accomplish this. All it does is compile all the Java source in the current directory tree.

<?xml version="1.0"?>
<project default="compile" basedir=".">
  <property name="build.dest" value="."/>

  <target name="init">
    <property name="build.compiler" value="classic"/>
    <property name="debug" value="off"/>
    <property name="src.dir" value="."/>
    <property name="build.file" value="build.xml"/>
  </target>

  <target name="compile"
          depends="init"
          description="Compiles the source files.">
    <javac srcdir="${src.dir}" destdir="${build.dest}" debug="${debug}" />
  </target>
</project>

The second step is starting the naming service. The J2SE SDK supplies two alternatives; I use the transient naming service, which on Windows 2000 I run from the command line using start tnameserv. This name server, as well as the alternative orbd, supports command line flags ORBInitialHost and ORBInitialPort so that you can specify non-default values for either of these.

The third step is to start the server: java HelloServer.

Before moving on to the SOAP wrapper, I wanted to verify that the CORBA server was working, which I did by running the client: java HelloClient.

Return to top.

Writing the SOAP Service

The SOAP service basically wraps the CORBA client code in a class that can be deployed within Apache SOAP. There is, however, one very important difference between the CORBA client and the Apache SOAP service, namely, the service cannot assume that the naming service defaults to localhost:900. The J2EE container in which Apache SOAP is deployed may have initialized the naming service to some other values. In particular, I found that when using Tomcat, the naming service lookup failed when I did not explicitly specify the initialization values to the ORB. The service class below has the values hard-coded. It would be far more elegant to provide the values as options in the deployment descriptor.

public class HelloService {
  public String sayHello() throws Exception {
    java.util.Properties props = new java.util.Properties();
    props.put("org.omg.CORBA.ORBInitialHost", "localhost");
    props.put("org.omg.CORBA.ORBInitialPort", "900");
    org.omg.CORBA.ORB orb = org.omg.CORBA.ORB.init(new String[0], props);

    org.omg.CORBA.Object object =
                      orb.resolve_initial_references("NameService");
    org.omg.CosNaming.NamingContext root = 
                      org.omg.CosNaming.NamingContextHelper.narrow(object);
    org.omg.CosNaming.NameComponent nc =
                      new org.omg.CosNaming.NameComponent("Hello", "");
    org.omg.CosNaming.NameComponent[] name = {nc};
    HelloApp.Hello hello = HelloApp.HelloHelper.narrow(root.resolve(name));

    return hello.sayHello();
  }
}

The deployment descriptor for this service is as follows.

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
             id="urn:CORBAHello">
  <isd:provider type="java"
                scope="Application"
                methods="sayHello">
    <isd:java class="HelloService" static="false"/>
    <isd:option key="SessionRequired" value="false"/>
  </isd:provider>
  <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>
  <isd:mappings>
  </isd:mappings>    
</isd:service>

The client to invoke this service is shown below.

import java.io.*;
import java.util.*;
import java.net.*;
import org.w3c.dom.*;
import org.apache.soap.util.xml.*;
import org.apache.soap.*;
import org.apache.soap.encoding.*;
import org.apache.soap.encoding.soapenc.*;
import org.apache.soap.rpc.*;

public class HelloServiceClient {
  public static void main(String[] args) throws Exception {
    if (args.length != 1
        && (args.length != 2 || !args[0].startsWith("-"))) {
      System.err.println("Usage:");
      System.err.println("  java " + HelloServiceClient.class.getName() +
                         " [-encodingStyleURI] SOAP-router-URL");
      System.exit(1);
    }

    // Process the arguments.
    int offset = 2 - args.length;
    String encodingStyleURI = args.length == 2
                              ? args[0].substring(1)
                              : Constants.NS_URI_SOAP_ENC;
    URL url = new URL(args[1 - offset]);
    SOAPMappingRegistry smr = new SOAPMappingRegistry();

    // Build the call.
    Call call = new Call();

    call.setSOAPMappingRegistry(smr);
    call.setTargetObjectURI("urn:CORBAHello");
    call.setMethodName("sayHello");
    call.setEncodingStyleURI(encodingStyleURI);

    Vector params = new Vector();
    call.setParams(params);

    // Invoke the call.
    Response resp;
    try {
      resp = call.invoke(url, "");
    } catch (SOAPException e) {
      System.err.println("Caught SOAPException (" +
                         e.getFaultCode() + "): " +
                         e.getMessage());
      return;
    }

    // Check the response.
    if (!resp.generatedFault()) {
      Parameter ret = resp.getReturnValue();
      Object value = ret.getValue();

      System.out.println(value != null ? "\n" + value : "I don't know.");
    } else {
      Fault fault = resp.getFault();

      System.err.println("Generated fault: " + fault);
    }
  }
}
Return to top.

Deploying the SOAP Service

The above code must be compiled, which ant can do with the existing build file. There are then two final steps to deploying the service: the class files must be copied to a location where Tomcat can load them, and the deployment descriptor must be deployed to Apache SOAP. The following commands accomplish these tasks.

xcopy /y /e *.class j:\jakarta-tomcat-4.0.1\webapps\soap\WEB-INF\classes
j:
cd \jakarta-tomcat-4.0.1\bin
shutdown
startup
java org.apache.soap.server.ServiceManagerClient
                http://localhost:8080/soap/servlet/rpcrouter deploy HelloService.xml

You can now run the SOAP client.

java HelloServiceClient http://localhost:8080/soap/servlet/rpcrouter
Return to top.

Using the CORBA Provider

In releases after 2.3.1, Apache SOAP includes a CORBA provider. With this, it is possible to access CORBA objects using Apache SOAP without writing any Java service code. This makes deployment very easy with a small loss in flexibility.

As before, you start with the IDL for the CORBA service and use the idlj tool to generate java files. These files must be compiled, and the class files must be copied to the webapp directory with the following commands.

xcopy /y /e *.class j:\jakarta-tomcat-4.0.1\webapps\soap\WEB-INF\classes
j:
cd \jakarta-tomcat-4.0.1\bin
shutdown
startup

The deployment descriptor for the "Hello, world" service would be as follows.

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
             id="urn:CORBAHello2">
  <isd:provider type="org.apache.soap.providers.CORBAProvider"
                scope="Application"
                methods="sayHello">
    <isd:java class="HelloService" static="false"/>
    <isd:option key="SessionRequired" value="false"/>
    <isd:option key="ORBInitialHost" value="localhost"/>
    <isd:option key="ORBInitialPort" value="900"/>
    <isd:option key="NameID" value="Hello"/>
    <isd:option key="NameKind" value=""/>
    <isd:option key="InterfaceClassName" value="HelloApp.Hello"/>
    <isd:option key="HelperClassName" value="HelloApp.HelloHelper"/>
  </isd:provider>

  <isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>

  <isd:mappings>
  </isd:mappings>    
</isd:service>

The service is deployed by running the following.

java org.apache.soap.server.ServiceManagerClient
                http://localhost:8080/soap/servlet/rpcrouter deploy HelloService2.xml

The client to invoke this service is identical to the client shown above, except the targetObjectURI must be urn:CORBAHello2 to match the deployment descriptor.

You can now run the SOAP client.

java HelloService2Client http://localhost:8080/soap/servlet/rpcrouter
Return to top.

Resources

A Detailed Comparison of CORBA, DCOM and Java/RMI, which compares coding and deployment for these three technologies.
The source for this page.


Return to top.

Copyright © 2002 Scott Nichol.
26-Aug-2002