XML/SOAP Service  Locate

Overview  Locate

XML/SOAP services work on the XML/SOAP Protocol level (see WSO2 SOA Enablement Server Message Processing Overview for a description of levels). This is a lower level than the one on which Java services reside. It provides direct access to the SOAP messages that represent Web service requests. In WSO2 SOA Enablement Server, SOAP messages on the XML/SOAP level follow Simple Object Access Protocol (SOAP) 1.1 or 1.2 and SOAP with Attachments specifications and are accessible by using SOAP with Attachments API for Java (SAAJ) 1.1.

There are two varieties of XML services, consuming either request-response or one-way messages, as per the Java API for XML Messaging (JAXM) 1.1 specification. The service instance implements a different interface for each:

Type of message consumedJAXM interface implemented by service instance
Request-responsejavax.xml.messaging.ReqRespListener
One-wayjavax.xml.messaging.OnewayListener

Upon receiving an incoming request, the only method of either interface, onMessage(SOAPMessage), is called: the parameter represents the incoming SOAP message. For one-way messages, the method has no return value. For request-response messages, the return value represents the SOAP response message.

The following sections explain how to choose which type of service to implement.

One-Way Message Services Using JAXM OneWayListener  Locate

The OnewayListener interface is implemented by one-way messaging services. The receiver of a one-way message is sent the message in one operation and returns the response in another operation.

There is only one method, onMessage(SOAPMessage), on OnewayListener. It describes how to process a one-way request. When the method is called, it passes the SOAPMessage object to the OnewayListener implementation.

Unlike the case for a request-response message, the recipient of a one-way request does not have to send an immediate response. The time interval between the request and the response can be very long.

Example 29 shows how to perform simple SOAP messaging through an XML/SOAP service that implements OnewayListener. When the service gets a SOAP message, it performs the following steps:

  1. It obtains the target from the SOAP header.

  2. It creates a new SOAP message with the rest of the header.

  3. It puts a new string message into the body of the outgoing message.

  4. It sends the message to the target.

Example 29. One-Way XML/SOAP Service

// Copyright WSO2 Inc. All rights reserved.
// Use is subject to license terms.
package example.basics.webservices;

import org.idoox.util.RuntimeWrappedException;

import org.systinet.wasp.messaging.WaspProviderConnection;

import java.util.Iterator;

import javax.xml.messaging.JAXMException;
import javax.xml.messaging.OnewayListener;
import javax.xml.messaging.ProviderConnectionFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPConstants;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;


public class MessagingService implements OnewayListener {
    private WaspProviderConnection provider;

    //constructor
    public MessagingService() {
        try {
            this.provider =
                (WaspProviderConnection) 
                ProviderConnectionFactory.newInstance().createConnection();
        } catch (JAXMException e) {
            throw new RuntimeWrappedException(e);
        }
    }

    public void onMessage(SOAPMessage msg) {
        try {
            // gets the envelope
            SOAPEnvelope envelope = msg.getSOAPPart()
                                       .getEnvelope();
            SOAPBody body = envelope.getBody();
            Iterator iter =
                body.getChildElements(envelope.createName("message"));
            SOAPElement message = (SOAPElement) iter.next();

            // the first header is the next target
            SOAPHeader headers = envelope.getHeader();

            // the message is not processed further if there is no header
            if (headers != null) {
                // get the first header and remove it - it is the next target
                iter = headers.examineHeaderElements(
                           SOAPConstants.URI_SOAP_ACTOR_NEXT);

                SOAPHeaderElement target = (SOAPHeaderElement) iter.next();
                target.detachNode();

                // remove old body/message and create new one
                message.detachNode();
                message = body.addBodyElement(envelope.createName("message"));

                int lastSlash = target.getValue()
                                      .lastIndexOf('/');
                String dwarf = target.getValue()
                                     .substring(lastSlash + 1);
                message.addTextNode("Hello " + dwarf + " !!!");

                // send the message
                this.provider.send(
                    msg,
                    target.getValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Request-Response Message Services Using JAXM ReqRespListener  Locate

The ReqResponseListener interface is implemented by request-response messaging services. In request-response messaging, sending a request and receiving the response are both done in a single operation.

As with OnewayListener, the only method on the interface is onMessage(SOAPMessage). It is used to describe how to process a request. In contrast to the OnewayListener, the onMessage method has a return value, representing the response. When the method is called, it passes the SOAPMessage object to the interface implementation and returns a response.

Example 30 is a simple HelloService that consumes an incoming request SOAP message and creates a simple response SOAP message.

Example 30. Request-Response XML/SOAP Service

// Copyright WSO2 Inc. All rights reserved.
// Use is subject to license terms.
package example.basics.webservices;

import org.idoox.util.RuntimeWrappedException;

import java.util.Iterator;

import javax.xml.messaging.ReqRespListener;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPBodyElement;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFault;
import javax.xml.soap.SOAPMessage;


public class XMLHelloService implements ReqRespListener {
    MessageFactory factory;

    //constructor
    public XMLHelloService() {
        try {
            this.factory = MessageFactory.newInstance();
        } catch (SOAPException e) {
            throw new RuntimeWrappedException(e);
        }
    }

    public SOAPMessage onMessage(SOAPMessage message) {
        try {
            // read request
            SOAPEnvelope envelope = message.getSOAPPart()
                                           .getEnvelope();
            SOAPBody body = envelope.getBody();
            Iterator operations =
                body.getChildElements(
                    envelope.createName("string_Request", null,
                        "http://wso2.com/xsd/SchemaTypes/"));
            String requestString =
                ((SOAPElement) operations.next()).getValue();

            // create response
            SOAPMessage respMessage = factory.createMessage();
            SOAPEnvelope respEnvelope =
                respMessage.getSOAPPart()
                           .getEnvelope();
            SOAPBody respBody = respEnvelope.getBody();
            SOAPBodyElement bodyElement =
                respBody.addBodyElement(
                    respEnvelope.createName("string_Response", null,
                        "http://wso2.com/xsd/SchemaTypes/"));
            bodyElement.addTextNode("Hello, " + requestString + "!");

            return respMessage;
        } catch (Exception e) {
            try {
                // create fault
                SOAPMessage faultMessage = factory.createMessage();
                SOAPBody body =
                    faultMessage.getSOAPPart()
                                .getEnvelope()
                                .getBody();
                SOAPFault fault = body.addFault();
                fault.setFaultCode("Server");
                fault.setFaultString("SOAP Message could not be processed");

                return faultMessage;
            } catch (SOAPException ee) {
                throw new RuntimeWrappedException(e);
            }
        }
    }
}

Stream-Based Service Using Tokenizer  Locate

Instead of using the tree-like SAAJ API, you can access and process SOAP messages in a stream-based manner using Tokenizer (see Javadoc for org.idoox.xml.Tokenizer). The tokenizer represents an XML document as a sequence of XML tokens (elements).

Example 31 is a stream-based HelloService using Tokenizer. The service performs the following steps:

  1. It gets an instance of Tokenizer from the incoming SOAP message. (This is done by calling the methods getSOAPPart(), getContent() and then getTokenizer(), in that order.

  2. It creates a wrapper of this tokenizer. Inside this wrapper, it modifies the tokens of the incoming SOAP message to create the outgoing message.

  3. It sets this tokenizer as the source for the outgoing SOAP message.

Example 31. Stream-Based Service Using Tokenizer

// Copyright WSO2 Inc. All rights reserved.
// Use is subject to license terms.
package example.basics.webservices;

import org.idoox.util.RuntimeWrappedException;

import org.idoox.xml.Token;
import org.idoox.xml.Tokenizer;
import org.idoox.xml.TokenizerException;
import org.idoox.xml.TokenizerSource;
import org.idoox.xml.TokenizerWrapper;

import java.io.IOException;

import javax.xml.messaging.ReqRespListener;
import javax.xml.soap.MessageFactory;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;


public class XMLTokenizerService implements ReqRespListener {
    MessageFactory factory;

    // constructor for service
    public XMLTokenizerService() {
        try {
            this.factory = MessageFactory.newInstance();
        } catch (SOAPException e) {
            throw new RuntimeWrappedException(e);
        }
    }

    public SOAPMessage onMessage(SOAPMessage message) {
        try {
            // gets SOAP part from incoming message
            SOAPPart part = message.getSOAPPart();

            // gets tokenizer from this part
            TokenizerSource source = (TokenizerSource) part.getContent();
            Tokenizer tokenizer = source.getTokenizer();

            // creates instance of Tokenizer that is a wrapper of the tokenizer
            // from the incoming message
            XMLTokenizerService.MyTokenizer myTokenizer =
                new XMLTokenizerService.MyTokenizer(tokenizer);

            // creates outgoing message
            SOAPMessage outMessage = factory.createMessage();

            // gets SOAP part for outgoing message
            SOAPPart outPart = outMessage.getSOAPPart();

            // gets the tokenizer source
            TokenizerSource outSource = (TokenizerSource) outPart.getContent();

            // sets new tokenizer
            outSource.setTokenizer(myTokenizer);
            outPart.setContent(outSource);

            return outMessage;
        } catch (Exception e) {
            throw new RuntimeWrappedException(e);
        }
    }

    /**
     * Logging tokenizer. Wraps original tokenizer
     * and logs tokens.
     */
    class MyTokenizer extends TokenizerWrapper {
        // current token
        private Token token = new Token();

        // constructor
        public MyTokenizer(Tokenizer tokenizer) {
            super(tokenizer);
        }

        // modifies tokens for outgoing message
        public byte next() throws TokenizerException, IOException {
            Tokenizer tokenizer = getTokenizer();
            byte next = tokenizer.next();

            switch (next) {
            case START_TOKEN:
                tokenizer.readToken(token);

                if (token.getLocalName().equals("string_Request")) {
                    token.localName = "string_Response";
                    setCurrentToken(token);
                }

                break;

            case END_TOKEN:
                tokenizer.readToken(token);

                if (token.getLocalName()
                         .equals("string_Request")) {
                    token.localName = "string_Response";
                    setCurrentToken(token);
                }

                break;

            case CONTENT:

                String content = tokenizer.readContent();

                if (content.equals("World")) {
                    setCurrentContent("Hello, World !");
                }

                break;
            }

            return next;
        }
    }
}

MIME and DIME Attachments, JAF  Locate

Sometimes a service needs to send or receive application-specific binary data to its peer. Such data is represented at the XML/SOAP level as attachments, following the SOAP with Attachments specification. A SOAP message can contain any number of these attachments in addition to the SOAP part.

In the SAAJ API the SOAPMessage object provides methods for creating AttachmentPart objects and for adding them to a SOAPMessage object.

Example 32 is a simple service that accesses attachments from the SOAP message.

Example 32. Service Accessing Binary Attachments From SOAP

// Copyright WSO2 Inc. All rights reserved.
// Use is subject to license terms.
package example.basics.webservices;

import java.io.FileOutputStream;
import java.io.InputStream;

import java.util.Iterator;

import javax.xml.messaging.OnewayListener;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPMessage;


public class XMLAttachmentService implements OnewayListener {
    public void onMessage(SOAPMessage message) {
        try {
            // gets iterator over attachments
            Iterator iterator = message.getAttachments();

            if (iterator.hasNext()) {
                // gets the first attachment
                AttachmentPart attachmentPart =
                    (AttachmentPart) iterator.next();

                // gets the content
                InputStream stream = (InputStream) attachmentPart.getContent();

                // writes to the file
                FileOutputStream toStream = new FileOutputStream("image.jpg");
                byte[] buffer = new byte[1024];
                int length;

                while ((length = stream.read(buffer)) > 0) {
                    toStream.write(buffer, 0, length);
                }

                toStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
Handling Attachments Through JAF  Locate

Alternatively, attachments can be accessed by using the DataHandler object that is a part of the JavaBeans Activation Framework (JAF). This is demonstrated in the code fragment in Example 33:

Example 33. Handling Attachments Through JAF

Iterator iterator = message.getAttachments();
if  (iterator.hasNext()) {
// gets the first attachment
AttachmentPart attachmentPart = (AttachmentPart)
iterator.next();
// gets the content
DataHandler handler = attachmentPart.getDataHandler();
Object data = handler.getContent();
if  ("image/jpeg".equals(handler.getContentType())
&& data instanceofjava.awt.Image) {
        java.awt.Image image = (java.awt.Image)data;
// .....
}

Setting Encapsulation Type  Locate

WSO2 SOA Enablement Server supports both MIME and DIME encapsulation types. The XML service can use the default encapsulation chosen during WSO2 SOA Enablement Server installation as its default Multi-part encapsulation or it can be set in the SOAP message. Both options are transparent to the user.

The code fragment in Example 34 illustrates how to set DIME encapsulation in the SOAP message:

Example 34. Setting DIME Encapsulation in the SOAP Message

MessageFactory factory = MessageFactory.newInstance();
WaspSOAPMessage message = (WaspSOAPMessage)factory.createMessage();
message.setAttachmentType(org.idoox.transport.Message.CT_APPLICATION_DIME);

See also Processing Attachments in XML/SOAP Level Processing, Message Processing.

SOAP Faults  Locate

If a service cannot correctly process an incoming SOAP message for any reason, the client gets back a response containing a SOAP Fault with information about the error. A SOAP message can carry one SOAP Fault element, which must be placed in the body of the message. In the SAAJ API, a SOAP fault is represented by the SOAPFault object.

The following code fragment in Example 35 shows how a SOAPFault object is added to a SOAPMessage:

Example 35. Adding a SOAPFault Object to a SOAP Message

SOAPBody body = message.getSOAPPart().getEnvelope().getBody();
SOAPFault fault = body.addFault();
fault.setFaultCode("MustUnderstand");
fault.setFaultString("SOAP Must Understand Error");

For more information on the SOAP Fault element and code, see SOAP Faults.