Sending and Receiving SOAP Messages

We have seen the building blocks of a SOAP message. The next steps are to understand how a message is built and how it is then communicated between two endpoints. To discuss these topics, we present a simple SOAP sender and a SOAP receiver using Apache SOAP and the Apache Tomcat servlet engine.[3] You may find it refreshing to discover that the additional pieces are not placed in the SOAP document by hand. The SOAP-ification is accomplished by using APIs that take care of the dirty work.

Before we look at the code of our first SOAP example, let’s run it and observe its behavior. This example consists of a simple HTTP sender class that reads an XML file, wraps it in a SOAP envelope, and sends it to a URL destination. The destination is a simple HTTP servlet that takes the contents of the message and dumps it to the screen. As we progress through concepts such as dynamic headers, SOAP with Attachments, and SOAP-RPC, these examples will become progressively more sophisticated. For now, lets run the simple one. From the command line, run the command:

java SimpleGenericHTTPSoapClient -df ./PO.xml

Tip

If this command doesn’t work, make sure that SimpleGenericHTTPSoapClient.class is on the classpath and PO.xml is in the current directory.

You should see the following output:

_________________________________________________________
Starting SimpleGenericHTTPSoapClient:
    host url        = http://localhost:8080/examples/servlet/SimpleHTTPReceive
    data file       = ./PO.xml
_________________________________________________________

Sent SOAP Message with Apache HTTP SOAP Client.
Waiting for response....
HTTP POST was successful.

In the command shell running the Tomcat servlet engine, you should see:

Received request.
-----------------------
  SOAPAction = "urn:oreilly-jaws-samples"
  Host = localhost
  Content-Type = text/xml; charset=utf-8
  Content-Length = 695
-----------------------
<?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>
<PurchaseOrder xmlns="urn:oreilly-jaws-samples">
        <shipTo country="US">
                <name>Joe Smith</name>
                <street>14 Oak Park</street>
                <city>Bedford</city>
                <state>MA</state>
                <zip>01730</zip>
        </shipTo>
        <items>
                <item partNum="872-AA">
                        <productName>Candy Canes</productName>
                        <quantity>444</quantity>
                        <price>1.68</price>
                        <comment>I want candy!</comment>
                </item>
        </items>
</PurchaseOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
____________________________

The SOAP Sender

We will soon examine the source code and some of the APIs used to create and send this message. But first, here is a listing of the simple SOAP sender in its entirety:

import java.io.*;
import java.util.*;

public class SimpleGenericHTTPSoapClient
{
    //////////////
    //Default values used if no command line parameters are set
    private static final String DEFAULT_HOST_URL =    
        "http://localhost:8080/examples/servlet/SimpleHTTPReceive";

    private static final String DEFAULT_DATA_FILENAME   = "./PO.xml";

    private static final String URI = "urn:oreilly-jaws-samples";
    //////////////
    //Member variables
    private String m_hostURL;
    //data file that will be the body content of a soap envelop
    private String m_dataFileName;

    public SimpleGenericHTTPSoapClient(String hostURL, String dataFileName)
        throws Exception
    {
        m_hostURL = hostURL;
        m_dataFileName    = dataFileName;

        System.out.println(  );
        System.out.println("_____________________________________________________");
        System.out.println("Starting SimpleGenericHTTPSoapClient:");
        System.out.println("    host url        = " + m_hostURL);
        System.out.println("    data file       = " + m_dataFileName);
        System.out.println("______________________________________________________");
        System.out.println(  );

    }

    public void sendSOAPMessage(  )
    {
        try
        {
            // get soap body to include in the SOAP envelope
            FileReader fr = new FileReader (m_dataFileName);
            javax.xml.parsers.DocumentBuilder xdb = 
                org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder(  );
            org.w3c.dom.Document doc = 
                xdb.parse (new org.xml.sax.InputSource (fr));
            if (doc == null) {
                throw new org.apache.soap.SOAPException 
                    (org.apache.soap.Constants.FAULT_CODE_CLIENT, "parsing error");
            }

            //Create the SOAP envelope
            org.apache.soap.Envelope envelope = new org.apache.soap.Envelope(  );

            // create a vector for collecting the body elements
            Vector bodyElements = new Vector(  );

            //obtain the top-level DOM element and place it into the vector
            bodyElements.add(doc.getDocumentElement (  ));

            //Create the SOAP body element
            org.apache.soap.Body body = new org.apache.soap.Body(  );
            body.setBodyEntries(bodyElements);

            //Add the SOAP body element to the envelope
            envelope.setBody(body);

            // Build the Message.
            org.apache.soap.messaging.Message msg 
                = new org.apache.soap.messaging.Message(  );

            msg.send (new java.net.URL(m_hostURL), URI, envelope);
            System.out.println("Sent SOAP Message with Apache HTTP SOAP Client.");

            // receive response from the transport and dump it to the screen
            System.out.println("Waiting for response....");
            org.apache.soap.transport.SOAPTransport st = msg.getSOAPTransport (  );
            BufferedReader br = st.receive (  );
            String line = br.readLine(  );
            if(line == null)
            {
                System.out.println("HTTP POST was successful. 
");
            }
            else
            {
               while (line != null)
               {
                    System.out.println (line);
                    line = br.readLine(  );
               }
            }
        }
        catch(Exception e)
        {
            e.printStackTrace(  );
        }
    }

    //
    // NOTE: the remainder of this deals with reading arguments
    //
    /** Main program entry point. */
    public static void main(String args[]) {

        // not relevant ...
    }
}

The main( ) method is responsible for parsing the command-line arguments, running the constructor, and calling the sendSOAPMessage( ) method:

/** Main program entry point. */
public static void main(String args[]) {

...

      // Start the HTTPSoapClient
     try
     {
         SimpleGenericHTTPSoapClient soapClient =
                            new SimpleGenericHTTPSoapClient(hostURL, dataFileName);
                        soapClient.sendSOAPMessage(  );

     }
...
}

The constructor simply stores some local member variables and prints things on the screen. All the real work happens in sendSOAPMessage( ). After reading the PO.xml document, sendSOAPMessage( ) parses the document into a DOM tree. We get a parser by calling the Apache getXMLDocBuilder( ) method, which returns a DocumentBuilder object. This parser is represented by a javax.xml.parsers.DocumentBuilder interface, which is part of the Java API for XML Processing (JAXP) package. While we chose to use the Xerces parser, the actual parser could be any parser that implements the interface. Once a suitable parser is obtained, the parser is invoked; it returns an org.w3c.dom.Document object:

public void sendSOAPMessage(  )
{
    try
    {
        // get soap body to include in the SOAP envelope
        FileReader fr = new FileReader (m_dataFileName);
        javax.xml.parsers.DocumentBuilder xdb = 
           org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder(  );
           org.w3c.dom.Document doc 
                = xdb.parse (new org.xml.sax.InputSource (fr));
        if (doc == null) {
           throw new org.apache.soap.SOAPException 
                (org.apache.soap.Constants.FAULT_CODE_CLIENT, "parsing error");
        }

Next, we need to create a SOAP envelope to hold everything and place the document into it. To associate the document with the envelope, place the top-level DOM element into a Vector, then attach the Vector to a Body object. Then place the body in the envelope using the setBody( ) method. In this simple case, only one top-level element, the <PurchaseOrder> tag, should be attached. The DOM parser has taken care of building and attaching the nodes underneath the main PurchaseOrder element:

        //Create the SOAP envelope
        org.apache.soap.Envelope envelope = new org.apache.soap.Envelope(  );

        // create a vector for collecting the body elements
        Vector bodyElements = new Vector(  );

        //obtain the top-level DOM element and place it into the vector
        bodyElements.add(doc.getDocumentElement (  ));

        //Create the SOAP body element
        org.apache.soap.Body body = new org.apache.soap.Body(  );
        body.setBodyEntries(bodyElements);

        //Add the SOAP body element to the envelope
        envelope.setBody(body);

Now that the envelope is constructed, it needs to be sent to a destination. In Apache SOAP, a Message object performs an asynchronous one-way send:

        // Build the Message.
        org.apache.soap.messaging.Message msg 
            = new org.apache.soap.messaging.Message(  );

        msg.send (new java.net.URL(m_hostURL), URI, envelope);
        System.out.println("Sent SOAP Message with Apache HTTP SOAP Client.");

The Message.send( ) method takes three parameters: a URL that represents a destination, a URI that represents the value for the SOAPAction header, and the SOAP envelope that we just built. The SOAPAction URI is really part of the binding to HTTP. When we ran the example, the value urn:oreilly-jaws-samples appeared as part of the HTTP header information that the receiving servlet dumped. Later, we will see how to use the SOAPAction to map the request into either an Apache MessageRouter service or a RPCRouter service. For now, it suffices to say that this URI is used to determine which function or service is invoked when the message reaches its destination.

The Message interface is intended for asynchronous one-way communications; Message.send( ) has a void return value. This value does not preclude it from being used in a two-way synchronous conversation. When the Message interface is implemented over a two-way transport protocol, such as HTTP, the SOAPTransport.receive( ) method can be used to receive a response:

        // receive response from the transport and dump it to the screen
        System.out.println("Waiting for response....");
        org.apache.soap.transport.SOAPTransport st = msg.getSOAPTransport (  );
        BufferedReader br = st.receive (  );
        String line = br.readLine(  );
        if(line == null)
        {
            System.out.println("HTTP POST was successful. 
");
        }
        else
        {
           while (line != null)
           {
                System.out.println (line);
                line = br.readLine(  );
           }
        }
    }

SOAPTransport.receive( ) blocks and waits for a response from the receiver. In the case of a SOAPTransport implemented over HTTP, the receive( ) method blocks and waits for an error, a timeout on the HTTP request, or even a good return code, such as a "HTTP 1.0 200 OK". It is a good idea to look for a response, even if your application is not expecting anything. In this example, the sender does not expect any application-level response from the sender, but it calls receive( ) to check for any underlying HTTP errors. In a more serious application, you would probably want to raise an alert when an error occurs and log the unexpected error to a logging service.

That’s all you need to do to send a SOAP message—at least for now. We will revisit this example as we go along, building envelope headers dynamically, adding MIME attachments, and moving on to SOAP-RPC. Before we get too far along that path, though, let’s become more familiar with our receiver.

The Simple Servlet Receiver

Here’s a listing of SimpleHTTPReceive , which is the servlet that received the SOAP message you ran in the first example. So far, this is a plain Java servlet with no knowledge of SOAP, or even of XML; it simply receives the HTTP POST request and dumps out the HTTP headers to the screen, followed by the body of the message:

import java.io.*;
import java.text.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class SimpleHTTPReceive extends HttpServlet
{
    // Treat GET requests as errors.
    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException
    {
        System.out.println("Received GET request");
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
    }

    // Our SOAP requests are going to be received as HTTP POSTS
    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException
    {
        System.out.println("____________________________");
        System.out.println("Received request.");
        System.out.println("-----------------------");
        
        // Traverse the HTTP headers and show them on the screen
        for(Enumeration enum = request.getHeaderNames(  ); 
          enum.hasMoreElements(  ); )
        {
            String header = (String)enum.nextElement(  );
            String value  = request.getHeader(header);

            System.out.println("  " + header + " = " + value);
        }
        
        System.out.println("-----------------------");
        
        // If there is anything in the body of the message, 
        // dump it to the screen as well
        if(request.getContentLength(  ) > 0)
        {
            try{
                java.io.BufferedReader reader = request.getReader(  );
                String line = null;
                while((line = reader.readLine(  )) != null)
                {
                    System.out.println(line);
                }
            }
            catch(Exception e)
            {
                System.out.println(e);
            }
        }

        System.out.println("____________________________");
        // Need this to prevent Apache SOAP from gacking
        response.setContentType("text/xml"); 
    }
}

The Servlet Receiver Becomes SOAP-Aware

While this generic servlet is useful for dumping the contents of an HTTP request, it is not very helpful for SOAP programming. Let’s expand on it a bit and explore the SOAP-aware version, HTTPReceive . First, run the example again and direct the output to the HTTPReceive:

java SimpleGenericHTTPSoapClient -df ./PO.xml -url 
    http://localhost:8080/examples/servlet/HTTPReceive

The output in the Tomcat window should be the same as it was before, with an important difference: the information is now being extracted using the inverse of the APIs that were used to construct the message. The SOAP-aware servlet looks exactly like the simple servlet until it gets to the processing of the request content. We start by getting a DocumentBuilder, just as we did in the sender:

public class HTTPReceive extends HttpServlet
{
    ...

    public void doPost(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException
    {
        ...

        if(request.getContentLength(  ) > 0)
        {
            try
            {
                java.io.BufferedReader reader = request.getReader(  );

                // get the document builder
                               javax.xml.parsers.DocumentBuilder xdb = 
                                   org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder(  );

Next, we parse that document into a DOM tree, getting a Document object as the result:

                // parse it into a DOM
                org.w3c.dom.Document doc = 
                        xdb.parse (new org.xml.sax.InputSource (reader));
                    if (doc == null) 
                     {
                         // Error occured
                         System.out.println("Doc is null!");
                             throw new org.apache.soap.SOAPException 
                             (org.apache.soap.Constants.FAULT_CODE_CLIENT, 
                             "parsing error");
                      }
                      else
                      {

In the sender, we created an envelope and populated it. In the receiver, we already have an envelope that was sent to us. The SOAP envelope is the outermost element of a SOAP document, and therefore its root element. We could just walk the DOM tree and obtain the envelope and its children directly. However, we choose to use the envelope and its associated interfaces because they help separate the details of the SOAP packaging from the raw processing of the document contents. We obtain an Envelope instance from the document object by calling unmarshall( ) , which is a static method of the Envelope class:

                    // call static method to create the envelope from the document
                    org.apache.soap.Envelope env = 
                                       org.apache.soap.Envelope.unmarshall(
                                           doc.getDocumentElement(  ));

Now that we have an envelope, we do the inverse of what we did to populate it: we get the Vector of BodyEntrys from the Envelope and get the Body from the Vector:

                    org.apache.soap.Body body = env.getBody(  );
                    java.util.Vector bodyEntries = body.getBodyEntries(  );

                    java.io.StringWriter writer = new java.io.StringWriter(  );
                    for (java.util.Enumeration e = bodyEntries.elements(  ); 
                         e.hasMoreElements(  );)
                    {
                         org.w3c.dom.Element el = 
                         (org.w3c.dom.Element)e.nextElement(  );

In this case, only one entry, the <PurchaseOrder> element, is in the Vector. Now that we have the PurchaseOrder element, we have a DOM object that is identical to the raw DOM object that we built for PO.xml (before it got SOAPified). Since the goal of this example is to write the original XML document to the screen, we call the static method DOM2Writer.serializeAsXML( ). This method serializes the PurchaseOrder element and all of its children into a StringWriter object:

                        org.apache.soap.util.xml.DOM2Writer.serializeAsXML(
                          (org.w3c.dom.Node)el, writer);
                    }
                    System.out.println(writer.toString(  ));
                }
          }
          catch(Exception e)
          {
              System.out.println(e);
          }
        }

        System.out.println("____________________________");

        response.setContentType("text/xml"); 
    }
}

Tip

Apache SOAP provides a more straightforward way to go about this process. It has the notion of a service that can be deployed using a special message router service handler. In that model, a special method signature causes the Envelope object to get passed to the listener directly. We will discuss that model in more detail later. For this introductory example, we thought it would be appropriate to show how to handle the message using the DOM and the Envelope interfaces by themselves, in the event that you work with an infrastructure that is not Tomcat-based.

Adding a Header Block

So far, the construction of the SOAP envelope has relied on reading a file from disk to obtain the XML content and then running that file through a parser to build a DOM tree. That’s an acceptable way to deal with the message body if the SOAP layer will interact with a backend system that produces and consumes raw XML documents. However, SOAP documents, or parts of them, need to be built dynamically in many cases. In this example, we construct a SOAP <Header> block dynamically. A SOAP header is not generally intended for application data but for carrying information specific to the SOAP processors that are at either endpoint in the conversation. Therefore, it is the most likely candidate for dynamic construction. To see it in action, run the following command:[4]

java GenericHTTPSoapClient -df ./PO.xml -url 
    http://localhost:8080/examples/servlet/SimpleHTTPReceive

You should see the following output in the Tomcat servlet window. Note that the header information enclosed by the <SOAP-ENV:Header> tags is not formatted nicely, but it is all there:

Received request.
-----------------------
  SOAPAction = "urn:oreilly-jaws-samples"
  Host = localhost
  Content-Type = text/xml; charset=utf-8
  Content-Length = 869
-----------------------
<?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:Header>
               <jaws:MessageHeader xmlns:jaws="urn:oreilly-jaws-samples"><From>Me</From><To>You
               </To><MessageId>9999</MessageId></jaws:MessageHeader>
               </SOAP-ENV:Header>
<SOAP-ENV:Body>
<PurchaseOrder xmlns="urn:oreilly-jaws-samples">
        <shipTo country="US">
                <name>Joe Smith</name>
                <street>14 Oak Park</street>
                <city>Bedford</city>
                <state>MA</state>
                <zip>01730</zip>
        </shipTo>
        <items>
                <item partNum="872-AA">
                        <productName>Candy Canes</productName>
                        <quantity>444</quantity>
                        <price>1.68</price>
                        <comment>I want candy!</comment>
                </item>
        </items>
</PurchaseOrder>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
____________________________

Let’s examine the code that puts the header there. Here’s an excerpt from GenericHTTPSoapClient. It is identical to its “simple” sibling, with the addition of the code in sendSoapMessage( ) . This method reads the PO.xml document and parses it, just as in the previous example:

    public void sendSOAPMessage(  )
    {
        try
        {
            // get soap body to include in the SOAP envelope
            FileReader fr = new FileReader (m_dataFileName);
            javax.xml.parsers.DocumentBuilder xdb = 
                org.apache.soap.util.xml.XMLParserUtils.getXMLDocBuilder(  );
            org.w3c.dom.Document doc = 
                xdb.parse (new org.xml.sax.InputSource (fr));
            if (doc == null) {
                throw new org.apache.soap.SOAPException 
                   (org.apache.soap.Constants.FAULT_CODE_CLIENT, "parsing error");
            }

Next, it creates a Vector for holding the header elements, similar to the way the body elements are handled:

            // create a vector for collecting the header elements
            Vector headerElements = new Vector(  );

The org.w3c.dom.Document object is used as a factory to create all nodes in the DOM tree. We create a tag with the name MessageHeader, which has its own namespace, using the namespace prefix jaws:. To create this tag, we use createElementNS( ) . It takes two parameters: the namespace URI and the qualified name (Qname) of the element:

            // Create a header element in a namespace
            org.w3c.dom.Element headerElement =
                doc.createElementNS("urn:oreilly-jaws-samples",
                "jaws:MessageHeader");

The <MessageHeader> element contains three subelements: <From>, <To>, and <MessageId>. An element is created to represent the <From> tag. Another node is created under that tag to represent the text within the tag. The objects Element, Text, and Node are all members of the tree; Element and TextNode both extend Node, which is the base object responsible for the linkage to parents, children, and siblings:

            // Create subnodes within the MessageHeader
            org.w3c.dom.Element ele = doc.createElement("From");
            org.w3c.dom.Text textNode = doc.createTextNode("Me");
            org.w3c.dom.Node tempNode = ele.appendChild(textNode);

            tempNode = headerElement.appendChild(ele);

            ele = doc.createElement("To");
            textNode = doc.createTextNode("You");
            tempNode = ele.appendChild(textNode);

            tempNode = headerElement.appendChild(ele);

            ele = doc.createElement("MessageId");
            textNode = doc.createTextNode("9999");
            tempNode = ele.appendChild(textNode);

            tempNode = headerElement.appendChild(ele);

Now that this subtree is constructed, place it in the document using the Envelope APIs. The APIs for creating the Header and attaching it to the envelope are analogous to the Body APIs that we have already seen:

                headerElements.add(headerElement);

                ...

                //Create the SOAP envelope
                org.apache.soap.Envelope envelope = new org.apache.soap.Envelope(  );
  
                //Add the SOAP header element to the envelope
                org.apache.soap.Header header = new org.apache.soap.Header(  );
                header.setHeaderEntries(headerElements);
                envelope.setHeader(header);
 
                //Create the SOAP body element
                org.apache.soap.Body body = new org.apache.soap.Body(  );
                body.setBodyEntries(bodyElements);
                //Add the SOAP body element to the envelope
                envelope.setBody(body);

            ...
            }


[3] While we use Apache SOAP, this chapter is not intended to be a tutorial on Apache SOAP or Tomcat. The intent is to talk about SOAP and its behavior whenever possible. The installation and set up of Apache SOAP, Tomcat, and the example files are not discussed. Installation is covered by the readme file included with the examples, which is available from http://www.oreilly.com/catalog/javawebserv/examples.

[4] We have removed the word “Simple” from the SimpleGenericHTTPSoapClient sender class. We explicitly tell it to send the message to the SimpleHTTPReceive servlet, which dumps the raw content of the message to the console. We’re still using SimpleHTTPReceive because we have not yet told our HTTPReceive servlet how to extract the header.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset