The Apache SOAP Project is an open source Java implementation of the Simple Object Access Protocol (SOAP) v1.1. SOAP is a wire protocol that leverages HTTP or SMTP as its transport layer and XML as its data layer, to execute remote methods, known as SOAP services.
The Apache implementation of SOAP provides two methods for invoking SOAP services: a Remote Procedure Call (RPC) model and a message-based model. The RPC method, which is the focus of this article, is a synchronous technique using a client-server model to execute remote SOAP services. The message-based model uses SMTP to transport SOAP documents to and from the appropriate SOAP server. While this method is interesting, it is out of the scope of this article.
The RPC model can be defined using the following steps:
Before we begin using the Apache SOAP project, we must acquire the necessary components to execute SOAP services. Listing 1 provides a list of these items and where they can be found.
Listing 1. Components required to execute SOAP clients and services
soap.jar)http://xml.apache.org/soap/index.htmlmail.jar v1.2<TOMCAT_HOME>/common/lib/ directory.activation.jar v1.0.1<TOMCAT_HOME>/common/lib/ directory.xerces.jar v1.4.2<TOMCAT_HOME>/common/lib/ directory.Once we have all of these items, we need to extract the SOAP archive to a local directory. We then need to add each of the previously mentioned .jar files to your classpath, including soap.jar, which comes packaged with the SOAP archive. This step is very important and must not be ignored.
There are several ways to deploy a SOAP project to Tomcat. Of these methods, we are going to perform the easiest, which is simply to copy the soap.war file to the <TOMCAT_HOME>/webapps/ directory. You can find this file in the SOAP 2.2 archive.
|
Related Reading
Programming Web Services with SOAP |
Once you have moved the soap.war file into <TOMCAT_HOME>/webapps/directory, you need to make sure that each of the previously listed .jar files are in the <TOMCAT_HOME>/common/lib/ directory, excluding the soap.jar file.
After you have copied the above files to the named locations, restart Tomcat. You should now be able to access the SOAP Web application by opening your Web browser to http://localhost:8080/soap/
You should see a page similar to Figure 1.
At this point, you should also be able to use the SOAP admin tool, which can be accessed by selecting the Run link. Figure 2 shows the home page for the SOAP admin tool. From this page, you can list the current services, deploy new services, and remove previously-deployed services.
Now let's develop a simple SOAP application that acts as a simple integer calculator, with only addition and subtraction functions. To do this, we need to first develop a SOAP service for handling our calculator functions, and then create a client to access the service.
Writing an RPC-based SOAP service is a very simple process that can be broken down into three steps.
Creating a SOAP service is the simplest step of the entire "SOAPifying" process. A SOAP service can be just about any Java class that exposes public methods for invocation. The class does not need to know anything about SOAP, or even that it is being executed as a SOAP service.
The only restriction is that the method parameters of a SOAP service must be serializable. The available types that can, by default, be used as SOAP service parameters are shown in Listing 2.
Listing 2. Types that can be used as SOAP service parameters.
java.lang.Stringjava.util.Datejava.util.GregorianCalendarjava.util.Vectorjava.util.Hashtablejava.util.Mapjava.math.BigDecimaljavax.mail.internet.MimeBodyPartjava.io.InputStreamjavax.activation.DataSourcejavax.activation.DataHandlerorg.apache.soap.util.xml.QNameorg.apache.soap.rpc.Parameterjava.lang.Object (must be a JavaBean)The source listing for our service, a simple adding and subtracting calculator, is shown in its entirety in Example 1.
Example 1. Simple calculator service
package onjava;
public class CalcService {
public int add(int p1, int p2) {
return p1 + p2;
}
public int subtract(int p1, int p2) {
return p1 - p2;
}
}
As you can see, there is really nothing special about this class. It simply defines two public methods, add() and subtract(), each with a parameter list containing two native ints. To make this class available, build and copy it into the <TOMCAT_HOME>/webapps/soap/WEB-INF/classes/onjava/ directory.
|
The next step in creating a new SOAP service is to create a deployment descriptor. The deployment descriptor describes the SOAP service. This description is required for the service to be published as an Apache SOAP service. The deployment descriptor for our service is contained in Example 2.
Example 2. Deployment descriptor for a simple calculator
<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment"
id="urn:onjavaserver">
<isd:provider type="java"
scope="Application"
methods="add subtract">
<isd:java class="onjava.CalcService"/>
</isd:provider>
<isd:faultListener>org.apache.soap.server.DOMFaultListener</isd:faultListener>
</isd:service>
The deployment descriptor for our calculator service contains only three elements that we need to look at. The first element is the service element, which defines two attributes, the XML namespace and the unique ID of the service to be deployed. The ID defined in the service element must be unique, since this attribute is used to uniquely identify a published SOAP service.
The next element we need to examine is the provider element. It defines the actual implementation of the SOAP service. It does this with three attributes, each of which are defined as follows:
Listing 3. Attributes of the Provider Element
typetype attribute defines the implementation type of the SOAP service. We defined our service as a Java service.scopescope attribute defines the lifetime of the SOAP service. The possible values are page, scope, session, and application. These scope values map one-to-one with the scope values defined by the JSP specification.methodsmethods attribute defines the names of the methods that can be invoked on this service object. This list should be a space-separated list of method names.The final element of the deployment descriptor that we'll look at here is the java element. This element contains a single attribute, class, which names the fully qualified class of the named service.
Now that we have defined our SOAP service and its deployment descriptor, we can publish it so that it can start servicing requests. To do this, you need to first compile the service and make sure it is in your classpath.
After you have compiled the service, you're ready to deploy it. The Apache SOAP Project is packaged with two administration tools -- a graphical tool and a command-line tool. They both allow you to easily deploy and undeploy services to the SOAP server. The three functions provided by these tools are listed below:
deploy function allows you to deploy a new service to a SOAP server.undeploy function removes an existing SOAP service from a SOAP server.list function lists all deployed SOAP services.For our examples, we are going to use the Apache SOAP command-line tools to manage our service. SOAP command-line management functions are implemented by the org.apache.soap.server.ServiceManagerClient class. Using the ServiceManagerClient is very easy. We'll go through each of its functions in this section.
As we cover the following commands, you should note that each command references a servlet named rpcrouter. This servlet is at the core of all SOAP actions. It performs all service management and execution.
List
The list command lists all currently deployed services. To execute the list command, type the following line:
java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter list
If you have not published any other SOAP services, you should get a response that shows no deployed services. If you examine this command, you see that it executes the Java application ServiceManagerClient with two parameters: the location of the SOAP server, and the command to perform (list, in this case).
Deploy
The deploy command deploys our service to the SOAP server. This command also uses the ServiceManagerClientwith the deployment descriptor describing the SOAP service. To deploy our service, execute the following command:
java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter
deploy DeploymentDescriptor.xml
This command takes three parameters: the URL to the SOAP server, the command deploy, and the file containing our deployment descriptor. After you have executed this command, execute the list command. You should now see output listing urn:onjavaserver, which is the unique ID of our service. You can also view this service from the Web admin tool. Go to http://localhost:8080/soap/admin/index.html and select the "List" button. Typical results are shown in Figure 3.
If you select the service name, you will see the details of the service, as shown in Figure 4.
Undeploy
The undeploy command removes a previously deployed service. To execute the undeploy command, type the following line:
java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter undeploy urn:onjavaserver
Note: Do not execute this command until you have completed the remaining exercises in this article.
The undeploy command takes three parameters: the location of the SOAP server, the actual command to perform (in this case, the undeploy command), and the name of the service to remove.
|
Now that we have a service defined and deployed, let's write a client that will execute one of the service's methods. The Apache SOAP Project provides a client-side API that makes it extremely simple to create SOAP clients. An example client, which we will use to execute the subtract method of our service, can be found in Example 3.
Example 3. A SOAP Client
package onjava;
import java.io.*;
import java.net.*;
import java.util.*;
import org.apache.soap.*;
import org.apache.soap.rpc.*;
public class CalcClient {
public static void main(String[] args) throws Exception {
URL url = new URL ("http://localhost:8080/soap/servlet/rpcrouter");
Integer p1 = new Integer(args[0]);
Integer p2 = new Integer(args[1]);
// Build the call.
Call call = new Call();
call.setTargetObjectURI("urn:onjavaserver");
call.setMethodName("subtract");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Vector params = new Vector();
params.addElement(new Parameter("p1", Integer.class, p1, null));
params.addElement(new Parameter("p2", Integer.class, p2, null));
call.setParams (params);
// make the call: note that the action URI is empty because the
// XML-SOAP rpc router does not need this. This may change in the
// future.
Response resp = call.invoke(url, "" );
// Check the response.
if ( resp.generatedFault() ) {
Fault fault = resp.getFault ();
System.out.println("The call failed: ");
System.out.println("Fault Code = " + fault.getFaultCode());
System.out.println("Fault String = " + fault.getFaultString());
}
else {
Parameter result = resp.getReturnValue();
System.out.println(result.getValue());
}
}
}
This client follows a simple process that is common to most SOAP RPC clients. It first creates a URL referencing the rpcrouter (which we noted earlier) on the HTTP server localhost. This is done in the following code snippet:
URL url = new URL
("http://localhost:8080/soap/servlet/rpcrouter");
The next step performed by the client application is to parse the arguments from the command line. These values will be passed to the SOAP service in a subsequent method. The values created will be integers.
The 2002 O'Reilly Emerging Technologies Conference explored how P2P and Web services are coming together in a new Internet operating system. |
After the client has parsed to command-line arguments, it creates an instance of an org.apache.soap.rpc.RPCMessage.Call. The Call object is the main interface used when executing a SOAP RPC invocation.
To use the Call object, we first tell it which service we want to use. We do this by calling the setTargetObjectURI, passing it the name of the service that we want to execute. We then set the name of the service method we want to execute using the setMethodName() method, with the name of the method we want to execute. The next step is to set the encoding style used in the RPC call. The final step is to add the parameters that are expected when executing the named method. This is done by creating a Vector of Parameter objects and adding them to the Call object using the setParams() method. All of these steps are completed using the following code snippet:
Call call = new Call();
call.setTargetObjectURI("urn:onjavaserver");
call.setMethodName("subtract");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
Vector params = new Vector();
params.addElement(new Parameter("p1", Integer.class, p1, null));
params.addElement(new Parameter("p2", Integer.class, p2, null));
call.setParams (params);
The next step performed by the client application is to call the service method that we are interested in. This is done using invoke() with the URL we created earlier. Here is the snippet of code calling the invoke() method:
Response resp = call.invoke(url, "" );
You will notice the return value of the invoke() method is a Response object. The Response object returns two very important items -- an error code and the value returned from the executed service method. You check for an error by calling the generatedFault() method. This method will return true if there was an error returned; then you can check the getFault() method. If generatedFault() returns false, you can then get the value returned in the Response object, using the Response.getReturnValue() method. The following code snippet shows how you should process the response of an invoke():
if ( resp.generatedFault() ) {
Fault fault = resp.getFault();
System.out.println("The call failed: ");
System.out.println("Fault Code = " + fault.getFaultCode());
System.out.println("Fault String = " + fault.getFaultString());
}
else {
Parameter result = resp.getReturnValue();
System.out.println(result.getValue());
}
That is all there is to it. To test your client and service, compile the client and execute it using the following command line:
java onjava.CalcClient 98 90
Note:At this point, you should have the CalcService deployed and Tomcat should be running.
In this article we discussed the Apache SOAP Project. We described each of the steps involved when integrating SOAP into the Tomcat container. We also created a sample SOAP application demonstrating how each of the SOAP components works together.
As for the next topic we examine, I am leaving it up to you. From this point, we can go in many different directions. Some of the topics that we can discuss include:
Please let me know where you think we should go next, or if you have other related topics that you would like to see covered. You can reach me at jgoodwill@virtuas.com Please include "onjava" in the subject line.
James Goodwill is the co-Founder of Virtuas Solutions, LLC, a Colorado-based software consultancy.
Read more Using Tomcat columns.
Return to ONJava.com.
Copyright © 2007 O'Reilly Media, Inc.