Java and XML: SOAP
Pages: 1, 2, 3, 4, 5, 6, 7
Deploying the service
At this point, you've got a working deployment descriptor and a set of code artifacts to expose, and you can deploy your service. Apache SOAP comes with a utility to do this task, provided you have done the setup work. First, you need a deployment descriptor for your service, which I just talked about. Second, you need to make the classes for your service available to the SOAP server. The best way to do this is to jar up the service class from the last section:
jar cvf javaxml2.jar javaxml2/CDCatalog.class
Take this jar file and drop it into your lib/ directory (or wherever libraries are auto-loaded for your servlet engine), and restart your servlet engine.
WARNING: When you do this, you have created a snapshot of your class file. Changing the code in the
CDCatalog.javafile and recompiling it will not cause the servlet engine to pick up the changes. You'll need to re-jar the archive and copy it over to yourlib/directory each time code changes are made to ensure your service is updated. You'll also want to restart your servlet engine to make sure the changes are picked up by the engine as well.
With your service class (or classes) accessible by your SOAP
server, you can now deploy the service, using Apache SOAP's org.apache.soap.server.ServiceManager utility class:
C:\javaxml2\Ch12>java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter deploy xml\CDCatalogDD.xml
The first argument is the SOAP server and RPC router servlet, the second is the action to take, and the third is the relevant deployment descriptor. Once this has executed, verify your service was added:
(gandalf)/javaxml2/Ch12$ java org.apache.soap.server.ServiceManagerClient
http://localhost:8080/soap/servlet/rpcrouter list
Deployed Services:
urn:cd-catalog
urn:AddressFetcher
urn:xml-soap-demo-calculator
At a minimum, this should show any and all services you have available on the server. Finally, you can easily undeploy the service, as long as you know its name:
C:\javaxml2\Ch12>java org.apache.soap.server.ServiceManagerClient http://localhost:8080/soap/servlet/rpcrouter undeploy urn:cd-catalog
Every time you update your service code, you must undeploy and then redeploy to ensure the SOAP server is running the newest copy.
An RPC Client
Next up is the client. I'm going to keep things simple, and just write a couple of command-line programs that invoke SOAP-RPC. It would be impossible to try and guess your business case, so I just focus on the SOAP details and let you work out integration with your existing software. Once you have the business portion of your code working, there are some basic steps you'll take in every SOAP-RPC call:
- Create the SOAP-RPC call
- Set up any type mappings for custom parameters
- Set the URI of the SOAP service to use
- Specify the method to invoke
- Specify the encoding to use
- Add any parameters to the call
- Connect to the SOAP service
- Receive and interpret a response
That may seem like a lot, but most of the operations are one- or
two-line method invocations. In other words, talking to a SOAP service is
generally a piece of cake. Example 12-6 shows the code for the CDAdder class, which allows you to add a new CD to the catalog. Take a look at the code, and then I'll walk you through the juicy bits.
Example 12-6: The CDAdder class
package javaxml2;
import java.net.URL;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
public class CDAdder {
public void add(URL url, String title, String artist)
throws SOAPException {
System.out.println("Adding CD titled '" + title + "' by '" +
artist + "'");
// Build the Call object
Call call = new Call( );
call.setTargetObjectURI("urn:cd-catalog");
call.setMethodName("addCD");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
// Set up parameters
Vector params = new Vector( );
params.addElement(new Parameter("title", String.class, title, null));
params.addElement(new Parameter("artist", String.class, artist, null));
call.setParams(params);
// Invoke the call
Response response;
response = call.invoke(url, "");
if (!response.generatedFault( )) {
System.out.println("Successful CD Addition.");
} else {
Fault fault = response.getFault( );
System.out.println("Error encountered: " + fault.getFaultString( ));
}
}
public static void main(String[] args) {
if (args.length != 3) {
System.out.println("Usage: java javaxml2.CDAdder [SOAP server URL] " +
"\"[CD Title]\" \"[Artist Name]\"");
return;
}
try {
// URL for SOAP server to connect to
URL url = new URL(args[0]);
// Get values for new CD
String title = args[1];
String artist = args[2];
// Add the CD
CDAdder adder = new CDAdder( );
adder.add(url, title, artist);
} catch (Exception e) {
e.printStackTrace( );
}
}
}
This program captures the URL of the SOAP server to connect to,
as well as information needed to create and add a new CD to the catalog. Then,
in the add( ) method, the code creates the SOAP
Call object, on which all the interesting
interaction occurs. The target URI of the SOAP service and the method to
invoke are set on the call, both of which match up to values from the
service's deployment descriptor, from Example
12-5. Next, the encoding is set, which should always be the constant Constants.NS_URI_SOAP_ENC unless you have very unique
encoding needs.
It creates a new Vector populated
with SOAP Parameter objects. Each of these represents a parameter to the
specified method, and since the addCD( ) method
takes two String values, this is pretty simple. Supply the name of the
parameter (for use in the XML and debugging), the class for the parameter, and
the value. The fourth argument is an optional encoding, if a single parameter
needs a special encoding. For no special treatment, the value null suffices. The resulting Vector is then added to the Call object.
Once your call is set up, use the invoke( ) method on that object. The return value from this method is an org.apache.soap.Response instance, which is queried for
any problems that resulted. This is fairly self-explanatory, so I'll leave it
to you to walk through the code. Once you've compiled your client, and
followed the instructions earlier in this chapter for setting up your
classpath, run the sample as follows:
C:\javaxml2\build>java javaxml2.CDAdder
http://localhost:8080/soap/servlet/rpcrouter
"Riding the Midnight Train" "Doc Watson"
Adding CD titled 'Riding the Midnight Train' by 'Doc Watson'
Successful CD Addition
Example 12-7 is another simple class, CDLister, which lists all current CDs in the catalog. I won't go into detail on it, as it's very similar to Example 12-6, and is mainly reinforcement of what I've already talked about.
Example 12-7: The CDLister class
package javaxml2;
import java.net.URL;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.SOAPException;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
public class CDLister {
public void list(URL url) throws SOAPException {
System.out.println("Listing current CD catalog.");
// Build the Call object
Call call = new Call( );
call.setTargetObjectURI("urn:cd-catalog");
call.setMethodName("list");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);
// No parameters needed
// Invoke the call
Response response;
response = call.invoke(url, "");
if (!response.generatedFault( )) {
Parameter returnValue = response.getReturnValue( );
Hashtable catalog = (Hashtable)returnValue.getValue( );
Enumeration e = catalog.keys( );
while (e.hasMoreElements( )) {
String title = (String)e.nextElement( );
String artist = (String)catalog.get(title);
System.out.println(" '" + title + "' by " + artist);
}
} else {
Fault fault = response.getFault( );
System.out.println("Error encountered: " + fault.getFaultString( ));
}
}
public static void main(String[] args) {
if (args.length != 1) {
System.out.println("Usage: java javaxml2.CDAdder [SOAP server URL]");
return;
}
try {
// URL for SOAP server to connect to
URL url = new URL(args[0]);
// List the current CDs
CDLister lister = new CDLister( );
lister.list(url);
} catch (Exception e) {
e.printStackTrace( );
}
}
}
The only difference in this method from the CDAdder class is that the Response object has a return value (the Hashtable from the list( ) method). This is returned as a Parameter object, which allows a client to check its encoding and then extract the actual method
return value. Once that's done, the client can use the returned value like any
other Java object, and in the example simply runs through the CD catalog and
prints out each one. You can now run this additional client to see it in
action:
C:\javaxml2\build>java javaxml2.CDLister
http://localhost:8080/soap/servlet/rpcrouter
Listing current CD catalog.
'Riding the Midnight Train' by Doc Watson
'Taproot' by Michael Hedges
'Nickel Creek' by Nickel Creek
'Let it Fall' by Sean Watkins
'Aerial Boundaries' by Michael Hedges
That's really all there is to basic RPC functionality in SOAP. I'd like to push on a bit, though, and talk about a few more complex topics.