Sun Java System Message Queue 4.1 Developer's Guide for Java Clients

SOAP Messaging Models and Examples

This section explains how you use SAAJ to send and receive a SOAP message. It is also possible to construct a SOAP message using SAAJ and to send it as the payload of a JMS message. For information, see Integrating SOAP and Message Queue.

SOAP Messaging Programming Models

This section provides a brief summary of the programming models used in SOAP messaging using SAAJ.

A SOAP message is sent to an endpoint by way of a point-to-point connection (implemented by the SOAPConnection class).

You use point-to-point connections to establish a request-reply messaging model. The request-reply model is illustrated in Figure 5–6.

Figure 5–6 Request-Reply Messaging

Diagram showing the client sending a message to an endpoint
that receives the message, processes it, and then returns to the sender.

Using this model, the client does the following:

It is assumed that the client will ignore the SOAPMessage object returned by the call method because the only reason this object is returned is to unblock the client.

The SOAP service listening for a request-reply message uses a ReqRespListener object to receive messages.

For a detailed example of a client that does point-to-point messaging, see Writing a SOAP Client.

Working with Attachments

If a message contains any data that is not XML, you must add it to the message as an attachment. A message can have any number of attachment parts. Each attachment part can contain anything from plain text to image files.

To create an attachment, you must create a URL object that specifies the location of the file that you want to attach to the SOAP message. You must also create a data handler that will be used to interpret the data in the attachment. Finally, you need to add the attachment to the SOAP message.

To create and add an attachment part to the message, you need to use the JavaBeans Activation Framework (JAF) API. This API allows you to determine the type of an arbitrary piece of data, encapsulate access to it, discover the operations available on it, and activate a bean that can perform these operations. You must include the activation.jar library in your application code in order to work with the JavaBeans Activation Framework.

ProcedureTo Create and Add an Attachment

  1. Create a URL object and initialize it to contain the location of the file that you want to attach to the SOAP message.


    URL url = new URL("http://wombats.com/img.jpg");
  2. Create a data handler and initialize it with a default handler, passing the URL as the location of the data source for the handler.


    DataHandler dh = new DataHandler(url);
  3. Create an attachment part that is initialized with the data handler containing the URL for the image.


    AttachmentPart ap1 = message.createAttachmentPart(dh);
  4. Add the attachment part to the SOAP message.


    myMessage.addAttachmentPart(ap1);

    After creating the attachment and adding it to the message, you can send the message in the usual way.

    If you are using JMS to send the message, you can use the SOAPMessageIntoJMSMessage conversion utility to convert a SOAP message that has an attachment into a JMS message that you can send to a JMS queue or topic using Message Queue.

Exception and Fault Handling

A SOAP application can use two error reporting mechanisms: SOAP exceptions and SOAP faults:

Writing a SOAP Client

The following steps show the calls you have to make to write a SOAP client for point-to-point messaging.

ProcedureTo Write a SOAP Client for Point-to-Point Messaging

  1. Get an instance of a SOAPConnectionFactory:


    SOAPConnectionFactory myFct = SOAPConnectionFactory.newInstance();
  2. Get a SOAP connection from the SOAPConnectionFactory object:


    SOAPConnection myCon = myFct.createConnection();

    The myCon object that is returned will be used to send the message.

  3. Get a MessageFactory object to create a message:


    MessageFactory myMsgFct = MessageFactory.newInstance();
  4. Use the message factory to create a message:


    SOAPMessage message = myMsgFct.createMessage();

    The message that is created has all the parts that are shown in Figure 5–7.

    Figure 5–7 SOAP Message Parts

    Diagram showing SOAP message with pre-initialized objects:
part, envelope, header, and body.

    At this point, the message has no content. To add content to the message, you need to create a SOAP body element, define a name and content for it, and then add it to the SOAP body.

    Remember that to access any part of the message, you need to traverse the tree, calling a get method on the parent element to obtain the child. For example, to reach the SOAP body, you start by getting the SOAP part and SOAP envelope:


    SOAPPart mySPart = message.getSOAPPart();
    SOAPEnvelope myEnvp = mySPart.getEnvelope();
  5. Now, you can get the body element from the myEnvp object:


    SOAPBody body = myEnvp.getBody();

    The children that you will add to the body element define the content of the message. (You can add content to the SOAP header in the same way.)

  6. When you add an element to a SOAP body (or header), you must first create a name for it by calling the envelope.createName method. This method returns a Name object, which you must then pass as a parameter to the method that creates the body element (or the header element).


    Name bodyName = envelope.createName("GetLastTradePrice", "m",
                                           "http://eztrade.com")
    SOAPBodyElement gltp = body.addBodyElement(bodyName);
  7. Now create another body element to add to the gltp element:


    Name myContent = envelope.createName("symbol");
    SOAPElement mySymbol = gltp.addChildElement(myContent);
  8. And now you can define data for the body element mySymbol:


    mySymbol.addTextNode("SUNW");

    The resulting SOAP message object is equivalent to this XML scheme:


    <SOAP-ENV: Envelope
        xmlns:SOAPENV="http://schemas.xmlsoap.org/soap/envelope/">
            <SOAP-ENV:Body>
                <m:GetLastTradePrice xmlns:m="http://eztrade.com">
                    <symbol>SUNW</symbol>
                </m:GetLastTradePrice>
        </SOAP-ENV:Body>
    </SOAP-ENV: Envelope>
  9. Every time you send a message or write to it, the message is automatically saved. However if you change a message you have received or one that you have already sent, this would be the point when you would need to update the message by saving all your changes. For example:


    message.saveChanges();
  10. Before you send the message, you must create a URLEndpoint object with the URL of the endpoint to which the message is to be sent. (If you use a profile that adds addressing information to the message header, you do not need to do this.)


    URLEndpoint endPt = new URLEndpoint("http://eztrade.com//quotes");
  11. Now, you can send the message:


    SOAPMessage reply = myCon.call(message, endPt);

    The reply message (reply) is received on the same connection.

  12. Finally, you need to close the SOAPConnection object when it is no longer needed:


    myCon.close();

Writing a SOAP Service

A SOAP service represents the final recipient of a SOAP message and should currently be implemented as a servlet. You can write your own servlet or you can extend the JAXMServlet class, which is furnished in the soap.messaging package for your convenience. This section describes the task of writing a SOAP service based on the JAXMServlet class.

Your servlet must implement either the ReqRespListener or OneWayListener interfaces. The difference between these two is that ReqRespListener requires that you return a reply.

Using either of these interfaces, you must implement a method called onMessage(SOAPMsg). JAXMServlet will call onMessage after receiving a message using the HTTP POST method, which saves you the work of implementing your own doPost() method to convert the incoming message into a SOAP message.

Example 5–2 shows the basic structure of a SOAP service that uses the JAXMServlet utility class.


Example 5–2 Skeleton Message Consumer


public class MyServlet extends JAXMServlet implements
                                 ReqRespListener
{
    public SOAPMessage onMessage(SOAP Message msg)
    { //Process message here
    }
}

Example 5–3 shows a simple ping message service:


Example 5–3 A Simple Ping Message Service


public class SOAPEchoServlet extends JAXMServlet
                                         implements ReqRespListener{

    public SOAPMessage onMessage(SOAPMessage mySoapMessage) {
        return mySoapMessage
    }
}

Table 5–2 describes the methods that the JAXM servlet uses. If you were to write your own servlet, you would need to provide methods that performed similar work. In extending JAXMServlet , you may need to override the Init method and the SetMessageFactory method; you must implement the onMessage method.

Table 5–2 JAXMServlet Methods

Method 

Description 

void init (ServletConfig)

Passes the ServletConfig object to its parent’s constructor and creates a default messageFactory object.

If you want incoming messages to be constructed according to a certain profile, you must call the SetMessageFactory method and specify the profile it should use in constructing SOAP messages.


void doPost (HTTPRequest,
HTTPResponse

Gets the body of the HTTP request and creates a SOAP message according to the default or specified MessageFactory profile. 

Calls the onMessage() method of an appropriate listener, passing the SOAP message as a parameter.

It is recommended that you do not override this method. 


void setMessageFactory
  (MessageFactory)

Sets the MessageFactory object. This is the object used to create the SOAP message that is passed to the onMessage method.


MimeHeaders getHeaders
 (HTTPRequest)

Returns a MimeHeaders object that contains the headers in the given HTTPRequest object.


void putHeaders (mimeHeaders,
 HTTPresponse) 

Sets the given HTTPResponse object with the headers in the given MimeHeaders object.


onMessage
(SOAPMesssage)

User-defined method that is called by the servlet when the SOAP message is received. Normally this method needs to disassemble the SOAP message passed to it and to send a reply back to the client (if the servlet implements the ReqRespListener interface.)

Disassembling Messages

The onMessage method needs to disassemble the SOAP message that is passed to it by the servlet and process its contents in an appropriate manner. If there are problems in the processing of the message, the service needs to create a SOAP fault object and send it back to the client as described in Handling SOAP Faults.

Processing the SOAP message may involve working with the headers as well as locating the body elements and dealing with their contents. The following code sample shows how you might disassemble a SOAP message in the body of your onMessage method. Basically, you need to use a Document Object Model (DOM) API to parse through the SOAP message.

See http://xml.coverpages.org/dom.html for more information about the DOM API.


Example 5–4 Processing a SOAP Message


{http://xml.coverpages.org/dom.html
    SOAPEnvelope env = reply.getSOAPPart().getEnvelope();
    SOAPBody sb = env.getBody();
    // create Name object for XElement that we are searching for
    Name ElName = env.createName("XElement");

    //Get child elements with the name XElement
    Iterator it = sb.getChildElements(ElName);

    //Get the first matched child element.
    //We know there is only one.
    SOAPBodyElement sbe = (SOAPBodyElement) it.next();

    //Get the value for XElement
    MyValue =   sbe.getValue();
}               

Handling Attachments

A SOAP message may have attachments. For sample code that shows you how to create and add an attachment, see Code Samples. For sample code that shows you how to receive and process an attachment, see Code Samples.

In handling attachments, you will need to use the Java Activation Framework API. See http://java.sun.com/products/javabeans/glasgow/jaf.html for more information.

Replying to Messages

In replying to messages, you are simply taking on the client role, now from the server side.

Handling SOAP Faults

Server-side code must use a SOAP fault object to handle errors that occur on the server side when unmarshalling the request, processing the message, or marshalling the response. The SOAPFault interface extends the SOAPBodyElement interface.

SOAP messages have a specific element and format for error reporting on the server side: a SOAP message body can include a SOAP fault element to report errors that happen during the processing of a request. Created on the server side and sent from the server back to the client, the SOAP message containing the SOAPFault object reports any unexpected behavior to the originator of the message.

Within a SOAP message object, the SOAP fault object is a child of the SOAP body, as shown in the figure below. Detail and detail entry objects are only needed if one needs to report that the body of the received message was malformed or contained inappropriate data. In such a case, the detail entry object is used to describe the malformed data.

Figure 5–8 SOAP Fault Element

Diagram showing hierarchy from top to bottom for a message
containing fault information: SOAP part, envelope, body, fault, detail, and
detail entry.

The SOAP Fault element defines the following four sub-elements:

Predefined Fault Codes

The SOAP specification lists four predefined faultcode values. The namespace identifier for these is http://schemas.xmlsoap.org/soap/envelope/.

Table 5–3 SOAP Faultcode Values

Faultcode Name 

Meaning 


VersionMismatch 

The processing party found an invalid namespace for the SOAP envelope element; that is, the namespace of the SOAP envelope element was not http://schemas.xmlsoap.org/soap/envelope/ .


MustUnderstand 

An immediate child element of the SOAP Header element was either not understood or not appropriately processed by the recipient. This element’s mustUnderstand attribute was set to 1 (true).


Client

The message was incorrectly formed or did not contain the appropriate information. For example, the message did not have the proper authentication or payment information. The client should interpret this code to mean that the message must be changed before it is sent again. 

If this is the code returned, the SOAPFault object should probably include a detailEntry object that provides additional information about the malformed message.


Server 

The message could not be processed for reasons that are not connected with its content. For example, one of the message handlers could not communicate with another message handler that was upstream and did not respond. Or, the database that the server needed to access is down. The client should interpret this error to mean that the transmission could succeed at a later point in time. 

These standard fault codes represent classes of faults. You can extend these by appending a period to the code and adding an additional name. For example, you could define a Server.OutOfMemory code, a Server.Down code, and so forth.

Defining a SOAP Fault

Using SAAJ you can specify the value for faultcode, faultstring, and faultactor using methods of the SOAPFault object. The following code creates a SOAP fault object and sets the faultcode, faultstring, and faultactor attributes:


SOAPFault fault;
reply = factory.createMessage();
envp = reply.getSOAPPart().getEnvelope(true);
someBody = envp.getBody();
fault = someBody.addFault():
fault.setFaultCode("Server");
fault.setFaultString("Some Server Error");
fault.setFaultActor(http://xxx.me.com/list/endpoint.esp/)
reply.saveChanges();
               

The server can return this object in its reply to an incoming SOAP message in case of a server error.

The next code sample shows how to define a detail and detail entry object. Note that you must create a name for the detail entry object.


SOAPFault fault = someBody.addFault();
fault.setFaultCode("Server");
fault.setFaultActor("http://foo.com/uri");
fault.setFaultString ("Unkown error");
Detail myDetail = fault.addDetail();
detail.addDetailEntry(envelope.createName("125detail", "m",
        "Someuri")).addTextNode("the message cannot contain
         the string //");
reply.saveChanges();