Clean up your wire protocol with SOAP, Part 2

Use Apache SOAP to create SOAP-based applications

SOAP (Simple Object Access Protocol) is a wire protocol that uses XML for data encoding. It is a minimalist specification of sorts; it only defines the most critical pieces required by a wire protocol, and purposefully omits the details of garbage collection, object activation, and so forth.

SOAP is especially important for Java developers because it adds to Java's value proposition by making platform independence and portability more interoperable. In fact, I would not be surprised if future releases of Java 2 Platform, Enterprise Edition (J2EE) make SOAP one of the mandatory wire protocols that all J2EE-compliant application servers must support. But that's enough speculation for now.

In Part 2 of this four-part series, I will introduce you to Apache's SOAP implementation.

Read the whole series on SOAP:

Introducing Apache SOAP

Apache SOAP, the Apache Software Foundation's implementation of the SOAP specification, is based on IBM's SOAP4J. Like all Apache projects, Apache SOAP is open source and available under the Apache license. I think it is currently one of the best implementations of SOAP. Though Apache SOAP conforms to version 1.1 of the SOAP specification, it lacks support for some features included in SOAP 1.1. (See Resources for a list of Apache SOAP'S available features.)

Download and install Apache SOAP

As I mentioned above, you can download Apache SOAP free of charge. (See Resources for a link.) For my Windows NT laptop, I downloaded the file soap-bin-2.0.zip, which contains Apache SOAP 2.0, the latest version as of this writing. Installing Apache SOAP is a breeze. It consists of three easy steps:

  1. Unzip the downloaded zip file: This results in the creation of a soap-2_0 subdirectory. I unzipped the contents into the root directory of my E drive, so I now have a directory E:\soap-2_0 that contains Apache SOAP.
  2. Set up your Web environment: You will need a Web server that supports servlets and JavaServer Pages (JSPs). At this point, you will fall into one the following two categories:
    • Category 1: You already have a Web server that supports servlets and JSPs, and you feel comfortable configuring it. In this case, set up your Web server so that you can point your browser to http://localhost:8080/apache-soap/. The browser will access the index.html file in the directory soap-2_0 \webapps\soap\.
    • Category 2: You don't have a Web server that supports servlets and JSPs, or you have one but don't want to fool around with it. In this case, I suggest downloading the latest version of Tomcat (3.1 at the time of this writing). (See Resources for a link.) Tomcat is yet another example of the excellent software that Apache creates and makes available for free to the software development community. Once you've downloaded the appropriate zip file (jakarta-tomcat-3.1.1.zip), unzip it. A jakarta-tomcat subdirectory will be created. Once again, I've unzipped the contents into the root directory of my E drive. Add a new context to the jakarta-tomcat\conf\server.xml configuration file like this:

      <Context path="/apache-soap" docBase="E:/soap-2_0/webapps/soap"
               debug="1" reloadable="true">
      </Context>
      

      You will need to replace E: in the docBase attribute of the Context element with the location of your soap-2_0 directory. To start Tomcat, execute the startup.bat (startup.sh for Unix) file. To shut it down, execute the shutdown.bat (shutdown.sh for Unix) file. But wait -- don't start Tomcat just yet.

  3. Set up your Web server classpath: Apache SOAP requires Apache Xerces (Java) version 1.1.2 or higher, which supports the DOM (Document Object Model) level 2 candidate recommendation that provides namespace support. I use version 1.2 of Xerces, available from Apache as Xerces-J-bin.1.2.0.zip. Unzipping this file will result in the creation of a xerces-1_2_0 subdirectory. As before, I've unzipped the file into the root directory of my E drive. You must configure your Web server to use xerces.jar (which is in the xerces-1_2_0 subdirectory) -- as opposed to any native library/jar that came with your server -- for all XML parsing. For example, Tomcat comes with an XML parser (jakarta-tomcat\lib\xml.jar) that has the DOM level 1 interfaces. Even if you put the xerces.jar in your classpath, any Java code running in Tomcat will find the wrong interfaces because the shell script/batch file that you use to run Tomcat places your classpath at the end. So you must edit tomcat.bat (tomcat.sh for Unix) in the jakarta-tomcat\bin\directory and put xerces.jar at the front of the classpath that the script builds. That is what I did in my jakarta-tomcat\bin\tomcat.bat file:

    set CLASSPATH=E:\xerces-1_2_0\xerces.jar;%CLASSPATH%;%cp%
    

    If you fall in Step 2's Category 2, then you must also configure your server to use xerces.jar.

    Regardless of which category you fall into, in addition to xerces.jar, you must also set your Web server's classpath to use soap.jar from the soap-2_0\lib\ subdirectory.

Test your installation

Now, start your Web server and make sure that your installation is correct by pointing your browser to http://localhost:8080/apache-soap/admin. You should see an administration screen as shown in the figure below.

Apache SOAP administration tool

The HelloWorld example

Now that you have set up Apache SOAP, let's put it to use with a simple HelloWorld application. In SOAP lingo, applications are called services. Typically, services are created in two steps, which may or may not be performed by the same person/organization. The first step is to define and implement the service itself in the language of choice: in this case, Java. The second step is to create clients that actually need and invoke the service. Let's look at the HelloWorld service first.

The HelloWorld service

The HelloWorld service is the Apache SOAP implementation of the example I discussed in Part 1. The service expects to obtain a person/user's name and returns a customized hello message to the caller. The code below shows the complete implementation of the HelloWorld service:

package hello;
public class HelloServer 
{
   public String sayHelloTo(String name)
   {
      System.out.println("sayHelloTo(String name)");
      return "Hello " + name + ", How are you doing?";       
   }    
}

Is that all? That is too simple to be true!

Apache SOAP makes creating services extremely easy. Basically, a service consists of the business logic, which is code that you would write regardless of how you made the service available to the rest of the world. In other words, the service is not tainted by any SOAP-related code, because the Apache SOAP infrastructure -- which consists of the rpcrouter servlet and soap.jar -- handles all the intricacies for you. Let's talk briefly about those intricacies. Just how does Apache SOAP handle remote procedure call (RPC) requests over HTTP? Understanding that will ease the creation of clients (yes, clients).

The org.apache.soap.rpc package provides support for performing RPC over SOAP in Apache SOAP. The central concept behind Apache SOAP's RPC support is that of an object ID. All Apache SOAP services must have a unique ID that acts as the service's object ID. As always, uniqueness is relative; in Apache SOAP, the uniqueness of these object IDs is related to the Apache SOAP server on which a service is deployed. That is, two services deployed on different Apache SOAP servers might have the same object ID.

A client interested in using a service sets up an org.apache.soap.rpc.Call object with the desired service's object ID, the name of the method to be invoked, and the parameters (if any) for that method. Once the Call object is set up, the client calls its invoke() method, which takes two parameters. The first is a URL to the rpcrouter servlet; in this case, the URL is http://localhost:8080/apache-soap/servlet/rpcrouter. The second parameter is the SOAPAction header. Refer to Part 1 for a refresher on the importance of the SOAPAction header and its possible values.

The invoke() method converts the Call object into an XML SOAP request (which is similar to the examples in Part 1) and sends that request to the rpcrouter servlet, as indicated by the URL. When the servlet returns a response, the invoke() method returns an org.apache.soap.rpc.Response object, which contains the service response (if any) or the fault (if a fault was generated). HTTP dictates that every request must have a response; so even if the service itself does not return anything, the rpcrouter servlet always does. Hence, the invoke() method will always return a Response object.

On the service side, the Apache SOAP server -- that is, the rpcrouter servlet -- receives the POSTed SOAP request from the client and rebuilds a Call object. The servlet uses the object ID from the rebuilt Call object to locate the object in the service manager.

The servlet then verifies the method name and invokes it on the located object. The servlet also serializes the return value from this call and sends it back in the HTTP response.

The above discussion raises an interesting question: How does Apache SOAP know how to serialize a given data type? Apache SOAP handles the marshalling and unmarshalling of Java data types to and from XML via a type-mapping registry (org.apache.soap.encoding.SOAPMappingRegistry), and via serialization (org.apache.soap.util.xml.Serializer) and deserialization (org.apache.soap.util.xml.Deserialization) interfaces that all marshallers and unmarshallers, respectively, must implement. Apache SOAP provides a number of built-in marshallers and unmarshallers that implement these interfaces. For example, you can use the org.apache.soap.encoding.soapenc.BeanSerializer class to marshall and unmarshall JavaBeans. I will show you how to use that class later in this article.

Serializers and deserializers for the Java primitive types (int, long, double, etc.) and their objectified forms (Integer, Long, Double, etc.) are preregistered in the type-mapping registry. Therefore, using Java primitives and their objectified forms as method parameters is seamless to a client. However, if a service method requires a parameter that is a JavaBean, it must manually register the BeanSerializer with the type-mapping registry. The service never performs any extra work; the client always ends up doing more. In Part 4 of this series, I will introduce you to a framework built using dynamic proxies, which will make creating clients just as easy as creating services.

Deploy the HelloWorld service

There are two ways to deploy a service in Apache SOAP: use the Web-based administration tool or deploy via the command line. Either choice will deploy your service and make it available to clients.

Use the administration tool

To use the administration tool, point your browser to http://localhost:8080/apache-soap/admin, which should bring up the window shown in Figure 1. Now click on the Deploy button in the left frame. A form with a number of fields should pop up. Not all of the fields are relevant at this time. I will explain the meaning of each field as you use it. Since you will not use all the fields in this article, a few will remain a mystery. But don't worry; by the end of Part 3, I will cover all the fields.

The ID field is used to set the object ID; as described above, the object ID is used by the SOAP infrastructure to tie an RPC request to a SOAP service. As I mentioned earlier, all Apache SOAP services must have an object ID that is unique among all the deployed services for that server. I generally use the format urn:<UniqueServiceID>, -- UniqueServiceID is the unique object ID for my service. For this example, set the ID to urn:Hello.

You use the Scope field to define the lifetime of the service instance serving the invocation request. Scope may have one of the following values:

  • page: The service instance is available until a response is sent back or the request is forwarded to another page -- if you use the standard deployment mechanism, request-forwarding is unlikely to happen.
  • request: The service instance is available for the duration of the request, regardless of forwarding.
  • session: The service instance is available for the entire session.
  • application: The same service instance is used to serve all invocations.

It is important to remember that Scope's value can have important security implications. The page and request values assure the isolation of successive calls. At the other extreme, the application value implies that all users of the SOAP server share the service instance. Astute readers may have noticed that those values are also used by the <jsp:useBean> tag in JSPs. In fact, the rpcrouter servlet was once a JSP page; that's probably how these values were chosen. For this example, set the scope to application.

Set the Methods field to a white-space-delimited list of method names that can be invoked on the service that is being deployed. Your example service only supports one method, sayHelloTo.

1 2 3 Page 1
Page 1 of 3