|
Home
TOC Index |
|
Tutorial
This section will walk you through the basics of sending a SOAP message using the JAXM API. At the end of this chapter, you will know how to do the following:
- Get a connection
- Create a message
- Add content to a message
- Send a message
- Retrieve the content from a response message
- Create and retrieve a SOAP fault element
First, we'll walk through the steps in sending a request-response message for a client that does not use a messaging provider. Then we'll do a walkthrough of a client that uses a messaging provider sending a one-way message. Both types of client may add attachments to a message, so adding attachments is covered as a separate topic. Finally, we'll see what SOAP faults are and how they work.
The section Code Examples puts the code fragments you will produce into runnable applications, which you can test yourself. The JAXM part of the case study (JAXM Distributor Service) demonstrates how JAXM code can be used in a Web service, showing both the client and server code.
Client without a Messaging Provider
An application that does not use a messaging provider is limited to operating in a client role and can send only request-response messages. Though limited, it can make use of Web services that are implemented to do request-response messaging.
Getting a SOAPConnection Object
The first thing any JAXM client needs to do is get a connection, either a
SOAPConnectionobject or aProviderConnectionobject. The overview section (Connections) discusses these two types of connections and how they are used.A client that does not use a messaging provider has only one choice for creating a connection, which is to create a
SOAPConnectionobject. This kind of connection is a point-to-point connection, meaning that it goes directly from the sender to the destination (usually a URL) that the sender specifies.The first step is to obtain a
SOAPConnectionFactoryobject that you can use to create your connection. The SAAJ API makes this easy by providing theSOAPConnectionFactoryclass with a default implementation. You can get an instance of this implementation with the following line of code.SOAPConnectionFactory scFactory = SOAPConnectionFactory.newInstance();Notice that because
newInstanceis a static method, you will always use the class nameSOAPConnectionFactorywhen you invoke itsnewInstancemethod.Now you can use
scFactoryto create aSOAPConnectionobject.SOAPConnection con = scFactory.createConnection();You will use
conlater to send the message that is created in the next part.Creating a Message
The next step is to create a message, which you do using a
MessageFactoryobject. If you are a standalone client, you can use the default implementation of theMessageFactoryclass that the SAAJ API provides. The following code fragment illustrates getting an instance of this default message factory and then using it to create a message.MessageFactory factory = MessageFactory.newInstance(); SOAPMessage message = factory.createMessage();As is true of the
newInstancemethod forSOAPConnectionFactory, thenewInstancemethod forMessageFactoryis static, so you invoke it by callingMessageFactory.newInstance. Note that it is possible to write your own implementation of a message factory and plug it in via system properties, but the default message factory is the one that will generally be used.The other way to get a
MessageFactoryobject is to retrieve it from a naming service where it has been registered. This way is available only to applications that use a messaging provider, and it will be covered later (in Creating a Message).Parts of a Message
A
SOAPMessageobject is required to have certain elements, and the SAAJ API simplifies things for you by returning a newSOAPMessageobject that already contains these elements. Somessage, which was created in the preceding line of code, automatically has the following:I. A
SOAPPartobject that containsA. A
SOAPEnvelopeobject that containsThe
SOAPHeaderobject, though optional, is included for convenience because most messages will use it. TheSOAPBodyobject can hold the content of the message and can also contain fault messages that contain status information or details about a problem with the message. The section SOAP Faults walks you through how to useSOAPFaultobjects.Accessing Elements of a Message
The next step in creating a message is to access its parts so that content can be added. The
SOAPMessageobjectmessage, created in the previous code fragment, is where to start. It contains aSOAPPartobject, so you usemessageto retrieve it.SOAPPart soapPart = message.getSOAPPart();Next you can use
soapPartto retrieve theSOAPEnvelopeobject that it contains.SOAPEnvelope envelope = soapPart.getEnvelope();You can now use
envelopeto retrieve its emptySOAPHeaderandSOAPBodyobjects.SOAPHeader header = envelope.getHeader(); SOAPBody body = envelope.getBody();Our example of a standalone client does not use a SOAP header, so you can delete it. Because all
SOAPElementobjects, includingSOAPHeaderobjects, are derived from theNodeinterface, you use the methodNode.detachNodeto deleteheader.header.detachNode();Adding Content to the Body
To add content to the body, you need to create a
SOAPBodyElementobject to hold the content. When you create any new element, you also need to create an associatedNameobject to identify it. One way to createNameobjects is by usingSOAPEnvelopemethods, so you can useenvelopefrom the previous code fragment to create theNameobject for your new element.
Note: The SAAJ API augments thejavax.xml.soappackage by adding theSOAPFactoryclass, which lets you createNameobjects without using aSOAPEnvelopeobject. This capability is useful for creating XML elements when you are not creating an entire message. For example, JAX-RPC implementations find this ability useful. When you are not working with aSOAPMessageobject, you do not have access to aSOAPEnvelopeobject and thus need an alternate means of creatingNameobjects. In addition to a method for creatingNameobjects, theSOAPFactoryclass provides methods for creatingDetailobjects and SOAP fragments. You will find an explanation ofDetailobjects in the SOAP Fault sections Overview and Creating and Populating a SOAPFault Object.
Nameobjects associated withSOAPBodyandSOAPHeaderobjects must be fully qualified; that is, they must be created with a local name, a prefix for the namespace being used, and a URI for the namespace. Specifying a namespace for an element makes clear which one is meant if there is more than one element with the same local name.The code fragment that follows retrieves the
SOAPBodyobjectbodyfromenvelope, creates aNameobject for the element to be added, and adds a newSOAPBodyElementobject tobody.SOAPBody body = envelope.getBody(); Name bodyName = envelope.createName(""GetLastTradePrice"", ""m"", ""http://wombat.ztrade.com""); SOAPBodyElement gltp = body.addBodyElement(bodyName);At this point,
bodycontains aSOAPBodyElementobject identified by theNameobjectbodyName, but there is still no content ingltp. Assuming that you want to get a quote for the stock of Sun Microsystems, Inc., you need to create a child element for the symbol using the methodaddChildElement. Then you need to give it the stock symbol using the methodaddTextNode. TheNameobject for the newSOAPElementobjectsymbolis initialized with only a local name, which is allowed for child elements.Name name = envelope.createName("symbol"); SOAPElement symbol = gltp.addChildElement(name); symbol.addTextNode(""SUNW"");You might recall that the headers and content in a
SOAPPartobject must be in XML format. The JAXM API takes care of this for you, building the appropriate XML constructs automatically when you call methods such asaddBodyElement,addChildElement, andaddTextNode. Note that you can call the methodaddTextNodeonly on an element such asbodyElementor any child elements that are added to it. You cannot calladdTextNodeon aSOAPHeaderorSOAPBodyobject because they contain elements, not text.The content that you have just added to your
SOAPBodyobject will look like the following when it is sent over the wire:<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" <SOAP-ENV:Body> <m:GetLastTradePrice xmlns:m= "http://wombat.ztrade.com"> <symbol>SUNW</symbol> </m:GetLastTradePrice> </SOAP-ENV:Body> </SOAP-ENV:Envelope>Let's examine this XML excerpt line by line to see how it relates to your JAXM code. Note that an XML parser does not care about indentations, but they are generally used to indicate element levels and thereby make it easier for a human reader to understand.
SOAPPart soapPart = message.getSOAPPart(); SOAPEnvelope envelope = soapPart.getEnvelope();<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" . . . . . . (intervening elements omitted) </SOAP-ENV:Envelope>The outermost element in this XML example is the SOAP envelope element, indicated by
SOAP-ENV:Envelope.Envelopeis the name of the element, andSOAP-ENVis the namespace prefix. The interfaceSOAPEnveloperepresents a SOAP envelope.The first line signals the beginning of the SOAP envelope element, and the last line signals the end of it; everything in between is part of the SOAP envelope. The second line has an attribute for the SOAP envelope element.
xmlnsstands for "XML namespace," and its value is the URI of the namespace associated withEnvelope. This attribute is automatically included for you.SOAPBody body = envelope.getBody();<SOAP-ENV:Body> . . . . . . </SOAP-ENV:Body>These two lines mark the beginning and end of the SOAP body, represented in JAXM by a
SOAPBodyobject.Name bodyName = envelope.createName("GetLastTradePrice", "m", "http://wombat.ztrade.com"); SOAPBodyElement gltp = body.addBodyElement(bodyName);<m:GetLastTradePrice xmlns:m= "http://wombat.ztrade.com"> . . . . </m:GetLastTradePrice>These lines are what the
SOAPBodyElementgltpin your code represents. "GetLastTradePrice" is its local name, "m" is its namespace prefix, and "http://wombat.ztrade.com" is its namespace URI.Name name = envelope.createName("symbol"); SOAPElement symbol = gltp.addChildElement(name); symbol.addTextNode("SUNW");<symbol>SUNW</symbol>The
String"SUNW"is the message content that your recipient, the stock quote service, receives.Sending a Message
A standalone client uses a
SOAPConnectionobject and must therefore use theSOAPConnectionmethodcallto send a message. This method takes two arguments, the message being sent and the destination to which the message should go. This message is going to the stock quote service indicated by theURLobjectendpoint.java.net.URL endpoint = new URL( "http://wombat.ztrade.com/quotes"; SOAPMessage response = con.call(message, endpoint);Your message sent the stock symbol SUNW; the
SOAPMessageobjectresponseshould contain the last stock price for Sun Microsystems, which you will retrieve in the next section.A connection uses a fair amount of resources, so it is a good idea to close a connection as soon as you are through using it.
con.close();Getting the Content of a Message
The initial steps for retrieving a message's content are the same as those for giving content to a message: You first access the
SOAPBodyobject, using the message to get the envelope and the envelope to get the body. Then you access itsSOAPBodyElementobject because that is the element to which content was added in the example. (In a later section you will see how to add content directly to theSOAPBodyobject, in which case you would not need to access theSOAPBodyElementobject for adding content or for retrieving it.) To get the content, which was added with the methodSOAPElement.addTextNode, you call the methodNode.getValue. Note thatgetValuereturns the value of the immediate child of the element that calls the method. Therefore, in the following code fragment, the methodgetValueis called onbodyElement, the element on which the methodaddTextNodewas called.In order to access
bodyElement, you need to call the methodgetChildElementonbody. PassingbodyNametogetChildElementreturns ajava.util.Iteratorobject that contains all of the child elements identified by theNameobjectbodyName. You already know that there is only one, so just calling the methodnexton it will return theSOAPBodyElementyou want. Note that the methodIterator.nextreturns a JavaObject, so it is necessary to cast theObjectit returns to aSOAPBodyElementobject before assigning it to the variablebodyElement.SOAPPart sp = response.getSOAPPart(); SOAPEnvelope env = sp.getEnvelope(); SOAPBody sb = env.getBody(); java.util.Iterator it = sb.getChildElements(bodyName); SOAPBodyElement bodyElement = (SOAPBodyElement)it.next(); String lastPrice = bodyElement.getValue(); System.out.print("The last price for SUNW is");System.out.println(lastPrice);If there were more than one element with the name
bodyName, you would have had to use awhileloop using the methodIterator.hasNextto make sure that you got all of them.while (it.hasNext()) { SOAPBodyElement bodyElement = (SOAPBodyElement)it.next(); String lastPrice = bodyElement.getValue(); System.out.print("The last price for SUNW is");System.out.println(lastPrice); }At this point, you have seen how to send a request-response message as a standalone client. You have also seen how to get the content from the response. The next part shows you how to send a message using a messaging provider.
Client with a Messaging Provider
Using a messaging provider gives you more flexibility than a standalone client has because it can take advantage of the additional functionality that a messaging provider can offer.
Getting a ProviderConnection Object
Whereas a
SOAPConnectionobject is a point-to-point connection directly to a particular URL, aProviderConnectionobject is a connection to a messaging provider. With this kind of connection, all messages that you send or receive go through the messaging provider.As with getting a
SOAPConnectionobject, the first step is to get a connection factory, but in this case, it is aProviderConnectionFactoryobject. You can obtain aProviderConnectionFactoryobject by retrieving it from a naming service. This is possible when your application is using a messaging provider and is deployed in a servlet or J2EE container. With aProviderConnectionFactoryobject, you can create a connection to a particular messaging provider and thus be able to use the capabilities of a profile that the messaging provider supports.To get a
ProviderConnectionFactoryobject, you first supply the logical name of your messaging provider to the container at deployment time. This is the name associated with your messaging provider that has been registered with a naming service based on the Java Naming and Directory Interface(JNDI). You can then do a lookup using this name to obtain a
ProviderConnectionFactoryobject that will create connections to your messaging provider. For example, if the name registered for your messaging provider is "ProviderABC", you can do a lookup on "ProviderABC" to get aProviderConnectionFactoryobject and use it to create a connection to your messaging provider. This is what is done in the following code fragment. The first two lines use methods from the JNDI API to retrieve theProviderConnectionFactoryobject, and the last line uses a method from the JAXM API to create the connection to the messaging provider. Note that because the JNDI methodlookupreturns a JavaObject, you must convert it to aProviderConnectionFactoryobject before assigning it to the variablepcFactory.Context ctx = new InitialContext(); ProviderConnectionFactory pcFactory = (ProviderConnectionFactory)ctx.lookup("ProviderABC"); ProviderConnection pcCon = pcFactory.createConnection();You will use
pcCon, which represents a connection to your messaging provider, to get information about your messaging provider and to send the message you will create in the next section.Creating a Message
You create all JAXM messages by getting a
MessageFactoryobject and using it to create theSOAPMessageobject. For the standalone client example, you simply used the defaultMessageFactoryobject obtained via the methodMessageFactory.newInstance. However, when you are using a messaging provider, you obtain theMessageFactoryobject in a different way.Getting a MessageFactory
If you are using a messaging provider, you create a
MessageFactoryobject by using the methodProviderConnection.createMessageFactory. In addition, you pass it aStringindicating the profile you want to use. To find out which profiles your messaging provider supports, you need to get aProviderMetaDataobject with information about your provider. This is done by calling the methodgetMetaDataon the connection to your provider. Then you need to call the methodgetSupportedProfilesto get an array of the profiles your messaging provider supports. Supposing that you want to use the ebXML profile, you need to see if any of the profiles in the array matches "ebxml". If there is a match, that profile is assigned to the variableprofile, which can then be passed to the methodcreateMessageFactory.ProviderMetaData metaData = pcCon.getMetaData(); String[] supportedProfiles = metaData.getSupportedProfiles(); String profile = null; for (int i=0; i < supportedProfiles.length; i++) { if (supportedProfiles[i].equals("ebxml")) { profile = supportedProfiles[i]; break; } } MessageFactory factory = pcCon.createMessageFactory(profile);You can now use
factoryto create aSOAPMessageobject that conforms to the ebXML profile. This example uses the minimal ebXML profile implementation included in the Java WSDP. Note that the following line of code uses the classEbXMLMessageImpl, which is defined in the ebXML profile implementation and is not part of the JAXM API.EbXMLMessageImpl message = (EbXMLMessageImpl)factory. createMessage();For this profile, instead of using
Endpointobjects, you indicatePartyobjects for the sender and the receiver. This information will appear in the message's header, and the messaging provider will use it to determine where to send the message. The following lines of code use the methodssetSenderandsetReceiver, which are defined in theEbXMLMessageImplimplementation. These methods not only create aSOAPHeaderobject but also give it content. You can use these methods because yourSOAPMessageobject is anEbXMLMessageImplobject, giving you access to the methods defined inEbXMLMessageImpl.message.setSender(new Party("http://grand.products.com")); message.setReceiver(new Party("http://whiz.gizmos.com"));You can view the Javadoc comments for the ebXML and SOAP-RP profile implementations provided in this Java WSDP at the following location:
<JWSDP_HOME>/docs/jaxm/profile/com/sun/xml/messaging/If you are not using a profile or you want to set content for a header not covered by your profile's implementation, you need to follow the steps shown in the next section.
Adding Content to the Header
To add content to the header, you need to create a
SOAPHeaderElementobject. As with all new elements, it must have an associatedNameobject, which you create using the message'sSOAPEnvelopeobject.The following code fragment retrieves the
SOAPHeaderobject fromenvelopeand adds a newSOAPHeaderElementobject to it.SOAPHeader header = envelope.getHeader(); Name headerName = envelope.createName("Purchase Order", "PO", "http://www.sonata.com/order"); SOAPHeaderElement headerElement = header.addHeaderElement(headerName);At this point,
headercontains theSOAPHeaderElementobjectheaderElementidentified by theNameobjectheaderName. Note that theaddHeaderElementmethod both createsheaderElementand adds it toheader.Now that you have identified
headerElementwithheaderNameand added it toheader, the next step is to add content toheaderElement, which the next line of code does with the methodaddTextNode.headerElement.addTextNode("order");Now you have the
SOAPHeaderobjectheaderthat contains aSOAPHeaderElementobject whose content is "order".Adding Content to the SOAP Body
The process for adding content to the
SOAPBodyobject is the same for clients using a messaging provider as it is for standalone clients. This is also the same as the process for adding content to theSOAPHeaderobject. You access theSOAPBodyobject, add aSOAPBodyElementobject to it, and add text to theSOAPBodyElementobject. It is possible to add additionalSOAPBodyElementobjects, and it is possible to add subelements to theSOAPBodyElementobjects with the methodaddChildElement. For each element or child element, you add content with the methodaddTextNode.The section on the standalone client demonstrated adding one
SOAPBodyElementobject, adding a child element, and giving it some text. The following example shows adding more than oneSOAPBodyElementand adding text to each of them.The code first creates the
SOAPBodyElementobjectpurchaseLineItems, which has a fully-qualified namespace associated with it. That is, theNameobject for it has a local name, a namespace prefix, and a namespace URI. As you saw earlier, aSOAPBodyElementobject is required to have a fully-qualified namespace, but child elements added to it may haveNameobjects with only the local name.SOAPBody body = envelope.getBody(); Name bodyName = envelope.createName("PurchaseLineItems", "PO", "http://sonata.fruitsgalore.com"); SOAPBodyElement purchaseLineItems = body.addBodyElement(bodyName); Name childName = envelope.createName("Order"); SOAPElement order = purchaseLineItems.addChildElement(childName); childName = envelope.createName("Product"); SOAPElement product = order.addChildElement(childName); product.addTextNode("Apple"); childName = envelope.createName("Price"); SOAPElement price = order.addChildElement(childName); price.addTextNode("1.56"); childName = envelope.createName("Order"); SOAPElement order2 = purchaseLineItems.addChildElement(childName); childName = envelope.createName("Product"); SOAPElement product2 = order2.addChildElement(childName); product2.addTextNode("Peach"); childName = envelope.createName("Price"); SOAPElement price2 = order2.addChildElement(childName); price2.addTextNode("1.48");The JAXM code in the preceding example produces the following XML in the SOAP body:
<PO:PurchaseLineItems xmlns:PO="http://www.sonata.fruitsgalore/order"> <Order> <Product>Apple</Product> <Price>1.56</Price> </Order> <Order> <Product>Peach</Product> <Price>1.48</Price> </Order> </PO:PurchaseLineItems>Adding Content to the SOAPPart Object
If the content you want to send is in a file, JAXM provides an easy way to add it directly to the
SOAPPartobject. This means that you do not access theSOAPBodyobject and build the XML content yourself, as you did in the previous section.To add a file directly to the
SOAPPartobject, you use ajavax.xml.transform.Sourceobject from JAXP (the Java API for XML Processing). There are three types ofSourceobjects:SAXSource,DOMSource, andStreamSource. AStreamSourceobject holds content as an XML document.SAXSourceandDOMSourceobjects hold content along with the instructions for transforming the content into an XML document.The following code fragment uses JAXP API to build a
DOMSourceobject that is passed to theSOAPPart.setContentmethod. The first two lines of code get aDocumentBuilderFactoryobject and use it to create theDocumentBuilderobjectbuilder. Thenbuilderparses the content file to produce aDocumentobject, which is used to initialize a newDOMSourceobject.DocumentBuilderFactory dbFactory = DocumentBuilderFactory. newInstance(); DocumentBuilder builder = dbFactory.newDocumentBuilder(); Document doc = builder.parse("file:///music/order/soap.xml"); DOMSource domSource = new DOMSource(doc);The following two lines of code access the
SOAPPartobject (using theSOAPMessageobjectmessage) and set the newDOMSourceobject as its content. The methodSOAPPart.setContentnot only sets content for theSOAPBodyobject but also sets the appropriate header for theSOAPHeaderobject.SOAPPart soapPart = message.getSOAPPart(); soapPart.setContent(domSource);You will see other ways to add content to a message in the section on
AttachmentPartobjects. One big difference to keep in mind is that aSOAPPartobject must contain only XML data, whereas anAttachmentPartobject may contain any type of content.Sending the Message
When the connection is a
ProviderConnectionobject, messages have to be sent using the methodProviderConnection.send. This method sends the message passed to it and returns immediately. Unlike theSOAPConnectionmethodcall, it does not have to block until it receives a response, which leaves the application free to do other things.The
sendmethod takes only one argument, the message to be sent. It does not need to be given the destination because the messaging provider can use information in the header to figure out where the message needs to go.pcCon.send(message); pcCon.close();Adding Attachments
Adding
AttachmentPartobjects to a message is the same for all clients, whether they use a messaging provider or not. As noted in earlier sections, you can put any type of content, including XML, in anAttachmentPartobject. And because the SOAP part can contain only XML content, you must use anAttachmentPartobject for any content that is not in XML format.Creating an AttachmentPart Object and Adding Content
The
SOAPMessageobject creates anAttachmentPartobject, and the message also has to add the attachment to itself after content has been added. TheSOAPMessageclass has three methods for creating anAttachmentPartobject.The first method creates an attachment with no content. In this case, an
AttachmentPartmethod is used later to add content to the attachment.AttachmentPart attachment = message.createAttachmentPart();You add content to
attachmentwith theAttachmentPartmethodsetContent. This method takes two parameters, a JavaObjectfor the content, and aStringobject that gives the content type. Content in theSOAPBodypart of a message automatically has a Content-Type header with the value "text/xml" because the content has to be in XML. In contrast, the type of content in anAttachmentPartobject has to be specified because it can be any type.Each
AttachmentPartobject has one or more headers associated with it. When you specify a type to the methodsetContent, that type is used for the headerContent-Type.Content-Typeis the only header that is required. You may set other optional headers, such asContent-IdandContent-Location. For convenience, JAXM providesgetandsetmethods for the headersContent-Type,Content-Id, andContent-Location. These headers can be helpful in accessing a particular attachment when a message has multiple attachments. For example, to access the attachments that have particular headers, you call theSOAPMessagemethodgetAttachmentsand pass it the header or headers you are interested in.The following code fragment shows one of the ways to use the method
setContent. The JavaObjectbeing added is aString, which is plain text, so the second argument has to be "text/plain". The code also sets a content identifier, which can be used to identify thisAttachmentPartobject. After you have added content toattachment, you need to addattachmentto theSOAPMessageobject, which is done in the last line.String stringContent = "Update address for Sunny Skies " + "Inc., to 10 Upbeat Street, Pleasant Grove, CA 95439"; attachment.setContent(stringContent, "text/plain"); attachment.setContentId("update_address"); message.addAttachmentPart(attachment);The variable
attachmentnow represents anAttachmentPartobject that contains theStringstringContentand has a header that contains theString"text/plain". It also has aContent-Idheader with "update_address" as its value. And nowattachmentis part ofmessage.Let's say you also want to attach a jpeg image showing how beautiful the new location is. In this case, the second argument passed to
setContentmust be "image/jpeg" to match the content being added. The code for adding an image might look like the following. For the first attachment, theObjectpassed to the methodsetContentwas aString. In this case, it is a stream.AttachmentPart attachment2 = message.createAttachmentPart(); byte[] jpegData = . . .; ByteArrayInputStream stream = new ByteArrayInputStream( jpegData); attachment2.setContent(stream, "image/jpeg"); message.addAttachmentPart(attachment);The other two
SOAPMessage.createAttachmentmethods create anAttachmentPartobject complete with content. One is very similar to theAttachmentPart.setContentmethod in that it takes the same parameters and does essentially the same thing. It takes a JavaObjectcontaining the content and aStringgiving the content type. As withAttachmentPart.setContent, theObjectmay be aString, a stream, ajavax.xml.transform.Sourceobject, or ajavax.activation.DataHandlerobject. You have already seen an example of using aSourceobject as content. The next example will show how to use aDataHandlerobject for content.The other method for creating an
AttachmentPartobject with content takes aDataHandlerobject, which is part of the JavaBeansActivation Framework (JAF). Using a
DataHandlerobject is fairly straightforward. First you create ajava.net.URLobject for the file you want to add as content. Then you create aDataHandlerobject initialized with theURLobject and pass it to the methodcreateAttachmentPart.URL url = new URL("http://greatproducts.com/gizmos/img.jpg"); DataHandler dh = new DataHandler(url); AttachmentPart attachment = message.createAttachmentPart(dh); attachment.setContentId("gyro_image"); message.addAttachmentPart(attachment);You might note two things about the previous code fragment. First, it sets a header for
Content-IDwith the methodsetContentId. This method takes aStringthat can be whatever you like to identify the attachment. Second, unlike the other methods for setting content, this one does not take aStringforContent-Type. This method takes care of setting theContent-Typeheader for you, which is possible because one of the things aDataHandlerobject does is determine the data type of the file it contains.Accessing an AttachmentPart Object
If you receive a message with attachments or want to change an attachment to a message you are building, you will need to access the attachment. When it is given no argument, the method
SOAPMessage.getAttachmentsreturns ajava.util.Iteratorobject over all theAttachmentPartobjects in a message. The following code prints out the content of eachAttachmentPartobject in theSOAPMessageobjectmessage.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(""); }Summary
In this section, you have been introduced to the basic JAXM API. You have seen how to create and send SOAP messages as a standalone client and as a client using a messaging provider. You have walked through adding content to a SOAP header and a SOAP body and also walked through creating attachments and giving them content. In addition, you have seen how to retrieve the content from the SOAP part and from attachments. In other words, you have walked through using the basic JAXM API.
SOAP Faults
This section expands on the basic JAXM API by showing you how to use the API for creating and accessing a SOAP Fault element in an XML message.
Overview
If you send a message that was not successful for some reason, you may get back a response containing a SOAP Fault element that gives you status information, error information, or both. There can be only one SOAP Fault element in a message, and it must be an entry in the SOAP Body. The SOAP 1.1 specification defines only one Body entry, which is the SOAP Fault element. Of course, the SOAP Body may contain other Body entries, but the SOAP Fault element is the only one that has been defined.
A
SOAPFaultobject, the representation of a SOAP Fault element in the JAXM API, is similar to anExceptionobject in that it conveys information about a problem. However, aSOAPFaultobject is quite different in that it is an element in a message'sSOAPBodyobject rather than part of thetry/catchmechanism used forExceptionobjects. Also, as part of theSOAPBodyobject, which provides a simple means for sending mandatory information intended for the ultimate recipient, aSOAPFaultobject only reports status or error information. It does not halt the execution of an application the way anExceptionobject can.Various parties may supply a
SOAPFaultobject in a message. If you are a standalone client using the SAAJ API, and thus sending point-to-point messages, the recipient of your message may add aSOAPFaultobject to the response to alert you to a problem. For example, if you sent an order with an incomplete address for where to send the order, the service receiving the order might put aSOAPFaultobject in the return message telling you that part of the address was missing.In another scenario, if you use the JAXM 1.1_01 API in order to use a messaging provider, the messaging provider may be the one to supply a
SOAPFaultobject. For example, if the provider has not been able to deliver a message because a server is unavailable, the provider might send you a message with aSOAPFaultobject containing that information. In this case, there was nothing wrong with the message itself, so you can try sending it again later without any changes. In the previous example, however, you would need to add the missing information before sending the message again.A
SOAPFaultobject contains the following elements:
- a fault code -- always requiredThe SOAP 1.1 specification defines a set of fault code values in section 4.4.1, which a developer may extend to cover other problems. The default fault codes defined in the specification relate to the JAXM API as follows:
VersionMismatch-- the namespace for aSOAPEnvelopeobject was invalidMustUnderstand-- an immediate child element of aSOAPHeaderobject had itsmustUnderstandattribute set to"1", and the processing party did not understand the element or did not obey itClient-- theSOAPMessageobject was not formed correctly or did not contain the information needed to succeedServer-- theSOAPMessageobject could not be processed because of a processing error, not because of a problem with the message itself- a fault string -- always requireda human readable explanation of the fault
- a fault actor -- required if the
SOAPHeaderobject contains one or moreactorattributes; optional if no actors are specified, meaning that the only actor is the ultimate destinationThe fault actor, which is specified as a URI, identifies who caused the fault. For an explanation of what an actor is, see the section Intermediate Destinations.- a
Detailobject -- required if the fault is an error related to theSOAPBodyobject If, for example, the fault code is "Client", indicating that the message could not be processed because of a problem in theSOAPBodyobject, theSOAPFaultobject must contain aDetailobject that gives details about the problem. If aSOAPFaultobject does not contain aDetailobject, it can be assumed that theSOAPBodyobject was processed successfully.Creating and Populating a SOAPFault Object
You have already seen how to add content to a
SOAPBodyobject; this section will walk you through adding aSOAPFaultobject to aSOAPBodyobject and then adding its constituent parts.As with adding content, the first step is to access the
SOAPBodyobject.SOAPEnvelope envelope = msg.getSOAPPart().getEnvelope(); SOAPBody body = envelope.getBody();With the
SOAPBodyobjectbodyin hand, you can use it to create aSOAPFaultobject with the following line of code.SOAPFault fault = body.addFault();The following code uses convenience methods to add elements and their values to the
SOAPFaultobjectfault. For example, the methodsetFaultCodecreates an element, adds it tofault, and adds aTextnode with the value"Server".fault.setFaultCode("Server"); fault.setFaultActor("http://gizmos.com/orders"); fault.setFaultString("Server not responding");The
SOAPFaultobjectfaultcreated in the previous lines of code indicates that the cause of the problem is an unavailable server and that the actor at "http://gizmos.com/orders" is having the problem. If the message were being routed only to its ultimate destination, there would have been no need for setting a fault actor. Also note thatfaultdoes not have aDetailobject because it does not relate to theSOAPBodyobject.The following code fragment creates a
SOAPFaultobject that includes aDetailobject. Note that aSOAPFaultobject may have only oneDetailobject, which is simply a container forDetailEntryobjects, but theDetailobject may have multipleDetailEntryobjects. TheDetailobject in the following lines of code has twoDetailEntryobjects added to it.SOAPFault fault = body.addFault(); fault.setFaultCode("Client"); fault.setFaultString("Message does not have necessary info"); Detail detail = fault.addDetail(); Name entryName = envelope.createName("order", "PO", "http://gizmos.com/orders/"); DetailEntry entry = detail.addDetailEntry(entryName); entry.addTextNode("quantity element does not have a value"); Name entryName2 = envelope.createName("confirmation", "PO", "http://gizmos.com/confirm"); DetailEntry entry2 = detail.addDetailEntry(entryName2); entry2.addTextNode("Incomplete address: no zip code");Retrieving Fault Information
Just as the
SOAPFaultinterface provides convenience methods for adding information, it also provides convenience methods for retrieving that information. The following code fragment shows what you might write to retrieve fault information from a message you received. In the code fragment,newmsgis theSOAPMessageobject that has been sent to you. Because aSOAPFaultobject must be part of theSOAPBodyobject, the first step is to access theSOAPBodyobject. Then the code tests to see if theSOAPBodyobject contains aSOAPFaultobject. If so, the code retrieves theSOAPFaultobject and uses it to retrieve its contents. The convenience methodsgetFaultCode,getFaultString, andgetFaultActormake retrieving the values very easy.SOAPBody body = newmsg.getSOAPPart().getEnvelope().getBody(); if ( body.hasFault() ) { SOAPFault newFault = body.getFault(); String code = newFault.getFaultCode(); String string = newFault.getFaultString(); String actor = newFault.getFaultActor();Next the code prints out the values it just retrieved. Not all messages are required to have a fault actor, so the code tests to see if there is one. Testing whether the variable
actorisnullworks because the methodgetFaultActorreturnsnullif a fault actor has not been set.System.out.println("SOAP fault contains: "); System.out.println(" fault code = " + code); System.out.println(" fault string = " + string); if ( actor != null ) { System.out.println(" fault actor = " + actor); } }The final task is to retrieve the
Detailobject and get itsDetailEntryobjects. The code uses theSOAPFaultobjectnewFaultto retrieve theDetailobjectnewDetail, and then it usesnewDetailto call the methodgetDetailEntries. This method returns thejava.util.Iteratorobjectit, which contains all of theDetailEntryobjects innewDetail. Not allSOAPFaultobjects are required to have aDetailobject, so the code tests to see whethernewDetailisnull. If it is not, the code prints out the values of theDetailEntryobject(s) as long as there are any.Detail newDetail = newFault.getDetail(); if ( newDetail != null) { Iterator it = newDetail.getDetailEntries(); while ( it.hasNext() ) { DetailEntry entry = (DetailEntry)it.next(); String value = entry.getValue(); System.out.println(" Detail entry = " + value); } }In summary, you have seen how to add a
SOAPFaultobject and its contents to a message as well as how to retrieve the information in aSOAPFaultobject. ASOAPFaultobject, which is optional, is added to theSOAPBodyobject to convey status or error information. It must always have a fault code and aStringexplanation of the fault. ASOAPFaultobject must indicate the actor that is the source of the fault only when there are multiple actors; otherwise, it is optional. Similarly, theSOAPFaultobject must contain aDetailobject with one or moreDetailEntryobjects only when the contents of theSOAPBodyobject could not be processed successfully.
|
Home
TOC Index |
|
This tutorial contains information on the 1.0 version of the Java Web Services Developer Pack.
All of the material in The Java Web Services Tutorial is copyright-protected and may not be published in other works without express written permission from Sun Microsystems.