Accessing and Interacting with Remote SOAP-enabled Services
By Qusay H. Mahmoud, September 2004
With the introduction of Java 2 Platform, Enterprise Edition (J2EE) 1.4, Java and Web Services are fused together in a single platform. With Web Services functionality in J2EE and the Java Web Services Developer Pack (Java WSDP), developers can now take advantage of new features to build applications that feature Web Services messaging and interaction. The key to delivering this functionality is Simple Object Access Protocol (SOAP), which has become the de facto standard for this type of functionality. It is designed to provide developers with a standard for XML message formats so that they do not need to invent a custom XML message format for every service they want to make available. SOAP is a simple, flexible, and highly extensible messaging protocol, and because it is based on XML, it is language and operating system neutral. SOAP provides the ability to exchange documents over HTTP, enabling two applications or services to exchange rich structured information without the introduction of any additional mechanisms to explicitly describe a content format, and a way to encode structured content.
Sun's SOAP with Attachments API for Java (SAAJ) provides a convenient API that can be used to create and process SOAP messages without having to write the XML yourself.
This article provides an introduction to SOAP, and:
- Discusses SOAP messages and their syntax
- Presents Sun's SOAP with Attachments API for Java (SAAJ)
- Shows how to develop SOAP applications with SAAJ
- Demonstrates the power of SAAJ through a sample application
- Shows how to access and interact with remote SOAP services such as those offered by XMethods and Google
- Gives you a flavor of the effort involved in developing SOAP clients
- Offers a generic a SOAP client and sample code that you can adapt to your own applications
Before going further, we recommend that you download the Java Web Services Developer Pack to try the examples in this article for yourself.
Please note that SOAP attachments are beyond the scope of this article, but you can learn about this topic from Chapter 9 of the J2EE 1.4 Tutorial.
Introduction to SOAP
The Simple Object Access Protocol (SOAP) is a general purpose messaging protocol. It is the de facto standard for web services messaging and interaction. Its XML-based messages can be passed through a number of protocols. In addition, it allows users to specify new data types.
SOAP can be used on the server-side and client-side. In other words, it is useful for low-level as well as high-level developers. It is useful for service-oriented infrastructure, allowing low-level distributed enterprise systems developers to turn applications into SOAP-enabled services that can be accessed, remotely, from any device. This can be accomplished by introducing a web service that understands SOAP, and the end result would be that applications can interoperate. It is useful for high-level application developers who wish to develop clients that can access and interact with SOAP-based services...such as the ones offered by XMethods, Google, and Amazon.
After reading this article, any developer can formulate a valid SOAP XML request for a particular service and be able to understand the response. Basically, a developer needs to obtain the following service details, which are described in a WSDL file: service name, method names implemented by the service, method signature of each service, and address of the service implementation that is expressed as a URI.
SOAP Messages
The basic unit of interaction between a SOAP client and a SOAP-enabled service is a message. A SOAP message is basically an XML document that consists of an envelope enclosing any number of optional headers, a body, and any optional MIME attachments as shown in Figure 1.
Figure 1: Genesis of a SOAP message
- SOAP Envelope: It is the root element of the XML document. This element falls under the
http://schemas.xmlsoap.org/soap/envelope
namespace. An envelope is uniquely identified by its namespace, and therefore processing tools can immediately determine if a given XML document is a SOAP message. But this capability or convenience comes at an expense -- you cannot send arbitrary XML documents; well, yes you can embed such documents in the Body element, but this requires XML validation with the web services engine. - SOAP Headers: They are the primary extensibility mechanism in SOAP. Using headers, SOAP messages can be extended with application-specific information like authentication, authorization, and payment processing.
- SOAP Body: It surrounds the information which is core to the SOAP message. Any number of XML elements can follow the
Body
element. This is a nice extensible feature that can help with the encoding of data in SOAP messages. - Attachments: They can be entire XML documents, XML fragments, text documents, images, or any other content with a valid MIME type. Please note that attachments are beyond the scope of this article, but you can find a couple of examples on how to work with SOAP attachments in Chapter 9 of the J2EE 1.4 Tutorial.
SOAP Message Syntax
To get an idea of what a SOAP message looks like, let's start by looking at the following simple SOAP message:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<getStockPrice/>
</Body>
</Envelope>
Here, the XML Namespace ( xmlns
) was used to identify the Envelope
as a SOAP Envelope
. Another important thing to note in the above segment of SOAP code is that the getStockPrice
element is in the default namespace. XML Namespaces can be used to specify which getStockPrice
procedure or method should be used, as in the following example:
<Envelope xmlns="http://schemas.xmlsoap.org/soap/envelope/">
<Body>
<getStockPrice
xmlns="http://finance.sometradingcompany.com" />
</Body>
</Envelope>
If you have seen SOAP messages before, you may think that the above must be missing something. Not really, except that the envelope is made the default namespace, and the SOAP messages you may have seen qualify it explicitly. If you do qualify it, the end result may look as follows, but this doesn't change the real meaning of the message.
<
SOAP-ENV: Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<
SOAP-ENV: Body>
<
m: getStockPrice xmlns
:m ="http://finance.sometradingcompany.com"/>
<
/SOAP-ENV: Body>
<
/SOAP-ENV: Envelope>
Input is passed to services through arguments. For example, the above SOAP message can be made to look more professional by adding an argument that represents the name of the stock as follows:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<m:getStockPrice xmlns:m="http://finance.sometradingcompany.com">
<symbol>SUNW</symbol>
</m:getStockPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Note that the above SOAP message has no header. An empty header can be added as follows:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<SOAP-ENV:Body>
<m:getStockPrice xmlns:m="http://finance.sometradingcompany.com"/>
<symbol>SUNW</symbol>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
A good example of using headers is the WS-I conformance claim header, so state that the message conforms to the WS-I Basic Profile 1.0:
<SOAP-ENV:Header>
<wsi:Claim conformsTo="http://ws-i.org/profiles/basic1.0/"
xmlns:wsi="http://ws-i.org/schemas/conformanceClaim/"/>
</SOAP-ENV:Header>
SOAP with Attachments API for Java (SAAJ)
The SAAJ API allows you to read, write, send, and receive SOAP messages over the Internet. The examples here show you how to create a SOAP connection, a SOAP message, populate the message, send the message, and receive the reply. This API (version 1.2) conforms to SOAP 1.1 and SOAP with Attachments, and supports the WS-I basic Profile. The APIs defines the javax.xml.soap
package that provides the classes needed for creating and populating SOAP messages, extracting content from SOAP messages, sending SOAP request-response messages, and accessing/adding/modifying parts of SOAP messages.
Creating a SOAP Message and Handling the Response
In SAAJ, SOAP messages are sent and received over a connection that is represented by a SOAPConnection
object. The following segment of code shows how to create a SOAPConnection
object; it is a good practice to close a connection once you're finished using it, since this will release resources:
SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection sc = scf.createConnection();
// close the connection
sc.close();
Note that an application can send SOAP messages directly using a SOAPConnection
object, or indirectly using a messaging provider such as JAXM. A message can be created using the MessageFactory
object as follows:
MessageFactory mf = MessageFactory.getInstance();
SOAPMessage msg = mf.createMessage();
The message created, msg
, already contains empty basic parts (envelope and header), and these can be retrieved as follows. Note that the SOAPPart
contains the envelope, which in turn contains the header and the body.
SOAPPart sp = msg.getSOAPPart();
SOAPEnvelope envelope = sp.getEnvelope();
SOAPHeader header = envelope.getHeader();
SOAPBody body = envelope.getBody();
Another way to access the parts of the message is by retrieving the header and the body directly as follows:
SOAPHeader header = msg.getSOAPHeader();
SOAPBody body = msg.getSOAPBody();
As mentioned earlier, the header is optional and thus, if you are not using it, you can delete it as follows:
header.detachNode();
The next step is to populate the SOAPBody
with the actual message to be sent. In doing so, you simply create an element specifying the message to be invoked and its argument(s). Here is an example:
Name bodyName = sf.createName("getStockPrice", "m", "http://finance.sometradingcompany.com");
SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
Name name = sf.createName("symbol");
SOAPElement symbol = bodyElement.addChildElement(name);
symbol.addTextNode("SUNW");
This segment of code will create a SOAP message that looks as follows:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<m:getStockPrice xmlns:m="http://finance.sometradingcompany.com"/>
<symbol>SUNW</symbol>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Note: As you can see, some coding is needed to generate a SOAP message, but remember that you do not have to write the XML content yourself. If, however, the SOAP message is already available in a text file, SAAJ provides the necessary APIs to populate a SOAP message simply by loading the contents of the message from an XML file (an example demonstrating how this is done is shown later).
Before a message can be sent, you should specify its destination, which can be specified as a URI string such as:
String destination = "http://...";
or
URL destination = new URL("http://...");
Now, the message is ready to be sent and this is done using the call()
method, which blocks until it receives the returned response represented in SOAPMessage
.
SOAPMessage response = sc.call(msg, destination);
The returned response, response
, is a SOAPMessage
object and therefore has the same format as the one sent.
Handling Faults
Things do not always work according to plan. For example, a client may fail to authenticate with an application (consider a developer that doesn't have a Google key to interact with Google services). SOAP defines a mechanism for error handling that is capable of identifying the source and cause of the error. In addition, it allows for error-diagnostic information to be exchanged by the parties involved in the interaction. This is accomplished through the notion of a SOAP fault. As an example, consider the following segment of a response message containing a fault caused by the authentication failure:
...
<SOAP-ENV:Body>
<SOAP-ENV:Fault>
<faultcode>Client.AuthenticationFailure</faultcode>
<faultstring>Failed to authenticate client</faultstring>
<faultactor>urn:X-SomeService:SomeGatewayProblem</facultactor>
</SOAP-ENV:Fault>
</SOAP-ENV:Body>
...
The body of the response contains a single Fault
element. This informs the client that an error has occurred, as well as some diagnostic information. The faultcode
, which must always be present, provides information that is helpful in identifying the error -- this is not for human consumption however. The faultstring
is the human-readable string representing the error message. Finally, the faultactor
specifies where in the message path the error has occurred. Here is an example of how to retrieve this information from a fault element using SAAJ:
// Using SAAJ to work with SOAP faults
if(responseBody.hasFault()) {
SOAPFault fault = responseBody.getFault();
Name code = fault.getFaultCodeAsName();
String string = fault.getFaultString();
String actor = fault.getFaultActor();
System.out.println("Fault contains: ");
System.out.println("Fault code: "+code.getQualifiedName());
System.out.println("Fault string: "+string);
if(actor != null) {
System.out.println("Actor: "+actor);
}
}
Sample Application 1: (Using XMethods' Barnes & Noble Price Quote Service)
This section provides a real-world sample application to demonstrate the power of SAAJ. The application is a SOAP client that can be used to interact with the XMethods' Barnes and Noble Price Quote service, which returns the price of a book at bn.com given its ISBN number.
To interact with this service, a SOAP request similar to the one shown in Code Sample 1 is used. Note that the ISBN would be different depending on the user's input.
Code Sample 1: request.xml
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getPrice xmlns:ns1="urn:xmethods-BNPriceCheck"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<isbn xsi:type="xsd:string">0596002432</isbn>
</ns1:getPrice>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Using SAAJ, the body of the above SOAP request can be populated in a message as follows:
Name bodyName = sf.createName("getPrice", "ns1", "urn:xmethods-BNPriceCheck");
SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
Name name = sf.createName("isbn");
SOAPElement isbn = bodyElement.addChildElement(name);
isbn.addTextNode("0596002432");
Code Sample 2 shows the full source code for the SOAP client for this application.
Code Sample 2: SoapClient.java
import java.net.*;
import javax.xml.soap.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;
public class SoapClient {
public static void main(String args[]) {
try {
//Create the connection
SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance();
SOAPConnection connection = scf.createConnection();
SOAPFactory sf = SOAPFactory.newInstance();
//Create the message
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage message = mf.createMessage();
//Create objects for the message parts
SOAPPart soapPart = message.getSOAPPart();
SOAPEnvelope envelope = soapPart.getEnvelope();
SOAPBody body = envelope.getBody();
//Populate the body of the message
Name bodyName = sf.createName("getPrice", "ns1", "urn:xmethods-BNPriceCheck");
SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
Name name = sf.createName("isbn");
SOAPElement isbn = bodyElement.addChildElement(name);
isbn.addTextNode("0596002432");
//Display the request sent (Code Sample 1)
System.out.println("SOAP Request Sent:");
message.writeTo(System.out);
//Set the destination
URL endpoint = new URL("http://services.xmethods.net:80/soap/servlet/rpcrouter");
//Send the message
SOAPMessage response = connection.call(message, endpoint);
//Close the connection
connection.close();
//Display the response received (Code Sample 3)
System.out.println("SOAP Response Received:");
//Create a transformer
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
//Retrieve content of the response
Source content = response.getSOAPPart().getContent();
//Display it on the console
StreamResult result = new StreamResult(System.out);
transformer.transform(content, result);
System.out.println();
} catch(Exception e) {
System.out.println(e.getMessage());
}
}
}
Once the message is populated and sent to its destination, the call()
method blocks to receive a response. The response is received in a SOAPMessage
that will have the same structure as the SOAP request, and therefore you can process it to retrieve the information needed (price). In this example, a transformer has been created to retrieve the content of the reply and display it as received. The response received is shown in Code Sample 3.
Code Sample 3: response.xml
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getPriceResponse xmlns:ns1="urn:xmethods-BNPriceCheck"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<return xsi:type="xsd:float">34.95</return>
</ns1:getPriceResponse>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
A SOAP message can be processed by retrieving the child elements and iterating over them as follows. In this example, there is only one child element.
SOAPBody responseBody = reply.getSOAPBody();
Iterator iterator = responseBody.getChildElements();
while(iterator.hasNext()) {
bodyElement = (SOAPBodyElement) iterator.next();
String price = bodyElement.getValue();
System.out.println("The price for book with ISBN: .... is: "+ price);
}
To experiment with this sample real-world application, do the following. Here I assume that you already have installed a copy of the J2EE 1.4 SDK or Java WSDP 1.4 (in order to have access to SAAJ as well as the build tool asant) as well as a copy of the J2EE 1.4 Tutorial. In addition, it is assumed that the J2EE 1.4 Tutorial is installed at this directory c:\j2eetutorial14
. Note that you do not need to start a J2EE application server in order to run this application.
- Create a directory (
myapp
) underc:\j2eetutorial14\examples\saaj
- Create a subdirectory (call it
src
) undermyapp
- Copy the above .java file and save it under the
src
subdirectory - Copy the
build.xml
tomyapp
(Use your browser' View Source to display and copy the file) - Change directory to
c:\j2eetutorial14\examples\saaj\myapp
- Build the application using the command:
asant build
- Run the application using the command:
asant run
. The response you receive will be similar to Code Sample 3 above
Sample Application 2 (Using Google's Search Service)
Google has released a set of APIs that can be used to access Google services (search for content, spell checking) from within your own web services. Basically, they have made their services SOAP-enabled.
To use the Google search service, a SOAP request such as the one in Code Sample 6 is used. Note that in order to use Google's service, you must create a Google account to obtain a license key. As you can see from Code Sample 4, the license key is sent as part of the request. Here, I have replaced my license key with 0's. Also, note that in this example, a search for 'Learning Wireless Java' is being requested.
Code Sample 4: GoogleSearch.xml
<?xml version="1.0" encoding="UTF-8" ?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:doGoogleSearch xmlns:ns1="urn:GoogleSearch"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<key xsi:type="xsd:string">00000000000000000000000000000000</key>
<q xsi:type="xsd:string">Learning Wireless Java</q>
<start xsi:type="xsd:int">0</start>
<maxResults xsi:type="xsd:int">10</maxResults>
<filter xsi:type="xsd:boolean">true</filter>
<restrict xsi:type="xsd:string" />
<safeSearch xsi:type="xsd:boolean">false</safeSearch>
<lr xsi:type="xsd:string" />
<ie xsi:type="xsd:string">latin1</ie>
<oe xsi:type="xsd:string">latin1</oe>
</ns1:doGoogleSearch>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Populating a message, manually, with the request shown in Code Sample 4 involves a significant amount of coding. In such situations, it is easier to populate the message by loading the request from a file. For example, the SOAP request in Code Sample 4 can be saved in a file (GoogleSearch.xml) and then loaded from that file to populate the message. This can be easily done using the segment of code shown in Code Sample 5.
Code Sample 5: Reading the Message From a File
StreamSource msg = new StreamSource(new FileInputStream("c:/request.xml"));
soapPart.setContent(msg);
which should be used to replace the following lines of code (Code Sample 6) in Code Sample 2 above.
Code Sample 6: Populating a Message Manually
//Populate the body of the message
Name bodyName = sf.createName("getPrice", "ns1", "urn:xmethods-BNPriceCheck");
SOAPBodyElement bodyElement = body.addBodyElement(bodyName);
Name name = sf.createName("isbn");
SOAPElement isbn = bodyElement.addChildElement(name);
isbn.addTextNode("0596002432");
To experiment with this, create a Google account in order to obtain a license key, then:
- Copy Code Sample 5 (
GoogleSearch.xml
) and save it underc:\
- Edit
GoogleSearch.xml
and replace the license key with your own key - Edit
SoapClient.java
and replace the segment of code shown in Code Sample 6 with the one shown in Code Sample 5 - Edit
SoapClient.java
and change the destination of the message fromhttp://services.xmethods.net:80/soap/servlet/rpcrouter
tohttp://api.google.com/search/beta2
- Compile the application (asant build) and run it (asant run)
Send/Receive SOAP Messages with Sun's JAX-RPC
Sun's JAX-RPC is a Java API for XML-based Remote Procedure Calls (RPC) that can be used to easily develop Web services and web services clients as shown in this article. The advantage of JAX-RPC is that it hides the complexity of SOAP messages from the developer. Using JAX-RPC, the developer doesn't need to worry about constructing SOAP messages on his/her own but instead can concentrate on the application logic.
Conclusion
The Simple Object Access Protocol (SOAP) is an XML-based protocol on which web services in general are based. Sun's SOAP with Attachments API for Java (SAAJ) provides an API that can be used to easily construct SOAP messages without having to create the XML yourself. This article presented a tutorial on SOAP and SAAJ. The sample code presented in the article show how easy it is to use SAAJ to develop client to access and invoke remote SOAP-based web services. As an exercise, you may want to use SAAJ to the server side to develop services that generate SOAP responses.
One of the sample applications developed in this article is a SOAP client (using SAAJ) for the XMethods' Barnes & Noble Price Quote Web service. As an exercise, you may want to write a JAX-RPC client for this service.
For More Information
- SOAP (Simple Object Access Protocol)
- SOAP Messages with Attachments
- Java WSDP 1.4 includes SAAJ 1.2.1
- J2EE 1.4 Tutorial (see Chapter 9 on SAAJ)
- Amazon Web Services
- Google Web APIs
- JAX-RPC
Acknowledgments
Special thanks to Rama Pulavarthi and Dennis MacNeil of Sun Microsystems, whose feedback helped me improve this article.