Sun ONE Application Server 7 Developer's Guide to Web Services |
Chapter 3
SOAP Clients and Services Using SAAJ and JAXMThis chapter describes how to use the SOAP with Attachments API for JavaTM (SAAJ) and the JavaTM API for XML Messaging (JAXM) to build clients that can send and receive messages, and deploy them to Sun ONE Application Server. This chapter contains the following sections:
SOAP ClientsThis section describes the two messaging models in which SOAP clients can be used; the procedure to develop and deploy such clients. This section describes the following topics:
SOAP Client Messaging Models
You can build SOAP clients using the following two messaging models.
Client Without a Messaging Provider
An application that does not use a messaging provider can exchange only synchronous messages. That is, an application operates in a client role and can send only request-response messages. This type of client uses the SOAPConnection method of the SAAJ API. TFigure 3-1he following figure illustrates how synchronous messages are exchanged between the sender and the receiver without using a messaging provider.
Figure 3-1 SOAP Message Without Using a Messaging Provider
Clients not using a messaging provider have the following advantages:
Clients not using a messaging provider have the following limitations:
Client With a Messaging Provider
You must use a messaging provider if you want to be able to get and save requests that are sent to you at any time. The JAXM API provides the framework to send and receive messages using a messaging provider.You will need to run the client in a container, which provides the messaging infrastructure used by the provider. The following figure Figure 3-2illustrates how asynchronous messages are exchanged between the sender and the receiver using a messaging provider.
Figure 3-2 SOAP Message Using a Messaging Provider
Clients using messaging provider have the following advantages:
- Clients can assume the roles of a client and a service.
- Clients can hand off message delivery to a provider.
- Clients can send messages to one or more destinations before it delivers the message to the final recipient. These intermediate message recipients are called actors and they are specified in the SOAPHeader object of the message.
- Clients can take advantage of any provider-supported SOAP messaging protocols and ‘Quality of Service’ affecting the types of messages and the reliability and the quality of service of message delivery.
SOAP Messages
This section introduces you to the structure and parts of a SOAP message, how you access these, and how you process SOAP messages. This section describes the following topics:
Parts of a SOAP Message
A SOAP message is an XML document that consists of a SOAPEnvelope, an optional SOAPHeader, and a SOAPBody. The SOAP message header contains information that allows the message to be routed through one or more intermediate nodes before it reaches its final destination.
Figure 3-3 Parts of a SOAP Message
The figure, "Parts of a SOAP Message"Figure 3-3, shows the structure and parts of a SOAP message. Different objects represents each part of a SOAP message.
The SOAPMessage object contains
The SOAPEnvelope is the root element of the XML document representing the message. It defines the framework for how the message should be handled and by whom. XML content starts at the SOAPEnvelope.
The SOAPHeader is a generic mechanism for adding features to a SOAP message. It can contain any number of child elements that define extensions to the base protocol. For example, header child elements might define authentication information, transaction information, locale information, and so on. The software that handles the message may, without prior agreement, use this mechanism to define who should deal with a feature and whether the feature is mandatory or optional.
The body is a container for mandatory information intended for the ultimate recipient of the message. A SOAP message may also contain an attachment, which need not necessarily be an XML document.
Accessing Elements of a Message
You need to access parts of a message when you create the message body or the attachment part or when you are processing the message.
The SOAPMessage object message contains a SOAPPart object. Use the message object to retrieve it.
SOAPPart soapPart = message.getSOAPPart();
Next you can use SOAPPart to retrieve the SOAPEnvelope object that it contains.
SOAPEnvelope envelope = soapPart.getEnvelope();
You can now use envelope to retrieve its empty SOAPHeader and SOAPBody objects.
SOAPHeader header = envelope.getHeader();
SOAPBody body = envelope.getBody();
SOAPBody objects are initially empty.
Namespaces
An XML namespace is a means of qualifying elements and attribute names to disambiguate from other names in the same document. An explicit XML Namespace declaration takes the following form:
<prefix:myElement
xmlns:prefix ="URI">
The declaration defines prefix as an alias for the specified URI. In the element myElement, you can use prefix with any element or attribute to specify that the element or attribute name belongs to the namespace specified by the URI. The following line of code is an example of a namespace declaration:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/”>
This declaration defines SOAP_ENV as an alias for the namespace http://schemas.xmlsoap.org/soap/envelope/
After defining the alias, you can use it as a prefix to any attribute or element in the Envelope element.
Pre-defined SOAP Namespaces
SOAP defines two namespaces:
When you use SAAJ or JAXM to construct or consume messages, you are responsible for setting or processing namespaces correctly and for discarding messages that have incorrect namespaces.
Using Namespaces when Creating a SOAP Message
When you create the body elements or header elements of a SOAP message, you must use the Name object to specify a well-formed name for the element. You obtain a Name object by calling the method SOAPEnvelope.createName.
When you call this method, you can pass a local name as a parameter or you can specify a local name, prefix, and a URI. For example, the following line of code defines a Name object bodyName.
Name bodyName = MyEnvelope.createName("TradePrice", "GetLTP", "http://foo.eztrade.com");
This would be equivalent to the namespace declaration:
<GetLTP:TradePrice xmlns:GetLTP= "http://foo.eztrade.com">
The following code shows how you can create a name and associate it with a SOAPBody element. Note the use and placement of the createName method.
SoapBody body = envelope.getBody();//get body from envelope
Name bodyName = envelope.createName("TradePrice", "GetLTP", "http://foo.eztrade.com");
SOAPBodyElement gltp = body.addBodyElement(bodyName);
Developing a SOAP Client
Using SAAJ, a client can create and send SOAP messages in a point-to-point model. JAXM defines the API for xml messaging using a messaging provider. JAXM depends on the SOAP with Attachments API for Java (SAAJ), which defines the API for operating on the SOAP with attachments message model in Java. Sun ONE Application Server does not include a supported JAXM messaging provider. It does however include along with the sample applications, a simple JAXM provider that demonstrates how a messaging provider handles asynchronous SOAP messaging from a client. This section describes the following topics:
How SOAP Messaging Occurs?
SOAP messaging occurs when a SOAP message, produced by a message factory is sent to an Endpoint via a Connection. This section describes the following topics:
Endpoint
An endpoint identifies the final destination of a message. An endpoint is defined by the URLEndpoint class. If you do not use a provider, you must construct or find an endpoint to which the message is sent.
Constructing an Endpoint
You can initialize an endpoint either by calling its constructor or by looking it up in a naming service.
The following code uses a constructor to create an URLEndpoint:
myEndpoint = new URLEndpoint("http://host/myServlet")
Using the Endpoint to Address a Message
Specify the endpoint as a parameter to the SOAPConnection.call method, which you use to send a SOAP message.
Sending a Message to Multiple Endpoints
Administered objects are objects that encapsulate provider-specific configuration and naming information. If you are using an administered object to define an endpoint, note that it is possible to associate that administered object with multiple URLs-each URL capable of processing incoming SOAP messages.
The code sample below associates the endpoint whose lookup name is myEndpoint with two URLs: http://www.myServlet1/ and http://www.myServlet2/. This syntax allows you to use a SOAP connection to publish a SOAP message to multiple endpoints.
imqobjmgr add
-t e
-l "cn=myEndpoint"
-o "imqSOAPEndpointList=http://www.myServlet1/
http://www.myServlet2/"Connection
To send a SOAP message using SAAJ or JAXM, you must obtain a SOAPConnection or a ProviderConnection respectively.You can also transport a SOAP message using the Message Queue; for more information, see the Sun ONE Message Queue Developer’s Guide.
SOAP Connection
A SOAPConnection allows you to send messages directly to a remote party. You can obtain a SOAPConnection object simply by calling the static method SOAPConnectionFactory.newInstance().Neither reliability nor security are guaranteed over this type of connection.Provider Connection
A ProviderConnection, which you get from a ProviderConnectionFactory, creates a connection to a particular messaging provider. When you send a SOAP message using a provider, the message is forwarded to the provider, and then the provider is responsible for delivery to its final destination thus ensuring reliable and secure messaging.
Creating a SOAP Client
Before creating a SOAP client, make sure to set up your client environment. For more information on setting up your client environment, see "Setting Up the Client Environment".
If you are creating a point-to-point client, you must import the javax.xml.soap package of the SAAJ API. If you are creating a pub/sub client, import the javax.xml.messaging package of the JAXM API. In addition, you must import the following packages:
import java.net.*;
import java.io.*;
import java.util.*;import javax.servlet.http.*;
import javax.servlet.*;import javax.activation.*;
import javax.naming.*;Creating a SOAP client and accessing the message involves the following steps:
- Adding an Attachment to the Message (optional)
Getting a Connection
Stand-alone Client
A client that does not use a messaging provider uses the SOAPConnection object to create a connection. The message sent using SoapConnection object goes directly from the sender to the URL that the sender specifies.
You must obtain a SOAPConnectionFactory object that you can use to create your connection. The SAAJ API makes this easy by providing the SOAPConnectionFactory class with a default implementation. The following code illustrates how to get an instance of the implementation:
SOAPConnectionFactory scf = SOAPConnectionFactory. newInstance();
Now you can use scFactory to create SOAPConnection object.
SOAPConnection con = scf.createConnection();
Creating a Message
To create a message, use a MessageFactory object. If you are creating a stand-alone client, that is, a client that does not use a messaging provider and also does not run in a container, you can use the implementation of the MessageFactory class that the SAAJ API provides. The following code illustrates getting an instance of the default message factory and then using it to create a message:
MessageFactory mf = MessageFactory.newInstance();
SOAPMessage msg = mf.createMessage();
Message creation takes care of creating the SOAPPart, a required part of the message as per the SOAP 1.1 specification.
SOAPPart sp = msg.getSOAPPart();
For information on structure and parts of a SOAP message, see "Parts of a SOAP Message".
Adding Content to the Header
You create a SOAPHeaderElement object to add content to the header. The following code illustrates how to create a SOAPHeaderElement using the SOAPEnvelope object.
SOAPHeader hdr = envelope.getHeader();
Name headerName = envelope.createName("Purchase Order","PO", "http://www.sonata.com/order");
SOAPHeaderElement headerElement = hdr.addHeaderElement(headerName);
The headerElement is identified by the Name object headerName. The addHeaderElement method is used to add or create a header element.
To add content to headerElement, use the addTextNode method as shown in the code illustration below:
headerElement.addTextNode(“order”);
The SOAPHeader object contains a SOAPHeaderElement object whose content is “order”.
Adding Content to a Message
You can add content to a SOAPPart object or to one or more AttachmentPart object or to both parts of a message.
To add content to the body, create a SOAPBodyElement object and add an XML element that you build with the method SOAPElement.addTextNode. The following code illustrates adding content to the message:
SOAPEnvelope envelope = sp.getSOAPEnvelope();
SOAPBody bdy = envelope.getSOAPBody();
SOAPBodyElement gltp = bdy.addBodyElement(envelope.createName("GetLastTradePrice", “ztrade”, “http://wombat.ztrade.com”));
gltp.addChildElement(envelope.createName("symbol","ztrade", "http://wombat.ztrade.com")).addTextNode("SUNW");;
The first three lines of the code access the SOAPBody object body, that is used to create a new SOAPBodyElement object and add it to body. The CreateName method has the argument Name object that identifies the SOAPBodyElement that is being added. The last line adds the XML string passed to the method addTextNode.
Adding an Attachment to the Message
The procedure to add attachments to a message is same for both the clients with and without using a messaging provider. The AttachmentPart object is used to add attachment part to a message.
You use the SOAPMessage object to create an AttachmentPart object. The SOAPMessage class has three methods for creating an AttachmentPart object. The first method allows you to create an AttachmentPart with no content. That is, the AttachmentPart method setContent is later used to add content to the attachment.
URL url = new URL(data);
AttachmentPart ap = msg.createAttachmentPart(new DataHandler(url));The setContent method takes two parameters, a Java object for the content and a String object that identifies the content type. Content is the SOAPBody part of a message that has a Content-Type header with the value “text/xml” because the content has to be in XML format. In the AttachmentPart, the type of the content has to be specified as this object can take any type of content.
Each AttachmentPart has one or more headers associated with it. In the setContent method, the type of the method used is the type for the header Content-Type. This is the only header that is required. You can also set other optional headers, such as Content-Id and Content-Location. For convenience, JAXM and SAAJ APIs provides get and set methods for the headers Content-type, Content-Id, and Content-Location. These headers are helpful in accessing a particular attachment when a message has multiple attachments.
The following code illustrates how you can use the setContent method:
String stringContent = "Update address for Sunny Skies " + "Inc., to 10 Upbeat Street, Pleasant Grove, CA 95439";
ap.setContent(stringContent, "text/html");
ap.setContentId("update_address");
msg.addAttachmentPart(ap);If you also want to attach a jpeg image, the second argument for the setContent method must be “image/jpeg”. The following code illustrates the use of setContent method to attach an image:
AttachmentPart ap2 = msg.createAttachmentPart();
byte[] jpegData = . . .;
ByteArrayInputStream stream = new ByteArrayInputStream(jpegData);
ap2.setContent(stream, "image/jpeg");
msg.addAttachmentPart(ap2);
The other two AttachmentPart methods allow you to create an AttachmentPart object with content. One of the two methods is very similar to the AttachmentPart.setContent method. It takes a Java object containing the content and a String giving the content type. The object may be a String, a stream, a javax.xml.transform.Source object, or a javax.activation.DataHandler object.
The other method for creating an AttachmentPart object with content takes a DataHandler object, which is part of the JavaBeans Activation Framework (JAF).
The following code illustrates how you can use DataHandler in content. First you create a java.net.URL object for the file you want to add as content. Create the DataHandler object javax.activation.DataHandler object dh initialized with the URL object and pass it to the method createAttachmentPart.
URL url = new URL("http://greatproducts.com/gizmos/img.jpg");
DataHandler dh = new DataHandler(url);
AttachmentPart ap = msg.createAttachmentPart(dh);
ap.setContentId(“gyro_image”)
msg.addAttachmentPart(ap);Sending a Message
Stand-alone Client
To send a message, a stand-alone client uses the SOAPConnection method call. This method takes two arguments, the message being sent and the destination to which the message should go which is an Endpoint object that contains the URL of the receiver.
When using SoapConnection, you send message using javax.xml.soap.SOAPConnection.call() method.
For example:
URL urlEndpoint = new URL(to);
SOAPMessage reply = con.call(msg, urlEndpoint);
Retrieving the Content from a Response Message
To retrieve message content, the client uses the onMessage method. The client accesses SOAPBody object, using the message to get the envelope and the envelope to get the body. Access its SOAPBodyElement object because that is the element to which content was added. To retrieve the content, which was added with the method Node.addTextNode, you call the method Node.getValue. The getValue returns the value of the immediate child of the element that calls the method. To access bodyElement, you need to call the method getChildElement on body. The following code illustrates how to retrieve contents from a response message:
public SOAPMessage onMessage(SOAPMessage message)
{
SOAPEnvelop env = msg.getSOAPPart().getEnvelope();
env getBody()
.addChildElement(env.createName(“Response”))
.addTextNode(“This is a Response”);return msg;
}
To retrieve the contents from the message that contains an attachment, you need to access the attachment. When it is given no argument, the method SOAPMessage.getAttachments returns a java.util.Iterator object over all the AttachmentPart objects in a message. The following code prints content of each AttachmentPart object in the SOAPMessage object message.
java.util.Iterator it = message.getAttachments();
while (it.hasNext()) {
AttachmentPart attachment = (AttachmentPart)it.next();
Object content = attachment.getContent();
String id = attachment.getContentId();
System.out.print("Attachment " + id + " contains: " + content);
System.out.println("");}
Accessing Attachment Part of the Message
When you receive a message with an attachment, or you wish to change an attachment to a message, you need to access the attachment part of the message. The SOAPMesssage.getAttachments method without any attachment returns a java.util.Iterator object over all AttachmentPart objects in a message. The following code illustrates accessing the attachment part to get the content of each AttachmentPart object in the SOAPMessage object message.
java.util.Iterator it = msg.getAttachments();
while (it.hasNext()) {
AttachmentPart ap = it.next();
Object content = ap.getContent();
String id = ap.getContentId();
System.out.print("Attachment " + id + " contains: " + content);
System.out.println("");}
Assembling and Deploying a SOAP Client
Applications created using JAXM API and SAAJ API are assembled as web applications (WAR) or J2EE platform based applications (EAR). For more information on assembling and deploying a web application, see the Sun ONE Application Server Developer’s Guide to Web Applications.
SOAP ServiceThis section describes how you can write SOAP service and describes how you can handle exceptions and faults in SOAP messages. This section describes the following topics:
Creating a SOAP Service
A SOAP service is the final recipient of a SOAP message and is implemented as a servlet. You can either create your own servlet or you can extend the JAXMServlet class which is bundled in the javax.xml.messaging package. This section describes the procedure to create a SOAP service based on the JAXMServlet class. To create a SOAP service, your servlet must implement either the ReqRespListener or OneWayListener interfaces. A ReqRespListener requires that you return a reply.public class MyServlet extends JAXMServlet implements ReqRespListener{
...
...
}
Using any of the interfaces, implement a method called onMessage(SOAPMsg).
public SOAPMessage onMessage(SOAP Message msg)
The following code is the complete listing of the SOAP consumer using JAXMServlet.
public class MyServlet extends JAXMServlet implements ReqRespListener {
public SOAPMessage onMessage(SOAP Message msg) {
//Process message here
}
}JAXMServlet will call onMessage after receiving a message using the HTTP POST method. This saves you the work of implementing your own doPost() method to convert the incoming message into a SOAP message.
The onMessage method needs to disassemble the SOAP message that is passed to it by the servlet and process its contents. Processing the message involves accessing the parts of a SOAP message. 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. For more information on handling faults, see "Fault Handling".
The following code illustrates the processing of 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(); }Exception and Fault Handling
On the client’s side, JAXM and SAAJ uses a SOAP exception to handle errors that occur during the generation of the SOAP request or unmarshalling of the response. This section describes the following topics:
Fault Handling
Server-side code must use the SOAPFault 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 SOAPFault element to report errors that occur 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.
The SOAPFault element defines the following four subelements:
faultcode
A code that identifies the error. The code is intended for use by software to provide an algorithmic mechanism for identifying the fault. This element is required.faultstring
A string that describes the fault identified by the fault code. This element provides an explanation of the error that is understandable to a human. This element is required.faultactor
A URI specifying the source of the fault: who caused the fault. This element is not required if the message is sent to its final destination without going through any intermediaries. If a fault occurs at an intermediary, then that fault must include a faultactor element.detail
This element carries specific information related to the body element. It must be present if the contents of the body element could not be successfully processed. Thus, if this element is missing, the client should infer that the body element was processed. While this element is not required for any error except a malformed payload, you can use it in other cases to supply additional information to the client.Predefined Fault Codes
The SOAP specification lists the following four predefined faultcode values:
VersionMismatch
The processing party found an invalid namespace for the SOAP envelope element; that is, the namespace of the SOAPEnvelope element was not http://schemas.xmlsoap.org/soap/envelope/.
MustUnderstand
An immediate child element of the SOAPHeader element was either not understood or not appropriately processed by the recipient. This elements 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.
Defining SOAP Fault
You can specify the value for faultcode, faultstring, and a faultctor using methods of the SOAPFault object. The following code illustrates the creation of a SOAPFault 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 following code illustrates 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");
DetailEntry entry = detail.addEntry(envelope.createName("125detail", "m", "Someuri");
entry.addTextNode("the message cannot contain the string //");
reply.saveChanges();
Assembling and Deploying a SOAP Service
Applications created using JAXM API and SAAJ API are assembled as web applications (WAR) or J2EE platform based applications (EAR). For more information on assembling and deploying a web application, see the Sun ONE Application Server Developer’s Guide to Web Applications.
Sample Clients and Services
Sample client and services applications are bundled with Sun ONE Application Server. These samples demonstrate the creation of services and clients that send and receive XML messages. You can find the samples at the following location.
install_dir/samples/webservices/jaxm
install_dir/imq/demo/jaxm