SAAJ Peter Komisar ©   Conestoga College      v.1.2  /  2005
references: Java JWSDP 1.5, SAAJ Documentation,
/jwsdp-1.5/saaj/docs/index.html 'Java Web Services Tutorial',
M.Fisher et.al, the Sun Web Site 'Using Java API for XML Messaging'

http://developers.sun.com/sw/building/tech_articles/api_xmlmessaging.html
'Writing Web Application Deployment Descriptors', BEA,
http://e-docs.bea.com/wls/docs61/webapp/webappdeployment.html


Overview

JAX-RPC provides a 'hands-off' approach to programming Web services
where a Java developer needs only program the RMI interface and provide
an implementation class for this interface. This simplicity is traded for a
complicated compilation, packaging and deployment phase which ends up
putting 'distance' between the developer and the code that is ultimately
deployed as a web service.


What is SAAJ?

SAAJ, the SOAP with Attachments API for Java, on the other brings
the developer a step closer to controlling the actual processes involved
in creating Web services. SAAJ allows us to build and exchange SOAP
messages over the web. This is API is in fact used behind the scenes
in JAX-RPC.  


SAAJ Versions

The SAAJ API that is shipped with the Java Web Services Development
Package version 1.5, ( jwsdp-1.5 ) is version 1.2.1. SAAJ depends on
packages found in the JAXP API and also uses packages in the JavaMail
and JAF ( JavaBeans Activation Framework) APIs.


The Key SAAJ Package

The key package for SAAJ is the Java extension package, javax.xml.soap.

Key SAAJ Package

A Brief Review of Key SOAP Constructs

Because SAAJ is intimately associated with the constructs of the SOAP
API we should refresh our memories as to what the basic parts of a SOAP
message are. If the structure of the SOAP message is not clear in our minds
before programming in SAAJ, the programming model will be less clear than
it should be. We review the parts of a SOAP message in the context of how
SOAP messages are viewed from inside the SAAJ API. First let us recall
a SOAP example from our earlier SOAP note.

A quick look shows the an outer root Envelope element holding a Body
element. The Body element holds a nested PurchaseOrder element.
This element's name is fully qualified using the namespace prefix, 'myNS'
that has been assigned a URI. Notice the sub-elements within this element,
<item>, <quantity> and <description> are not themselves qualified explicitly,
but will by default have the namespace of the parent element.


SOAP Example from the Apache SOAP User Guide

<soap:Envelope xmlns="http://xml.apache.org/axis/wsdd/"
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java">
<soap:Body>
<myNS:PurchaseOrder xmlns:myNS="http://commerce.com/PO">
<item>SK001</item>
<quantity>1</quantity>
<description>Sushi Knife</description>
</myNS:PurchaseOrder>
</soap:Body>
</soap:Envelope>

The optional header element is not present in this example. The following
sample shows a Header element in a SOAP message. Notice the <Header>
element has a namespace qualified nested header element that is named
by the application.


SOAP Example with Header Element

<?xml version="1.0"?>
<soap:Envelope xmlns:soap="
xmlns:env="http://www.w3.org/2003/05/soap-envelope"
soap:encodingStyle="http://www.w3.org/2003/05/soap-encoding">
<soap:Header>
<cs:creditStatus
xmlns:cs="http://www.example.com/status/" soap:mustUnderstand="true">
 1
</cs:creditStatus>
</soap:Header>
...
...
</soap:Envelope>


Following is a SOAP 1.1 Fault Message.


Sample of a SOAP 1.1 Fault Message

<env:Envelope xmlns:SOAP-ENV = "http://schemas.xmlsoap.org/soap/envelope/">
                      env:encodingStyle =  "http://schemas.xmlsoap.org/soap/encoding/">
   <env:Body>
      <env:Fault>
         <faultcode> env:VersionMismatch</faultcode>
         <faultstring> Cannot process other than SOAP 1.1. messages </faultstring>
         <faultactor>http://OleSoap.com/getOldPublications</faultactor>
      </env:Fault>
   </env:Body>
<env:Envelope>


Notice in the following SOAP 1.2 Fault Message there is a nested <Detail>
element that provides additional information regarding the fault.


Sample of a SOAP 1.2 Fault Message
<?xml version='1.0' ?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">
<env:Body>
<env:Fault>
<env:Code>
<env:Value>env:Sender</env:Value>
<env:Subcode>
<env:Value>rpc:BadArguments</env:Value>
</env:Subcode>
</env:Code>
<env:Reason>
<env:Text xml:lang="en-US">Processing error</env:Text>
<env:Text xml:lang="cs">Chyba zpracování</env:Text>
</env:Reason>
<env:Detail>
<e:myFaultDetails
xmlns:e="http://travelcompany.example.org/faults" >
<e:message>Name does not match card number</e:message>
<e:errorcode>999</e:errorcode>
</e:myFaultDetails>
</env:Detail>
</env:Fault>
</env:Body>
</env:Envelope>


The Tree View of SOAP Messages

If we consider that a SOAP message may be composed of a message part
that is SOAP and a non-XML part that is a MIME attachment we can think of
the overall SOAP message as consisting of a SOAP part and optionally, an
Attachment part.

Nodal View of a SOAP Message

SOAP Message

      |_____ SOAP Part
      |_____ Attachment Part


We can expand this tree by further decomposing the SOAP Part into a SOAP
envelope which further divides int an optional Header element and a SOAP
body. The attachment part, if present is required to have a content header and
associated content.


Expanded View of a SOAP Message

SOAP Message

      |___ SOAP Part
     |        |___SOAP Envelope
     |                |_________SOAP Header
// ?
     |                |_________SOAP Body     
     |
     |
     |___ Attachment Part
// *
                |____ContentHeader(s)      
                                |____Content                      


Corresponding SAAJ Classes & Interfaces


Each of the elements in the above tree diagram has a corresponding SAAJ
class that represents that element or part. Leaving aside the corresponents
that will populate these element classes, we can repeat the tree a final time
including the corresponding SAAJ classes.


Expanded View of a SOAP Message with Corresponding SAAJ classes / interfaces

SOAP Message 
// SOAPMessage
      |__SOAP Part
// SOAPPart
     |     |__SOAP Envelope // SOAPMessage
     |          |_________SOAP Header// SOAPMessage // optional   
     |          |_________SOAP Body 
// SOAPMessage    
     |
     |
     |__Attachment Part
// AttachmentPart
                |____ContentHeaders // MimeHeaders     
                                                            |___ContentHeader // MimeHeader

                    

SAAJ API Overview



SAAJ Structural Classes

If we reconsider our first Tree describing a SOAP message, we collect structural
elements that are represented by Java classes.

Nodal View of a SOAP Message

SOAP Message

      |_____ SOAP Part
      |_____ Attachment Part



Major SAAJ Structural Classes

 SAAJ Class
 Description
SOAPMessageFactory
- a factory for creating new SOAPMessage objects
SOAPMessage
 - root for all SOAP messages
SOAPPart
 - container for SOAP portion of SOAPMessage object
AttachmentPart
 - container for the Attachment Part of a SOAPMessage


The major SAAJ classes supply methods that return objects that implement
the following interfaces. These SAAJ interfaces are used to populate empty
SOAP messages.


SAAJ Interfaces

 SAAJ Interface
 Description
 
Node
 
SOAP's own extension  of the generic Node interface. Supplies
methods such as detachNode( ) as well as providing it's parent,
generic Node's methods, for getting, setting  and interrogating
nodes of Tree structures. 
Envelope Interfaces
SOAPEnvelope - container for the SOAPHeader and SOAPBody objects
SOAPHeader - represents a SOAP Header Element`
SOAPHeaderElement - represents the contents in the SOAP header part
SOAPBody - container for SOAPBodyElement objects.
SOAPBodyElement

- represents the element content of a SOAPBody object
Fault Interfaces
SOAPFault - represents Fault element containing error information
SOAPFaultElement -represents an element in a Fault Message
Detail -a SOAPFaultElement for providing detailed error information
DetailEntry
- the content for a Detail object
General  
Name - represents an XML name, methods get the namespace
  prefix, and the local & namespace-qualified names. 
Text
- "represents a text node, in element content or a comment.
Others  
SOAPFactory
- factory that creates a variety of SOAP XML tree objects
- replaces deprecated SOAPElementFactory

SOAPConstants
- interface supplies constants for three SOAP namespace
 URIs including SOAP encoding, SOAP envelope and
 the 'next' URL used in signals to SOAP intermediaries.

SOAPElement
 - this interface is the parent of all the standard SOAP
   element interfaces and defines many get /set methods
   used for loading and retrieving information to and from
   SOAP messages.


SOAPElement

The SOAPElement is the parent interface to the different interfaces that are used to
represent standard SOAP elements. These include the following,
Detail, DetailEntry,
SOAPBody, SOAPBodyElement, SOAPEnvelope, SOAPFault, SOAPFaultElement,
SOAPHeader and SOAPHeaderElement. The interface defines a large set of accessor
and mutator methods that are made available through inheritence to it's sub-interfaces.
Following is a summary of it's method, the details of which are available in the SAAJ
API documentation. Their names are for the most part self-descriptive.


SOAPElement Methods


SOAP Connectivity Classes

SOAP uses a SOAP connection factory to generate a SOAP Connection
with a remote URL. SAAJ is able to make simple request / response style
connections. Following are the two classes SAAJ supplies to create
connections.

SOAP Connectivity Classes

 Class
 Description
SOAPConnectionFactory  - a factory that creates SOAPConnection objects
SOAPConnection - used to create a point-to-point connection for
  sending messages to a remote URL


Mime Attachment Classes

 
The second part of the SOAP Message is the attachment parts that allow MIME
attachments to be sent with a SOAP message. There is a SAAJ class for the
to represent Mime Attachments, and header and headers classes to hole Mime
names value pairs.
 - object that stores a MIME header name and it's value
MimeHeaders - container for MimeHeader objects


 Class
 Description
AttachmentPart - a single attachment to a SOAPMessage object
MimeHeader - holds a Mime name and value pair
MimeHeaders - container to collect MimeHeader objects
.


How SAAJ Classes Are Used


The SOAPMessageFactory

The SOAPMessageFactory has a newInstance( ) method to create
SOAPFactory object. The SOAPFactory object two createMessage( )
methods, one which creates an empty SOAPMessage based on default
values and one which takes a MimeHeaders object and a InputStream
to create a SOAPMessage with associated values.


SOAPMessageFactory Methods // all throw SOAPException

newInstance( )      // returns a new MessageFactory object
createMessage( )  // returns a SOAPMessage object using default values
createMessage( )  // returns a SOAPMessage with predetermined values

Using the SAAJ API generally begins with the creation of a SOAP
message in the form of a SOAPMessage object. The following formulation
is used to obtain a SOAPMessage in default form. This is basically a
'blank' SOAP message waiting to be filled in.


Formulation for obtaining a SOAPMessage object in Default Form

MessageFactory factory = MessageFactory.newInstance( );
SOAPMessage message = factory.createMessage( );


The SOAPMessage
Class   // from the SAAJ API Documentation

A SOAP Message object will by default contain the following:

The SOAP part of a message can be retrieved by calling the method 
getSOAPPart( ) on the SOAPMessage object that was returned by
the factory. 


Example     SOAPPart soapPart = message.getSOAPPart( );


Subsequently, the SOAPPart object is used to get the Envelope. From
the envelope the Body and Header objects are obtained. 


Example
    SOAPEnvelope envelope = soapPart.getEnvelope( );
                 SOAPBody body = envelope.getBody( );
                 SOAPHeader header = envelope.getHeader( );


The SOAPFactory Class

The SOAPFactory class deprecates the SOAPElementFactory. The latter
hints at what this factory does but is an incomplete description as the
the factory supplies more than just elements. This factory class also
supplies different common features that are needed by the various
elements that may be found inside a SOAP message.

The newInstance( ) method returns a new instance of the SOAPFactory.
The other methods of SOAPFactory are used to create names, either local
or fully-qualified, to create elements with or without namespace qualification
and to create Detail objects for use in conjunction with Fault messages. All
the methods are declared as throwing SOAPException. The methods are
listed below.

SOAPFactory Methods  // all methods throw SOAPException

createElement(Name  name)
// returns a SOAPElement with given Name object
createElement(String localName)
// returns a SOAPElement with given local name
createElement(String localName,String prefix,String uri)
// returns a SOAP Element with given local name, qualified with given prefix and namespace URI

createName(String localName)
// returns a Name object, with given local name
 createName(String localName, String prefix, String uri )
// returns a Name object with given local name, prifix and namespace URI

createDetail( )
// creates a Detail object which acts as a container for DetailEntry objects

newInstance( )
// returns a SOAP Factory instance


Following is the formula we need to use to obtain a SOAPFactory object.


Formula for creating a SOAPFactory object

SOAPFactory soapFactory = SOAPFactory.newInstance( );
// use it to create elements, names or details


Adding Content to the SOAPHeader Object // populating the Header

Since the header is an optional element that is supplied by default.
it may be removed if it is not being used for anything. The detachNode( )
method may be used to detach the header object from the underlying
tree data structure.


Example     header.detachNode( );

To populate the Header object the Header object needs to be obtained
from the message object and then a SOAPFactory instance used to
create a fully-qualified name and an element to serve as a Header element.
In the following example from the JWSDP SAAJ tutorial an attribute is added
as well.

Sun Java Web Services Tutorial SAAJ Header Example

SOAPHeader header = message.getSOAPHeader( ); 
            // get header object

Name headerName = soapFactory.createName("Claim",
       "wsi","http://ws-i.org/schemas/conformanceClaim/");
           
// using factory to create a fully-qualified name
SOAPHeaderElement headerElement =
       header.addHeaderElement(headerName);
        // header element with given name added to header
headerElement.addAttribute(soapFactory.createName("conformsTo"),
       "http:/ws-i.org/profile/basic1.0"); 
           // attribute (name & value) added to header element

The Sun Tutorial shows the SOAP XML code that the above Java
code creates inside the SOAP message.

Sun Java Web Services Tutorial SOAP XML Header Example

<SOAP-ENV:Header>
  <wsi:Claim conformsTo= "http:/ws-i.org/profile/basic1.0/"
    xmlns:wsi="http://ws-i.org/schema/conformanceClaim/"/>
</SOAP-ENV:Header>


Adding Content to the SOAPBody Object

Adding content to the body of the SOAP message follows the same
sort of process that was used to add content to the header. The
following code from the Sun site's Java Web Services Tutorial
shows the steps involved.

One needs to distinguish between the 'SOAPBody' object and the
SOAPBodyElement object. The SOAPBody object must first be
obtained  and then elements are added to it. Top level elements
inside the SOAP message are represented by 'SOAPBodyElement'
objects. Sub-elements can be nested inside 'SOAPBodyElement'
objects. Text is added to each element using the addTextNode( )
method.

The sample begins by getting a 'handle' to the SOAPBody object,
then creating a name that will be used as the XML indentifier for
for a SOAPBodyElement object. The SOAPBodyElement is added
to the SOAPBody object with the addBodyElement(  ) method.
The top-level SOAPBodyElement must have a fully qualified name.

Following is the sample that get's the SOAPBody, provides a fully-
qualified name for the SOAPBodyElement and adds this element
to the body using the addBodyElement( ) method.

Sun Java Web Services Tutorial Sample 1

SOAPBody body = soapFactory.getSOAPBody( );
Name bodyName = soapFactory.createName("PurchaseLineItems",
"PO", "http://sonata.fruitsgalore.com");
SOAPBodyElement purchaseLineItems =
 body.addBodyElement(bodyName);


In the next example, the Java code causes two Order elements to
be added to the Body element, <PO:PurchaseLineItems>
. Each of
these Order elements have further elements nested, <Product>
and <Price> elements. These will hold text values.


Sun Java Web Services Tutorial Sample 2

Name childName = soapFactory.createName("Order");
SOAPElement order =
purchaseLineItems.addChildElement(childName);
// In the XML, an 'Order' element is added to the body element

childName = soapFactory.createName("Product");
SOAPElement product = order.addChildElement(childName);
product.addTextNode("Apple");
// a product element with text "Apple" is added to the Order element

childName = soapFactory.createName("Price");
SOAPElement price = order.addChildElement(childName);
price.addTextNode("1.56");
// a 'Price' element is added to the 'Order' element with a text set to "1.56"

childName = soapFactory.createName("Order");
SOAPElement order2 =
purchaseLineItems.addChildElement(childName);
// the process repeats, a secord Order element
// note though it has it's own reference identifier in the Java code

childName = soapFactory.createName("Product");
SOAPElement product2 = order2.addChildElement(childName);
product2.addTextNode("Peach");
// "ditto" for Product

childName = soapFactory.createName("Price");
SOAPElement price2 = order2.addChildElement(childName);
price2.addTextNode("1.48");
// "ditto' for second Price element


Following is the XML that this Java code creates. This will
supply the contents of A SOAP Body tag.


Sun Java Web Services Tutorial Sample 3

<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>

When this XML code is sent as a SOAP message the
element will take it's place inside the message as is
shown in the following example.

Example

<SOAP-ENV:Envelope
     xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
     
<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>
</SOAP-ENV:Envelope>

Sending SOAP Messages With SAAJ

The first objective of the SAAJ developers was to supply a Java API
for creating SOAP messages, leaving messaging to be handled by
Java APIs such as JAXM (Java API for XML Messaging).  SAAJ
does however supply a basic connection mechanism for request-
response messaging.

The process for sending a message using SAAJ is is simple and
elegant. First a connection is obtained, and a call is made on an
endpoint, sending the message in the process.

Following is the Factory pattern used to obtain a SOAPConnectionFactory
object which then is used to get a connection.

Formula for Getting a SOAPConnection Object

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


Once the endpoint URL has been encapsulated in the form of a Java URL
it and the message that was created earlier can serve as arguments to the
call( ) method, invoked on the SOAPConnection object. The SOAP response
is returned as a SOAPMessage object by the call( ) method.

Sending the SOAP Message  // need  import java.net.*; for URL class

URL endpoint =new URL("https://www-3.ibm.com/services/uddi/inquiryapi");
SOAPMessage response = connection.call(message, endpoint);


The connection should be closed when finished to release system resources.

Example     connection.close( );

We can let Sun put it all together for us after which we can build our
own

example using other techniques that SAAJ supplies.

Receiving a SOAP Message

The response that comes back from the call( )  method needs to be
unravelled in a manner similar to how the message has been put together.
The getSOAPBody( )  is called on the response message to obtain the
SOAPBody object. Child elements are returned to Iterator using
getChildElements(  ). An Iterator is a java.util class of undefined length
that can collect a variable length, set of objects. The Iterator class
supplies the hasNext( ) and next( ) methods that are used to return the
objects that are stored in the Iterator. Following is the typical form the
Iterator takes.

General Form of Taken to Access an Iterator

while(iterator.hasNext( )){
 Sometype typeObject  =  (SomeType) iterator.next( );
   // do something with typeObject
   }

Following is a sample from the Sun Java Web Service Tutorial that
shows the Iterator being used to capture SOAPBodyElements. The
Sun Java Web Services sample is built with the knowledge that
there is only one SOAPBodyElement, so they are able to omit
the while loop.


Sun Java Web Services Tutorial Sample

  Iterator iterator = soapBody.getChildElements(bodyName);
  SOAPBodyElement bodyElement =
                (SOAPBodyElement)iterator.next( );
 
           String lastPrice = bodyElement.getValue( );
            System.out.print("The last price for SUNW is ");
            System.out.println(lastPrice);

Sun's Java Web Service Tutorial's Request.java Example

Over what was discussed earlier, the code adds import statements, a main
method, and a try/catch block for exception handling. In order to run the
following code, the javax.xml.soap package has to be made available.

You can hard code the paths into your system or find the package in
your JWSDP directory using a -cp or -classpath switch at the command
line. Alternatively, just place the saaj jars into the <jdk_home>/jre/lib/ext
directory.

Sun Java Web Service Tutorial Example 

import javax.xml.soap.*;
import java.util.*;
import java.net.URL;

public class Request {
    public static void main(String[] args)  {
        try {
            SOAPConnectionFactory soapConnectionFactory =
                SOAPConnectionFactory.newInstance( );
            SOAPConnection connection =
                soapConnectionFactory.createConnection( );
            SOAPFactory soapFactory =
                SOAPFactory.newInstance( );

            MessageFactory factory =
                MessageFactory.newInstance( );
            SOAPMessage message = factory.createMessage( );

            SOAPHeader header = message.getSOAPHeader( );
            SOAPBody body = message.getSOAPBody( );
            header.detachNode( );

            Name bodyName = soapFactory.createName(
                "GetLastTradePrice", "m",
                "http://wombats.ztrade.com");
            SOAPBodyElement bodyElement =
                body.addBodyElement(bodyName);

            Name name = soapFactory.createName("symbol");
            SOAPElement symbol =
                bodyElement.addChildElement(name);
            symbol.addTextNode("SUNW");

            URL endpoint = new URL
                ("http://wombat.ztrade.com/quotes");
            SOAPMessage response =
                connection.call(message, endpoint);

            connection.close( );

            SOAPBody soapBody = response.getSOAPBody( );

            Iterator iterator =
                soapBody.getChildElements(bodyName);
            SOAPBodyElement bodyElement2 =
                (SOAPBodyElement)iterator.next( );
            String lastPrice = bodyElement2.getValue( );
    // 2nd SOAPBodyElement name changed to remedy error report

            System.out.print("The last price for SUNW is ");
            System.out.println(lastPrice);

        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}


Other SAAJ Techniques For Loading SOAP Message


While building SOAP Messages step by step from inside a Java
program might be described as 'cool', it remains quite a tedious
process. As well, for large and complex messages, the process
would be prone to error if under a human's control. Additionally,
developers who are conversant in XML but not Java would be
more at ease building SOAP messages in XML.

Fortunately, SAAJ supplies techniques for loading preformed
SOAP messages into SOAP Envelopes.


Adding a File to the SOAPPart Object

An XML file can be associated with a SOAPPart object. This
involves utilizing the JAXP DOM API. A DocumentBuilderFactory
is used to obtain a DocumentBuilder object which is used to parse
an XML file and store the result as a Document object. The Document
object is encapsulated as a DOMSource object which is set as the
content of the SOAPPart object.

These steps are illustrated in the following Sun Tutorial example.


Sun Java Web Services Tutorial Example


DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance( );
dbFactory.setNamespaceAware(true);
DocumentBuilder builder = dbFactory.newDocumentBuilder( );
Document document = builder.parse( "file:///music/order/soap.xml");
DOMSource domSource = new DOMSource(document);

SOAPPart soapPart = message.getSOAPPart( );
soapPart.setContent(domSource);


The Sun's tutorial cautions that the file represents the whole SOAP
message and must supply everything that is needed to form the
SOAPPart object. As such,  the XML file needs to include the
<SOAP-ENV: Envelope> and <SOAP-ENV:Body> elements.

// Using this technique the Document needs to include the SOAP Envelope & Body Elements


Adding a Document Object to the SOAPBody Object

A second variation on this technique allows a Document object to
be added to a SOAPBody object. This allows us to skip the feeding
the Document object into the DOMSource instance. Instead of obtaining
a SOAPPart object, the SOAPBody object is obtained from the message
instance.

Example // picking up from the fourth line of code in the above example

SOAPBody body = message.getSOAPBody( );
SOAPBodyElement docElement = body.addDocument(document);


// because the element by the document is a SOAPBodyElement it is
// assumed that the document in this case will not include the <Envelope>
// and Body elements.


Adding Attributes

The SOAPElement interface is the parent interface for all the structural,
SOAP elements. Accordingly, it's addAttribute(  ) method can be called
on all the standard SOAP elements.
Following is the form adding an
attribute takes.

Sun Java Web Service Tutorial Example 

Name attributeName = envelope.createName("id");
// creating attribute name
person.addAttribute(attributeName,"Person7"  );
// adding attribute to person object, initialized to "Person7"

// person is a SOAPElement object reference


Associated XML

<person id="Person7"> ... </person>

Shown earlier, the SOAPElement interface defines a get methods
for retrieving attribute values from an element. It supplies the
following methods. 

The getAllAttributes( ) method returns an Iterator object that
can be traversed to obtain all the attribute names of an element.
Once a name is obtained a value for it can be obtained using
the getAttributeValue( ) method.

Sun Java Web Services Tutorial Example // altered

Iterator iterator  = person.getAllAtributes( );
while(iterator.hasNext( )){
  Name attributeName = (Name) iterator.next( );
  String attributeValue = person.getAttributeValue(attributeName);
  // . . .
  }

SOAP Defined Header Attributes

The SOAPHeaderElement Interface defines methods for accessing
SOAP defined attributes. These get and set methods are shown
below.


Setting and Retrieving Fault Information


The Fault Element is represented in SAAJ by it's own SOAPFault
object. The way it is created and populated is fairly straight forward
as is shown in the following example from the Java Web Services
Tutorial.

Sun's Java Web Services Tutorial Example

SOAPBody body = message.getSOAPBody( );
SOAPFault fault = body.addFault( );
//assumes soapFactory is already created
Name faultName = soapFactory.createName("Server",
      "", SOAPConstants.URI_NS_SOAP_ENVELOPE);
// example of SOAP constant, here representing the SOAP Envelope namespace URI
fault.setFaultCode(faultName);
fault.setFaultActor("http://gizmos.com/orders");
fault.setFaultString("Server Not Responding");


Retrieving the Fault information mirrors the code used to set
the Fault object. A test to see if the body has a Fault is done
before calling the getFault( ) method.

// avoids a null pointer exception

Sun's Java Web Services Tutorial Example

SOAPBody body = message.getSOAPBody( );
  if (body.hasFault( )){
     //assumes soapFactory is already created
     SOAPFault newFault = body.getFault( );
     // example of SOAP constant, here representing the SOAP Envelope namespace URI 
    
Name code = newFault.getFaultCodeAsName( );
     String string = newFault.getFaultString( );
     String actor = newFault.getFaultActor( );
     // . . .
 }

We have left out dealing with Details, the SAAJ interface that stores
detail information. To see an example of a Detail object being added
to a Fault object and later accessed, see the Java Web Services
Tutorial.


Adding Attachments With SAAJ


Attachments are needed to represent any content that is not
XML. Attachments, of which there may be zero or more, may
have one or more headers associated with them. The only
required header is for Content-Type. The Content-Id and
Content-Location headers are optional.
 
Adding attachments with SAAJ is is also straight forward as is
shown in the following example where a String object is created
to represent plain text data transported as an attachment.


Sun's Java Web Services Tutorial Example


AttachmentPart attachment = message.createAttachmentPart( );
// an AttachmentPart object is created
String stringContent = " I hope Sun doesn't sue me " +
"for using so many of their examples.";
// some extra-XML content
attachment.setContent(stringContent, "text/plain" );
// associates MIME type with content
attachment.setContentID("instructors_plea");
message.addAttachmentPart(attachment);


The above example results in an attachment being created
with the string represented by 'stringContent' as it's data
part and with a header that identifies Content-Type as "text/plain",
and a header for Content-Id set to "instructors_plea".


Sun's Java Web Services Tutorial Example


java.util.Iterator iterator = message.getAttachments( );
while(iterator.hasNext( )){
   AttachmentPart attachment = (AttachmentPart) iterator.next( );
   String id = attachment.getContentID;
   Stirng type= attachment.getContentType( );
   System.out.println("Attachment " + id  + 
            " has content type " + type);
  if(type == "text/plain"){
           Object content = getContent( );
            System.out.println("Attachment " +
            "contains:\n" + content);
          }
       }


// The Sun tutorial also shows using the DataHandler class from the
// JAF (JavaBeans Activation Framework) to add data to an attachment.



SAAJ Client With a JAXM Server Example


// Code Samples from 'Using Java API for XML Messaging' by Joe Attardi, Sun MicroSystems

Many thanks to Sun's Joe Attardi for supplying for fine example that shows
us how the client and server interact to provide a meaningful response using
SAAJ in conjunction with a JAXM server application. See the original article at



WeatherClient
 // from 'Using Java API for XML Messaging' by Joe Attardi, Sun MicroSystems

import javax.xml.messaging.*;
import javax.xml.soap.*;
import java.util.Iterator;

public class WeatherClient
{
    public static void main(String[] args)
    {
        try
        {
    // Create a new, empty message.
    MessageFactory msgFactory = MessageFactory.newInstance( );
    SOAPMessage message = msgFactory.createMessage( );

    // Obtain references to the various parts of the message.
    SOAPPart msgSOAP = message.getSOAPPart();
    SOAPEnvelope msgEnv = msgSOAP.getEnvelope();
    SOAPBody msgBody = msgEnv.getBody();

    // Create the <w:GetForecast> body elements.
    Name nameForecast = msgEnv.createName("GetForecast", "w",
        "http://weather.news.com/weatherservice");
    SOAPBodyElement beForecastBOS =
        msgBody.addBodyElement(nameForecast);
    SOAPBodyElement beForecastNYC =
        msgBody.addBodyElement(nameForecast);

    // Create the <City> child elements under the
    // <w:GetForecast> elements.        
    Name nameCity = msgEnv.createName("City");
    SOAPElement elCityBOS =
        beForecastBOS.addChildElement(nameCity);
    SOAPElement elCityNYC =
        beForecastNYC.addChildElement(nameCity);

    // Add the city names to each <City> child element.
    elCityBOS.addTextNode("Boston");
    elCityNYC.addTextNode("New York");   

    // Remove the SOAP header.
    msgEnv.getHeader( ).detachNode( );

    // Get a SOAP connection from the connection factory.
    SOAPConnectionFactory connFactory =
        SOAPConnectionFactory.newInstance( );
    SOAPConnection conn = connFactory.createConnection( );

    // Define the message destination.
    URLEndpoint destination = new
        URLEndpoint("http://weather.news.com/weatherservice");

    // Send the message and wait for the response.
    SOAPMessage response = conn.call( message, destination );

    // Close the connection.
    conn.close( );

    SOAPPart respSOAP = response.getSOAPPart();
    SOAPEnvelope respEnv = respSOAP.getEnvelope();
    SOAPBody respBody = respEnv.getBody( );
    // fault handling  -ed
    if( respBody.hasFault( ) )
    {
        SOAPFault err = respBody.getFault( );
        System.out.print("Error: " + err.getFaultCode( ) +
            " (");
        System.out.println(err.getFaultString( ) + ")");
    }

    Name nameForecastResponse = respEnv.createName("Forecast",
        "w", "http://weather.news.com/weatherservice");

    Iterator itResponses =
        respBody.getChildElements(nameForecastResponse);

    while (itResponses.hasNext( ))
    {
System.out.println("==============[ Weather Forecast ]==========");

        SOAPBodyElement elForecast =
            (SOAPBodyElement)itResponses.next();
        Iterator itChildren = elForecast.getChildElements( );

        while (itChildren.hasNext( ))
        {
            SOAPElement elData =
                (SOAPElement)itChildren.next( );
            String strItem =
                elData.getElementName().getLocalName( );
            String strValue = elData.getValue( );
   
            System.out.println(strItem + ": " + strValue);   
        }

System.out.println("===========================================\n");
    }
        }
        catch (Exception e)
        {
            System.out.println("Error: " + e.getMessage());
        }
    }
}


WeatherService
 // from 'Using Java API for XML Messaging' by Joe Attardi, Sun MicroSystems


import javax.servlet.*;
import javax.servlet.http.*;
import javax.xml.messaging.*;
import javax.xml.soap.*;
import java.util.Iterator;

public class WeatherService extends JAXMServlet
    implements ReqRespListener
{
    public SOAPMessage onMessage( SOAPMessage message )
    {
        try
        {
    // Get a message factory and create the response message.
    MessageFactory msgFactory = MessageFactory.newInstance( );
    SOAPMessage response = msgFactory.createMessage( );
   
    // Obtain references to the various parts of the response
    // message.
    SOAPPart respSOAP = response.getSOAPPart();
    SOAPEnvelope respEnv = respSOAP.getEnvelope();
    SOAPBody respBody = respEnv.getBody();

    // Obtain references to the various parts of the request
    // message.
    SOAPPart msgSOAP = message.getSOAPPart();
    SOAPEnvelope msgEnv = msgSOAP.getEnvelope();
    SOAPBody msgBody = msgEnv.getBody();

    // Create all the different Names that are needed.
    Name nameForecast = msgEnv.createName("Forecast", "w",
        "http://weather.news.com/weatherservice");
    Name nameCity = msgEnv.createName("City");
    Name nameConditions = msgEnv.createName("Conditions");
    Name nameLowTemp = msgEnv.createName("LowTemp");
    Name nameHighTemp = msgEnv.createName("HighTemp");
    Name nameRequest = msgEnv.createName("GetForecast", "w",
        "http://weather.news.com/weatherservice");

    // Access the <w:GetForecast> elements in the request.
    Iterator itRequests =
        msgBody.getChildElements(nameRequest);
   
    // Iterate over each <w:GetForecast> element,
    // getting the data and adding a new <w:Forecast>
    // element to the response message for each one.
    while(itRequests.hasNext())
    {
        SOAPBodyElement elRequest =
        (SOAPBodyElement)itRequests.next();
        Iterator itCities =
            elRequest.getChildElements(nameCity);
        SOAPElement elCity = (SOAPElement)itCities.next();
        String strCityName = elCity.getValue();

        if (!cityExists(strCityName)) // if city doesn't exist set up fault -ed
        {
            if (!respBody.hasFault())
            {
                SOAPFault respErr = respBody.addFault();
                respErr.setFaultCode("Client.NoSuchCity");
                respErr.setFaultString
       ("You specified one or more invalid cities in your request.");
            }
        }
        else     // else set up reponse -ed
        {
            SOAPBodyElement elResponse =
                respBody.addBodyElement(nameForecast);
            SOAPElement elRespCity =
                elResponse.addChildElement(nameCity);
            SOAPElement elRespConds =
                elResponse.addChildElement(nameConditions);
            SOAPElement elRespLowTemp =
                elResponse.addChildElement(nameLowTemp);
            SOAPElement elRespHighTemp =
                elResponse.addChildElement(nameHighTemp);

            elRespCity.addTextNode(strCityName);
            elRespConds.addTextNode(
                getConditions(strCityName));
            elRespLowTemp.addTextNode(
                getLowTemp(strCityName));
            elRespHighTemp.addTextNode(
                getHighTemp(strCityName));
                }
        }

    // Remove the SOAP header.
    respEnv.getHeader( ).detachNode( );

    // Return and send the response message.
    return response;
        }     
        catch (SOAPException se)
        {
    System.err.println("SOAPException: " + se.getMessage());
    return null;
        }    

       
    }
       // Hard coded replies have been supplied for Mr. Attona's methods - ed

    // Access a data store or Web service to determine whether
    // or not a weather forecast exists for a particular city.
    private boolean cityExists(String strCityName) { return true;  }

    // Access a data store or Web service to get the conditions.
    private String getConditions(String strCityName) { return "Sunny"; }

    // Access a data store or Web service to get the low
    // temperature.
    private String getLowTemp(String strCityName) { return "12 degrees Celsius"; }

    // Access a data store or Web service to get the high
    // temperature.
    private String getHighTemp(String strCityName) { return "22 degrees Celsius"; }
}



Deployment


The JAXM package

We go on to look at JAXM next week, but in the meantime we need to
run this JAXM server we borrowed. The package for JAXM is not part
of the current JWSDP distribution although it soon will be part of
subsequent distributions. The JAXM API is available as a separate
download at the following site.

JAXM Download
http://java.sun.com/xml/downloads/jaxm.html


Deployment of a JAXM Server will be similar but a lot simpler than JAX-RPC
deployment. This is because JAXM servers are just servlets and have the
same deployment requirements. We still need War directory structure but,
in Tomcat we do not need to pack the directory structure in a jar. (This is
not surprising as the first thing Tomcat does is unpack the war file that an
application is archived in.)  We do need  a web.xml file to tell Tomcat about
our servlet, where it is and what path will be used to find it.

We could use the 'Ant' build tool to do this job, however what needs to be
done is not complicated so it really is not neccessary to use the tool. First
we need to supply a web.xml file to configure Tomcat.


The web.xml file

The web.xml file is a standard specified in J2EE. It is the standard 'deployment
descriptor' used to provide web applications configuration parameters to J2EE
compliant web servers. 
No surprise, deployment descriptors are standard XML
text files. Specifically, the  deployment descriptor, web.xml is described in Sun's
Servlet 2.3 specification.

Interestingly, rather than schema, the web.xml file uses the classic Document
Type declaration to define it's types. You can view it by using the URL described
in the DOCTYPE element. Do not be dismayed when you see nothing! The
contents are being filtered. Click on the browser's View --> Source menu button
and the Doctype document becomes visible.


Doctype Declaration for the web.xml File
<!DOCTYPE web-app PUBLIC 
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

Some of the most basic elements found in the deployment descriptor
that are most relevant to quick deployment of our example are described
below. 


The <webapps> Element

The root element is <webapps>.

Example   <web-app> </web-app>


The <servlet>  Element

A servlet needs to be given a name that will be used to reference the
the servlet by a user. It's class also needs to be specified. Other
properties may also be set.

The following example gives the servlet identified in the <servlet-class>
element the name 'PassWordAgent' which will be used as part of the
URL that accesses the servlet's functionality. The <servlet-class>
element gives the qualified name for the servlet. (Under the classes
directory of the Web Application Archive, there will be an 'access'
folder in which is located the PassWordAgent.class file.)

A Servlet Element Example 

<servlet>
 <servlet-name>PassWordAgent</servlet-name>
 <servlet-class>access.PassWordAgent</servlet-class>
 </servlet>


The <servlet-mapping>  Element

The servlet also requires a mapping to a URL. The <servlet-mapping>
is used to map an element to one or more URL patterns. The following
URL Pattern will map the servlet under the name of the webapp itself
which is generally the name of the folder that is under the 'webapps'
directory. ( Assume you web archive file is expanded to 'X' under
webapps, your path to this servlet would be as follows on a J2EE
compliant web server running locally.

Example     "http://localhost:8080/X/PassWordAgent"


A servlet-mapping Example  

<servlet-mapping>
<servlet-name>PassWordAgent</servlet-name>
<url-pattern>/PassWordAgent</url-pattern>
</servlet-mapping>
There are many more elements and details to be investigated in a
web.xml file. For a more detailed inspection of the web.xml file view
"Writing Web  Application Deployment Descriptors" at the BEA site.
Apache's Tomcat documentation is also excellent for web.xml files.

http://e-docs.bea.com/wls/docs61/webapp/webappdeployment.html#1006167


The Web App Directory Structure

Recall Web apps have a standardized directory structure that by and large
provide the portability, allowing a web application to be moved around and
deployed in different web server environments. Following is a reminder of
the directory structure that needs to be supplied. Our web.xml file is the
only configuration file that needs to be supplied and is put in the WEB-INF
directory.

Our servlets are put under the classes directory. The web.xml file tells
Tomcat where to find our servlet classes.


Standard J2EE Web Application Directory Structure

AWebAppService   // store HTML and JSPs under the main directory
       |___WEB-INF   // under WEB-INF include Deployment Descriptor web.xml
                   |____classes
                   |_____lib




Exercise


In preparation for your test, I would like you to take as your
exercise this week reading the XML, XML Schema and  JAXP
notes as your main duties this week.

Optional!

1) If you have these under control, you can do the following. Using
the article "Using Java API for XML Messaging" by Joe Attardi
from Sun Microsystems, as a guide deploy and test the Weather
Client and Server in Tomcat.

http://developers.sun.com/sw/building/tech_articles/api_xmlmessaging.html

Following is the web.xml file that is used in this example.

    <?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD
Web Application 2.3//EN"
"http://java.sun.com/j2ee/dtds/web-app_2_3.dtd">
    <web-app>
<servlet>
<servlet-name>WeatherService</servlet-name>
<servlet-class>WeatherService</servlet-class>
</servlet>
    <servlet-mapping>
<servlet-name>WeatherService</servlet-name>
<url-pattern>/weatherservice</url-pattern>
</servlet-mapping>
</web-app>


2) a) Create a simple message using SAAJ similar to the Request
example in the note.

b) Reduce Joe Attardi's Servlet and supply a simple servlet that
provides an acknowledgement for any message that is received.
There is an example of such a servlet in the text, 'Developing
Java Web Services', R Nagappan et. al. (page 424 Listing 9.2.
also see a similar one on page 435, Listing 9.6)