Chapter 7. JAX-RPC and JAXM

The Java API for XML Messaging (JAXM) and the Java API for XML-based RPC (JAX-RPC) are both part of the Java Web Services Developer Pack, Winter 01 release.[10] These APIs are a key part of Sun’s plans to integrate web services interfaces into future versions of the J2EE platform. JAXM provides a common set of Java APIs for creating, consuming, and exchanging SOAP envelopes over various transport mechanisms. It is intended mainly for a document-style exchange of information because it requires the use of low-level APIs to manipulate the SOAP envelope directly. JAX-RPC provides a means for performing RMI-like Remote Procedure Calls over SOAP. In addition, JAX-RPC provides rules for such things as client code generation, SOAP bindings, WSDL-to-Java and Java-to-WSDL mappings, and data mappings between Java and SOAP.

Fundamentally, JAXM supports synchronous communications. In fact, if you don’t run your JAXM provider in a J2EE web container (i.e., it is implemented as a message-driven bean or servlet), then it supports only synchronous communications. You don’t get asynchronous exchanges unless you use the connection provider. Don’t get hung up by the “M” versus “RPC” mislabeling. You can use JAXM to exchange document- or RPC-style SOAP messages, just as you can with JAX-RPC. The real distinction between JAXM and JAX-RPC is that JAXM forces the developer to work directly with the SOAP envelope constructs, and JAX-RPC provides a high-level, WSDL-based framework that hides details of the SOAP envelope from the developer. JAX-RPC uses WSDL to generate your messages and provides an object-oriented (i.e., RMI-like) interface to the developer. JAXM doesn’t use WSDL, so the developer must construct messages by hand and send or process them explicitly. You could make an analogy in terms of database access. You can access a database using JDBC, in which case the developer must construct SQL queries and work with the details of the database schema. Or, the developer can use JDO, which hides details of the database schema from the developer and allows the developer to work with the data as a set of Java objects.

JAXM defines the javax.xml.soap package, which includes the APIs for constructing and deconstructing a SOAP envelope directly, including a MIME-encoded multipart SWA (SOAP with attachments) message. Both JAXM and JAX-RPC share this package. Even if you only care about RPC, you should still go through the JAXM section to understand the SOAP Envelope APIs.

Java API for XML Messaging (JAXM)

JAXM consists of two main areas. The “messaging” capability provides a pattern for sending and receiving SOAP messages, with or without attachments. The SOAP packaging part provides APIs for constructing and deconstructing SOAP and MIME envelopes. Generally, the functionality is separated cleanly between the javax.xml.messaging package and javax.xml.soap packages.[11]

Where’s the Messaging?

The word "messaging” means different things to different people. For some, it refers to instant messaging or email. For others, it means reliable, asynchronous transport of critical business data, such as with Java Message Service (JMS)[12] or ebXML Message Service. In JAXM, the “M” could be any or none of those things. Like a chameleon, JAXM can take on the personality of another existing messaging protocol through the use of profiles.

Don’t infer that JAXM doesn’t support synchronous request/response interactions. JAXM can do both asynchronous, one-way communication and a synchronous request/response with the send() and call( ) methods, respectively. It can even do an RPC call. We will see an RPC call later when we revisit the GetBookPrice example using JAXM.

Simple Servlet Deployment

There’s that word again—“simple.” The bare minimum runtime requirement for JAXM is that it be deployable in a J2SE environment. This requirement means that there is no dependency on anything, except for the ability to send something over HTTP and receive it via a servlet interface, as illustrated in Figure 7-1.

JAXM invocation in a servlet-based environment

Figure 7-1. JAXM invocation in a servlet-based environment

In this type of environment, one can’t rely on having a JNDI store available. Therefore, instead of performing a lookup( ) to obtain a connection, you can get a connection by calling the static newInstance() method on the javax.xml.soap.SOAPConnectionFactory object:

SOAPConnectionFactory scf = SOAPConnectionFactory.newInstance(  );
SOAPConnection connection = scf.createConnection(  );

For sending messages in this kind of environment, we use the javax.xml.soap.SOAPConnection.call( ) method. We will encounter this method again later.

Receiving the message is fairly straightforward. To receive a message, the application implements the onMessage( ) method. In a simple servlet environment, JAXMServlet.doPost() delegates the call to onMessage( ). There is no concept of registering a message listener. In the provider situation, the provider may invoke the onMessage( ) method any way it likes, in accordance with its own particular delivery semantics:

public class ReceivingServlet extends JAXMServlet implements OnewayListener {
    ...
    public void onMessage(SOAPMessage msg) {
        System.out.println("onMessage(  ) called in receiving servlet");
        msg.writeTo(System.out);
}

The onMessage( ) method may return void or return a SOAP message, depending on whether it is intended for one-way message processing or two-way request/response. The class that implements the onMessage( ) method must extend either the OnewayListener or ReqRespListener interface to indicate its intent. Remember that you aren’t allowed to overload return values; therefore, these two versions of onMessage( ) must be defined in different interfaces.

The SOAP Package

Table 7-1 shows classes and interfaces found in the javax.xml.soap package. These items represent the Envelope API, which is shared by both JAXM and JAX-RPC. Collectively, they provide all the functionality you need for constructing and deconstructing a SOAP or a SOAP with Attachments envelope. In Chapter 3, we used Apache SOAP, portions of the org.w3c.dom.DocumentBuilder interface, and portions of the JavaMail API to accomplish the same thing.

Table 7-1. The SOAP package

Interface/class

Description

AttachmentPart

A single attachment to a SOAPMessage object

Detail

A container for DetailEntry objects

DetailEntry

The content for a Detail object, giving details for a SOAPFault object

MessageFactory

A factory used to create SOAPMessage objects

MimeHeader

An object that stores a MIME header name and its value

MimeHeaders

A container for MimeHeader objects, which represent the MIME headers present in a MIME part of a message

Name

A representation of an XML name

Node

A representation of a node (element) in a DOM representation of an XML document that provides tree manipulation methods

SOAPBody

An object that represents the contents of the SOAP body element in a SOAP message

SOAPBodyElement

An object that represents the contents in a SOAPBody object

SOAPConnection

A point-to-point connection that a client can use to send messages directly to a remote party (represented by a URL, for instance) without using a messaging provider

SOAPConnectionFactory

A factory used to create SOAPConnection objects

SOAPConstants

The definition of constants pertaining to the SOAP 1.1 protocol (e.g., URI_SOAP_ACTOR_NEXT and URI_NS_SOAP_ENCODING)

SOAPElement

An object representing the contents of a SOAPBody object, the contents of a SOAPHeader object, the content that can follow the SOAPBody object in a SOAPEnvelope object, or what follows the detail element in a SOAPFault object

SOAPElementFactory

A factory for XML fragments that eventually end up in the SOAP part

SOAPEnvelope

The container for the SOAPHeader and SOAPBody portions of a SOAPPart object

SOAPFault

An element in the SOAPBody object that contains error and/or status information

SOAPFaultElement

A representation of the contents in a SOAPFault object

SOAPHeader

A representation of the SOAP header element

SOAPHeaderElement

An object representing the contents in the SOAP header part of the SOAP envelope

SOAPMessage

The root class for all SOAP messages

SOAPPart

The container for the SOAP-specific portion of a SOAPMessage object

Text

A representation of a node whose value is text

Let’s see how these classes and interfaces fit together in some working examples.

The JAXM Sender—Request/Reply Client

This example shows how to construct a simple SOAP message and send it to a synchronous request/reply service that is expected to respond back. Let’s start by running the sender and looking at the results. Run the following command in a command window:

java SimpleJAXMClient

This command produces the following output in the sender window:

_________________________________________________________
Starting SimpleJAXMClient:
    host url        = http://localhost:8080/examples/servlet/SimpleJAXMReceive
_________________________________________________________

Sending message to URL: http://localhost:8080/examples/servlet/SimpleJAXMReceive
Received reply from: http://localhost:8080/examples/servlet/SimpleJAXMReceive
Result:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Header/><soap-env:Body><Response>This is the response</Response>
</soap-env:Body></soap-env:Envelope>

In the Tomcat servlet engine window, you should see:

On message called in receiving servlet
There are: 0 message parts
Here's the message:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><
soap-env:Header/><soap-env:Body><Text>Some Body text</Text></soap-env:Body>
</soap-env:Envelope>

Soon we’ll inspect the sending code in detail and look at the receiving code that sends the response. First, here is the sending client in its entirety:

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

public class SimpleJAXMClient {

    //Default values used if no command line parameters are set
    private static final String DEFAULT_HOST_URL = 
        "http://localhost:8080/examples/servlet/SimpleJAXMReceive";
    private static final String URI = "urn:oreilly-jaws-samples";

    //Member variables
    private String m_hostURL;

    public SimpleJAXMClient(String hostURL) throws Exception
    {
        m_hostURL = hostURL;

        System.out.println(  );
        System.out.println
            ("________________________________________________________");
        System.out.println("Starting SimpleJAXMClient:");
        System.out.println("    host url        = " + m_hostURL);
        System.out.println
            ("________________________________________________________");
        System.out.println(  );
    }

    public void sendJAXMMessage(  )
    {
        try {
            javax.xml.soap.SOAPConnectionFactory scf = 
            javax.xml.soap.SOAPConnectionFactory.newInstance(  );
            javax.xml.soap.SOAPConnection connection = scf.createConnection(  );

            // Get an instance of the MessageFactory class
            javax.xml.soap.MessageFactory mf = 
            javax.xml.soap.MessageFactory.newInstance(  );

            // Create a message from the message factory. It already contains
            // a SOAP part
            javax.xml.soap.SOAPMessage message = mf.createMessage(  );

            // Get the message's SOAP part
            javax.xml.soap.SOAPPart soapPart = message.getSOAPPart(  );

            // Get the SOAP part envelope.
            javax.xml.soap.SOAPEnvelope envelope = soapPart.getEnvelope(  );

            // Get the Body from the SOAP envelope
            javax.xml.soap.SOAPBody body = envelope.getBody(  );

            // Add an element and content to the Body
            javax.xml.soap.Name name = envelope.createName("Text");
            javax.xml.soap.SOAPBodyElement bodyElement = 
            body.addBodyElement (name);
            bodyElement.addTextNode ("Some Body text");

            // Send the message
            System.err.println("Sending message to URL: " + m_hostURL);

            // Synchronously send the message to the endpoint and wait for a reply
            javax.xml.soap.SOAPMessage reply = 
            connection.call(message, 
                new javax.xml.messaging.URLEndpoint (m_hostURL));

            System.out.println("Received reply from: " + m_hostURL);

            // Display the reply received from the endpoint
            boolean displayResult = true;
            if( displayResult ) {
               // Dump out message response.
               System.out.println("Result:");
               reply.writeTo(System.out);
           }

           connection.close(  );

        } catch(Throwable e) {
            e.printStackTrace(  );
      }
    }

    public static void main(String args[]) {

        ...
    }
}

Understanding the Simple JAXM Sender

We’ll start our examination of the code with the main( ) method. It’s not unlike the main( ) method in the other examples we have covered in the book. This method parses incoming parameters, calls the constructor, and then calls sendJAXMMessage( ) to do the real work:

    public static void main(String args[]) {

    . . .

        // Start the SimpleJAXMClient
        try
        {
            SimpleJAXMClient jaxmClient = new SimpleJAXMClient(hostURL);
            jaxmClient.sendJAXMMessage(  );
        }
    . . .
    }

sendJAXMMessage( )does all the interesting work; it creates and populates the SOAP envelope. First, we obtain a connection factory and use it to create a connection:

    public void sendJAXMMessage(  )
    {
        try {
                javax.xml.soap.SOAPConnectionFactory scf = 
                javax.xml.soap.SOAPConnectionFactory.newInstance(  );
                javax.xml.soap.SOAPConnection connection = scf.createConnection(  );

Creating the message

Next, we obtain a message factory and create an instance of a SOAP message. The simple call to MessageFactory.createMessage( ) creates the SOAP envelope with header and body elements already in it.

Here is an example:

    javax.xml.soap.MessageFactory mf = 
        javax.xml.soap.MessageFactory.newInstance(  );
    javax.xml.soap.SOAPMessage message = mf.createMessage(  );

If we were to look inside the SOAP message created so far, we would see that it already has the following contents:

                  <soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
                  <soap-env:Header/>
                  <soap-env:Body/>
                  </soap-env:Envelope>

These contents are accessible using the SOAPPart, SOAPEnvelope, SOAPHeader, and SOAPBody objects. In JAXM, a message is accessible using parts: either a SOAPPart or an AttachmentPart . The SOAPPart is the portion of the message that contains the envelope. The envelope contains the SOAPHeader and the SOAPBody:

    // Get the message's SOAP part
    javax.xml.soap.SOAPPart soapPart = message.getSOAPPart(  );

    // Get the SOAP envelope.
    javax.xml.soap.SOAPEnvelope envelope = soapPart.getEnvelope(  );

    // Get the Body from the SOAP envelope
    javax.xml.soap.SOAPBody body = envelope.getBody(  );

Adding content to the message

The pieces of the message to which we add content are SOAPHeader and SOAPBody ; we can also add content indirectly by adding attachments to the message using the AttachmentPart object. In a later example, we will show how to add attachments. For now, we’ll add some simple content to our Body. To do so, we must use the addBodyElement( ) method of the Body object. Each body element or header element must be associated with a Name object, which you obtain from the SOAPEnvelope using the createName( ) method. This method has two signatures: one takes a simple String argument, and the other requires a String, a prefix designation, and a URI designation. The latter approach is intended to create an element in a specific namespace.

After creating the Name object and using it to create a Body element, we add content by calling addTextNode( ) on the body element we just created:

    // Add an element and content to the Body
    javax.xml.soap.Name name = envelope.createName("Text");
    javax.xml.soap.SOAPBodyElement bodyElement = body.addBodyElement (name);
    bodyElement.addTextNode ("Some Body text");

Making the call

To execute the call, we use SOAPConnection.call( ) , passing it the message we created and a URLEndpoint. The URLEndpoint object, which inherits from Endpoint, specifies an absolute URL as a destination. The call blocks until a response is received:

    // Send the message
    System.err.println("Sending message to URL: " + m_hostURL);

    // Synchronously send the message to the endpoint and wait for a reply
    javax.xml.soap.SOAPMessage reply = 
        connection.call(message, 
            new javax.xml.messaging.URLEndpoint (m_hostURL));

    System.out.println("Received reply from: " + m_hostURL);

To dump the SOAP response from the called service, we use a convenience method that JAXM provides: writeTo( ) . This method sends the raw SOAP message to the specified output stream. This method even handles attachments correctly, as we’ll see later. When complete, we free resources by closing the connection explicitly:

            // Display the reply received from the endpoint
            boolean displayResult = true;
            if( displayResult ) {
                // Dump out message response.
                System.out.println("Result:");
                reply.writeTo(System.out);
            }
            connection.close(  );

Understanding the JAXM Receiver

The JAXM Receiver used in these examples is a simple request/reply servlet. There’s nothing profound here that we haven’t already covered. The servlet receives the SOAP message from the sender and responds with a SOAP message. The servlet code in the following listing creates a message factory during its initialization phase:

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

public class SimpleJAXMReceive
    extends javax.xml.messaging.JAXMServlet
    implements javax.xml.messaging.ReqRespListener {

    static javax.xml.soap.MessageFactory fac = null;

    static {
        try {
            fac = javax.xml.soap.MessageFactory.newInstance(  );
        } catch (Exception ex) { 
            ex.printStackTrace(  );
        }
    }

    public void init(javax.servlet.ServletConfig servletConfig) 
        throws javax.servlet.ServletException {
        super.init(servletConfig);
    }

Next, onMessage( ) dumps the contents of the message to the console by using writeTo( ) ; then it constructs a new message to return to the sender. Later, we will see how this same receiver and method can handle multipart messages with attachments:

    // This is the application code for handling the message. We simply display
    // the message and create and send a response.

    public javax.xml.soap.SOAPMessage onMessage
                (javax.xml.soap.SOAPMessage message) {

        System.out.println("On message called in receiving servlet");
        try {

            int count = message.countAttachments(  );
            System.out.println("There are: " + count + " message parts");

            /// Dump the raw message out
            System.out.println("Here's the message: ");
            message.writeTo(System.out);

            /// Construct and send SOAP message response
            javax.xml.soap.SOAPMessage msg = fac.createMessage(  );
            javax.xml.soap.SOAPPart part = msg.getSOAPPart(  );
            javax.xml.soap.SOAPEnvelope env = part.getEnvelope(  );
            javax.xml.soap.SOAPBody body = env.getBody(  );
            javax.xml.soap.Name name = env.createName("Response");
             javax.xml.soap.SOAPBodyElement bodyElement = 
            body.addBodyElement (name);
            bodyElement.addTextNode ("This is the response");

            return msg;

        } catch(Exception e) {
            System.out.println("Error in processing or replying to a message");
            return null;
        }
    }
}

Using JAXM for SOAP with Attachments

We will now show how to modify our sending client to use the JAXM API to add attachments and headers. To see the behavior and output of this new client, execute the following command:

java GenericJAXMSWAClient

This client assumes that files named PO.xml and attachment.txt are in the current directory. You should see the following output from the sending client:

_________________________________________________________
Starting GenericJAXMSWAClient:
    host url        = http://localhost:8080/examples/servlet/SimpleJAXMReceive
    data file       = PO.xml
    attachment      = Attachment.txt
_________________________________________________________

Sending message to URL: http://localhost:8080/examples/servlet/SimpleJAXMReceive

Received reply from: http://localhost:8080/examples/servlet/SimpleJAXMReceive
Result:
<?xml version="1.0" encoding="UTF-8"?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><s
oap-env:Header/><soap-env:Body><Response>This is the response</Response></soap-e
nv:Body></soap-env:Envelope>

The sender’s output is similar to the output from the previous example. The real difference is what is seen in the Tomcat console window. The same receiver we used before generates much different results because we’re now sending a multipart message. Note the MIME boundaries that separate the message’s parts. The first part of the message is the SOAP envelope; the next two parts are the added attachments:

On message called in receiving servlet
There are: 2 attachment parts
Here's the message:
--2023334682.1010158929328.JavaMail.chappell.nbchappell3
               Content-Type: text/xml

<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/"><s
oap-env:Header/><soap-env:Body><PurchaseOrder><shipTo country="US"><name>Joe Smi
th</name><street>14 Oak Park</street><city>Bedford</city><state>MA</state><zip>0
1730</zip></shipTo><items><item partNum="872-AA"><productName>Candy Canes</produ
ctName><quantity>444</quantity><price>1.68</price><comment>I want candy!</commen
t></item></items></PurchaseOrder></soap-env:Body></soap-env:Envelope>
--2023334682.1010158929328.JavaMail.chappell.nbchappell3
               Content-Type: text/plain

This is an attachment.
--2023334682.1010158929328.JavaMail.chappell.nbchappell3
               Content-Type: text/plain; charset=ISO-8859-1

Another Part
--2023334682.1010158929328.JavaMail.chappell.nbchappell3--

Understanding the SwA Sender

Here’s the code for the new sender, GenericJAXMSWAClient . We will break it down and walk through the new parts in a moment.

First, however, look at the whole thing:

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

public class GenericJAXMSWAClient {

    //Default values used if no command line parameters are set
    private static final String DEFAULT_DATA_FILENAME   = "PO.xml";
    private static final String DEFAULT_HOST_URL 
        = "http://localhost:8080/examples/servlet/SimpleJAXMReceive";
    private static final String URI = "urn:oreilly-jaws-samples";
    private static final String DEFAULT_ATTACHMENT_FILENAME  
        = "Attachment.txt";

    //Member variables
    private String m_hostURL;
    private String m_dataFileName;
    private String m_attachment;

    public GenericJAXMSWAClient(String hostURL, String dataFileName,
                                            String attachment) throws Exception
    {
        m_hostURL = hostURL;
        m_dataFileName    = dataFileName;
        m_attachment = attachment;

        System.out.println(  );
        System.out.println("_____________________________________________________");
        System.out.println("Starting GenericJAXMSWAClient:");
        System.out.println("    host url        = " + m_hostURL);
        System.out.println("    data file       = " + m_dataFileName);
        System.out.println("    attachment      = " + m_attachment);
        System.out.println("_____________________________________________________");
        System.out.println(  );
    }

    public void sendJAXMMessage(  )
    {
        try {

            // for doing JAXP transformations
            javax.xml.transform.TransformerFactory tFact 
                = javax.xml.transform.TransformerFactory.newInstance(  );
            javax.xml.transform.Transformer transformer 
                = tFact.newTransformer(  );

            // Create an specific URLEndpoint
            javax.xml.messaging.URLEndpoint endpoint 
                = new javax.xml.messaging.URLEndpoint(m_hostURL);

            // Create a connection 
            javax.xml.soap.SOAPConnectionFactory scf 
                = javax.xml.soap.SOAPConnectionFactory.newInstance(  );
            javax.xml.soap.SOAPConnection connection = scf.createConnection(  );

            // Get an instance of the MessageFactory class
            javax.xml.soap.MessageFactory mf 
                = javax.xml.soap.MessageFactory.newInstance(  );

            // Create a message from the message factory. 
            // It already contains a SOAP part
            javax.xml.soap.SOAPMessage message = mf.createMessage(  );

            // Get the message's SOAP part
            javax.xml.soap.SOAPPart soapPart = message.getSOAPPart(  );

            // Get the SOAP envelope from the SOAP part of the message. 
            javax.xml.soap.SOAPEnvelope envelope = soapPart.getEnvelope(  );

            // Read in the XML that will become the body in the SOAP envelope
            javax.xml.parsers.DocumentBuilderFactory dbf = 
                javax.xml.parsers.DocumentBuilderFactory.newInstance(  );
            javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder(  );
            org.w3c.dom.Document poDoc = db.parse(m_dataFileName);

            // Get the empty SOAP envelope as a generic Source
            // and put it into a DOMResult
            javax.xml.transform.Source spSrc = soapPart.getContent(  );
            javax.xml.transform.dom.DOMResult domResultEnv 
                    = new javax.xml.transform.dom.DOMResult(  );
            transformer.transform(spSrc, domResultEnv);
            
            // Now that we have the empty SOAP envelope in a DOMSource, we
            // need to put it together with the DOM we just built from the
            // input file.
            // Get the document
            org.w3c.dom.Node envelopeRoot = domResultEnv.getNode(  );
            if (envelopeRoot.getNodeType(  ) == org.w3c.dom.Node.DOCUMENT_NODE)
            {
                // Get the root element of the document.
                org.w3c.dom.Element docEl 
                    = ((org.w3c.dom.Document)envelopeRoot).getDocumentElement(  );

                // Find the <SOAP-ENV:Body> tag using the envelope namespace
                org.w3c.dom.NodeList nList 
                    = docEl.getElementsByTagNameNS(
                        javax.xml.soap.SOAPConstants.URI_NS_SOAP_ENVELOPE,"Body");
                if (nList.getLength(  ) > 0)
                {
                    // Found our <PurchaseOrder> element.  Plug it in
                    org.w3c.dom.Node bodyNode = nList.item(0);
                    org.w3c.dom.Node poRoot = poDoc.getDocumentElement(  );
                    
                    // Import the node into this document.  
                    org.w3c.dom.Node importedNode   
                        = ((org.w3c.dom.Document)envelopeRoot).importNode(poRoot, 
                                true);
                    bodyNode.appendChild(importedNode);

                    // Now shove it all back into the envelope.
                    javax.xml.transform.dom.DOMSource domSource 
                        = new javax.xml.transform.dom.DOMSource(envelopeRoot);
                    soapPart.setContent(domSource);
                }
            }
            else if (envelopeRoot.getNodeType(  ) == org.w3c.dom.Node.ELEMENT_NODE)
                System.out.println("ElementNode");
            else
                System.out.println("Unknown Node type");
                

            // Get the Header from the SOAP envelope
            javax.xml.soap.SOAPHeader header = envelope.getHeader(  );

            // Add an element and content to the Header
            javax.xml.soap.Name name 
                = envelope.createName("MessageHeader",
                    "jaxm","urn:oreilly-jaws-samples");
            javax.xml.soap.SOAPHeaderElement headerElement 
                = header.addHeaderElement(name);

            // Add an element and content to the Header
            name = envelope.createName("From");
            javax.xml.soap.SOAPElement childElement 
                = headerElement.addChildElement (name);
            childElement.addTextNode ("Me");

            // Add an element and content to the Header
            name = envelope.createName("To");
            childElement = headerElement.addChildElement(name);
            childElement.addTextNode ("You");

            // Add additional Parts to the message
            javax.activation.FileDataSource fds 
                = new javax.activation.FileDataSource(m_attachment);
            javax.activation.DataHandler dh 
                = new  javax.activation.DataHandler(fds);
            javax.xml.soap.AttachmentPart ap1 
                = message.createAttachmentPart(dh);
            message.addAttachmentPart(ap1);

            javax.xml.soap.AttachmentPart ap2 
                = message.createAttachmentPart("Another Part",
                    "text/plain; charset=ISO-8859-1");
            message.addAttachmentPart(ap2);

            // Save the changes made to the message
            message.saveChanges(  );

            System.err.println("Sending message to URL: "+ endpoint.getURL(  ));

            // Send the message to the endpoint and wait for a reply
            javax.xml.soap.SOAPMessage reply 
                = connection.call(message, endpoint);

            System.out.println("Received reply from: " + endpoint);

            // Display the reply received from the endpoint
            boolean displayResult = true;

            if( displayResult ) {
                // Document source, do a transform.
                System.out.println("Result:");
                javax.xml.soap.SOAPPart replyPart = reply.getSOAPPart(  );
                javax.xml.transform.Source src = replyPart.getContent(  );
                javax.xml.transform.stream.StreamResult result 
                  = new javax.xml.transform.stream.StreamResult( System.out );
                transformer.transform(src, result);
                System.out.println(  );
            }
            connection.close(  );

        } catch(Throwable e) {
            e.printStackTrace(  );
        }
    }

    //
    // NOTE: the remainder of this deals with reading arguments
    //
    /** Main program entry point. */

    public static void main(String args[]) {

        // Process command line, etc
   	...
        // Start the GenericJAXMSWAClient
        try
        {
            GenericJAXMSWAClient jaxmClient =
                new GenericJAXMSWAClient(hostURL, dataFileName, attachment);
            jaxmClient.sendJAXMMessage(  );

        }
        catch(Exception e)
        {
            System.out.println(e.getMessage(  ));
        }
    }
...
}

Attaching an XML fragment to the SOAP envelope

Much of the code in this chapter deals with attaching PO.xml to the SOAP envelope. When trying to port our Apache SOAP example from the previous chapter, we ran into a bit of a gotcha. Attaching an existing XML document to a SOAP envelope is reasonable—but you can’t do it, at least not simply. This flaw is by far the biggest we have encountered in the API. The MessageFactory creates an envelope with empty <Header> and <Body> elements. APIs exist for creating and manipulating elements individually, but nothing lets you take a whole document and attach it. A SOAPPart.setContent( ) method takes a document source and attaches it as the SOAP part of the message, but the document that you give it must have the envelope structure in place already. This sort of defeats the purpose. If we had corporate data that was already packaged in full SOAP envelopes, we wouldn’t need an API at all, would we?

Evidence suggests that this package was created solely for the purpose of providing an API for connecting to an ebXML infrastructure. In ebXML, the body of the envelope is intended to be a manifest and the actual payload of the message is intended to be an attachment. What if you don’t want to use it in that way?

Enough of the soapbox. Our solution converts both the envelope and the XML document into DOM trees, plugs them together, and assigns the whole thing back into the envelope. To help do this, we use a JAXP Transformer object. The javax.xml.transform package is an API in JAXP, which is mainly intended for transforming documents using XSLT stylesheets. We use it as a utility to convert the envelope and the XML document between a DOM tree representation and the javax.xml.transform.Stream datatype that is required by some JAXM envelope methods we will use:

            // for doing JAXP transformations
            javax.xml.transform.TransformerFactory tFact 
                = javax.xml.transform.TransformerFactory.newInstance(  );
            javax.xml.transform.Transformer transformer 
                = tFact.newTransformer(  );

First, read in the XML document (PO.xml) from a disk and put it into a document:

            // Read in the XML that will become the body in the SOAP envelope
            javax.xml.parsers.DocumentBuilderFactory dbf = 
                javax.xml.parsers.DocumentBuilderFactory.newInstance(  );
            javax.xml.parsers.DocumentBuilder db = dbf.newDocumentBuilder(  );
            org.w3c.dom.Document poDoc = db.parse(m_dataFileName);

After creating the message and getting its SOAPPart, getContent( ) retrieves the envelope as a generic javax.xml.transform.Source object. The Source object is the superclass of either DOMSource, SAXSource, or StreamSource. Likewise, the Result object is the superclass of DOMResult, SAXResult, and StreamResult. The transformer can take any Source object and do the right thing automatically, regardless of its subtype, and convert it to the desired Result, as shown in the following listing:

            // Get the empty SOAP Envelope as a generic Source
            // and put it into a DOMResult
            javax.xml.transform.Source spSrc = soapPart.getContent(  );
            javax.xml.transform.dom.DOMResult domResultEnv 
                    = new javax.xml.transform.dom.DOMResult(  );
            transformer.transform(spSrc, domResultEnv);

Now that we have the envelope as a DOMResult, retrieve the Document and its root element:

            org.w3c.dom.Node envelopeRoot = domResultEnv.getNode(  );
            if (envelopeRoot.getNodeType(  ) == org.w3c.dom.Node.DOCUMENT_NODE)
            {
                // Get the root element of the document.
                org.w3c.dom.Element docEl 
                    = ((org.w3c.dom.Document)envelopeRoot).getDocumentElement(  );

Next, find the <SOAP-ENV:Body> tag using the namespace of the envelope and plug in the purchaseOrder document:

                // Find the <SOAP-ENV:Body> tag using the envelope namespace
                org.w3c.dom.NodeList nList 
                    = docEl.getElementsByTagNameNS(
                        javax.xml.soap.SOAPConstants.URI_NS_SOAP_ENVELOPE,"Body");
                if (nList.getLength(  ) > 0)
                {
                    // Found our <PurchaseOrder> element.  Plug it in
                    org.w3c.dom.Node bodyNode = nList.item(0);
                    org.w3c.dom.Node poRoot = poDoc.getDocumentElement(  );

Now we have the two elements that need to be attached to one another: the SOAP Body element and the PurchaseOrder element. However, you can’t just reparent a Node from one document to another; doing so causes an error. Each element keeps track of its owning Document, which is checked by the individual routines that insert elements. Move a Node into another document properly by importing the Node and its subelements into the Document first. This operation performs a copy. The second parameter to import( ) is a Boolean that indicates whether this is a deep copy of all subnodes or just the current one. Once the nodes are imported into the Document that represents the envelope, we can simply attach the root Node as the immediate child of the <Body> element:

                    org.w3c.dom.Node importedNode   
                        = ((org.w3c.dom.Document)envelopeRoot).importNode(poRoot, 
                            true);
                    bodyNode.appendChild(importedNode);

Tip

If you use DOM level 3, there is an alternative to doing a copy. An experimental adoptNode( ) method reassigns an actual instance of a Node from one Document to another.

Now that the envelope is joined to the PO document, we take the root Node, convert it to a DOMSource, and place the whole thing into the messages’s SOAPPart:

                    javax.xml.transform.dom.DOMSource domSource 
                        = new javax.xml.transform.dom.DOMSource(envelopeRoot);
                    soapPart.setContent(domSource);

Adding a header dynamically

You will encounter no surprises here. This code is symmetric to the Body APIs that we saw at the beginning of this section:

            // Add an element and content to the Header
            javax.xml.soap.Name name 
                = envelope.createName("MessageHeader",
                    "jaxm","urn:oreilly-jaws-samples");
            javax.xml.soap.SOAPHeaderElement headerElement 
                = header.addHeaderElement(name);

            // Add an element and content to the Header
            name = envelope.createName("From");
            javax.xml.soap.SOAPElement childElement 
                = headerElement.addChildElement (name);
            childElement.addTextNode ("Me");

            // Add an element and content to the Header
            name = envelope.createName("To");
            childElement = headerElement.addChildElement(name);
            childElement.addTextNode ("You");

Adding MIME attachments

Next, let’s look at two (of many) ways to add attachments to our SOAP message. The simple sender uses both methods. No matter how you add an attachment, though, it requires two steps: call createAttachmentPart( ) with the appropriate content to get an attachment and addAttachmentPart( ) to add the attachment to the message.

The first method is arguably more complex; we use it to insert an external file (in this case, a purchase order, formatted as XML) as an attachment. First, we use the Activation Framework to create a FileDataSource that points at our external purchase order. We then convert the FileDataSource to a DataHandler ; in turn, we use the DataHandler to create our first attachment, ap1, by calling createAttachmentPart( ). Finally, we call addAttachmentPart( ) to add the attachment to the message.

Note that we don’t need to specify the content type anywhere; the content type is provided automatically by the DataHandler object:

            // Add additional Parts to the message
            javax.activation.FileDataSource fds 
                = new javax.activation.FileDataSource(m_attachment);
            javax.activation.DataHandler dh 
                = new  javax.activation.DataHandler(fds);
            javax.xml.soap.AttachmentPart ap1 
                = message.createAttachmentPart(dh);
            message.addAttachmentPart(ap1);

Perhaps a more intuitive way to create an attachment is to call createAttachmentPart( ) with the content and content type as arguments, as we’ve done here in the attachment ap2:

            javax.xml.soap.AttachmentPart ap2 
                 = message.createAttachmentPart("Another Part",
                    "text/plain; charset=ISO-8859-1");
            message.addAttachmentPart(ap2);

JAXM Profiles

JAXM is capable of morphing itself into an API that frontends any number of SOAP-based messaging frameworks through the use of "profiles.” A key part of a message profile is the ability to automate the creation of message headers and body elements that may be specific to Framework. We will describe this concept in more detail in a moment. In the 1.0 reference implementation, an ebXML MS profile and a SOAP-RP profile are provided as examples.

A profile consists of a ProviderConnectionFactory, a ProviderMetaData object that provides a list of profiles via a getSupportedProfiles( ) method, and a custom MessageFactory used to create messages specific to the profile being used. Let’s look at how these are used.

ProviderConnectionFactory

ProviderConnectionFactory allows a JAXM client to obtain a ProviderConnection to a messaging provider, such as an ebXML Message Service, or a JMS provider that supports SOAP over JMS. A ProviderConnectionFactory can be configured administratively and retrieved via a JNDI lookup( ). From there, a ProviderConnection is established:

    ctx = new InitialContext(  );
    ProviderConnectionFactory pcf = 
        (ProviderConnectionFactory)ctx.lookup("GuaranteedMessaging");
    ProviderConnection pc = pcf.createConnection(  );

Obtaining the profile via ProviderMetaData

Once the ProviderConnection is instantiated, the ProviderMetaData class can be queried to discover whether this connection supports a desired profile. The getSupportedProfiles( ) returns an array of Strings that lists the profiles that the ProviderConnection supports. For example, if ebXML is supported, the array will contain the string "ebXML":

ProviderMetaData pMetaData = pc.getMetaData(  );
String[] supportedProfiles = pMetaData.getSupportedProfiles(  );
String desiredProfile = null;

for(int i=0; i < supportedProfiles.length; i++) {
    if(supportedProfiles[i].equalsIgnoreCase("ebxml")) {
        desiredProfile = supportedProfiles[i];
        break;
    } 
}

Using the custom MessageFactory to create profile-specific messages

It is possible to plug in a custom MessageFactory that creates a message in the form expected by the transport being used. For example, a MessageFactory for an ebXML profile might create a message with a SOAP envelope, which is prepopulated with the <MessageHeader> element in the SOAP header. In the following code, the EbXMLMessageImpl is a custom extension of the javax.xml.soap.SOAPMessage:

MessageFactory mf = pc.createMessageFactory(desiredProfile);
EbXMLMessageImpl ebxmlMsg = (EbXMLMessageImpl)mf.createMessage(  );

Sending the message

You can send a message with JAXM in two ways. One way uses the ProviderConnection.send( ) method. The other uses SOAPConnection.call( ). The two methods have different purposes and different semantics. Since we are on the subject of ProviderConnection, we will talk about send( ) first and defer SOAPConnection.call( ) to Section 7.1.2.

The ProviderConnection.send( ) method assumes that one-way asynchronous sending can occur. Whether the send( ) method blocks and waits for the operation to occur depends on the provider’s underlying message delivery semantics.

If you look at the API document for ProviderConnection.send( ), you may notice that there is no way to specify a destination as part of the method signature. It assumes that the destination is established and somehow already associated with the SOAP message. There are a number of reasons for this design:

  • The JAXM API is intended to be agnostic with regard to the underlying workings of the provider. The API is designed to work with many different providers, and specifying the destination as a parameter to send( ) may not always be appropriate.

  • Whether the destination is a URI, URN, or an absolute URL is a function of the underlying infrastructure to which the JAXM API is attached. For instance, the messaging provider may provide an administration piece that maps generic URIs to specific destinations such as a URL or a JMS Topic or Queue.

  • The SOAP header element stores the destination (or destinations). The SOAP header is constructed using the Envelope APIs (just like any other part of the message).

To facilitate setting the destination, JAXM provides an Endpoint class that specifies a URI as a destination. The following code assumes that the profile-specific message has defined additional setFrom( ) and setTo( ) methods, and that the underlying infrastructure knows how to interpret the URI string used to construct the Endpoint object:

ebxmlMsg.setFrom(new Endpoint(from));
ebxmlMsg.setTo(new Endpoint(to));
pc.send(ebxmlMsg);

A strange inconsistency seems to exist in the API here: the code one would use to connect and send SOAP messages depends on how the client is deployed. When using a ProviderConnection, you send a message using the java.xml.messaging.ProviderConnection.send( ) method. Otherwise, you send the message using the javax.xml.soap.SOAPConnection.call( ) method. One could argue that the two scenarios are sufficiently different and don’t warrant consistent APIs. If you write an application that is intended to connect to a larger framework, which implies using the ProviderConnection approach, many things specific to that framework have to be coded into the application (beyond just the connect and send operations).



[10] Sun remamed the Java XML Pack to the Java Web Services Developer Pack in February 2002. The new name is confusing—the Java XML Pack still exists and remains unchanged; the Web Services Developer Pack is the XML Pack with the addition of Tomcat, Ant, and other tools. We don’t know what name Sun is likely to use in the future, so be prepared for some confusion when you go to their web site.

[11] We say “generally” because of the subtleties relating to the placement of the send() and call() methods, which we will cover in a later section.

[12] For more information on JMS, please refer to Java Message Service, by Richard Monson-Haefel and David Chappell (O’Reilly).

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

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