In this article, we will look at JAXB and SAAJ for providing data binding and reading and writing of SOAP-based messages of Service Oriented Architecture Web services. We will look at how JAXB 2.0 is used for type mappings from a Java type to an XML Schema component. This ties in nicely with the most recent articles on REST Web Services. This is part of a larger series of articles to help you prepare for the web service developer exam. You can review the articles on REST Web Services, “Tutorial:Review of REST Web Services for the Web Service Developer Exam I” and “Tutorial:Review of REST Web Services for the Web Service Developer Exam II” if needed.

Data Binding with JAXB

JAXB 2.0 is critical component because it supports type mappings. Type mapping is a map between a Java type or a class and an XML Schema component or a simple or complex type definition. Type mappings are implemented using serializers. Serializers convert an instance of a type mapping’s Java type into an instance of its XML Schema component. Deserializers convert an instance of a XML Schema component into of a type mapping’s Java type. We will look at how to use JAXB 2.0 to do the kinds of serialization needed for SOA as well as examining how JAXB 2.0 can be used for data transformations rather than using XSLT.

Data Binding versus Data Mapping

In order to clarify the difference between binding and mapping, we need to consider a couple of tools for doing either binding or mapping. First, a binding tool converts Java types to XML types according to a standard mapping. A binding tool implements a standard way of converting Java to XML or vice versa. A binding tool like JAXB maps Java instances to XML documents and map Java application’s class definitions to XML Schema definitions. The figure below shows the difference between binding and mapping:

Tutorial:Review of JAXB and SAAJ for the Web Service Developer Exam-b11-databinding-datamapping.jpg
Figure: Data Binding vs Data Mapping

As the figure shows, this enables you to implement type mappings in the following ways:
  • Start from an existing Java application and use a schema generator to create a machine-generated XML schema as shown in the red box in the figure.
  • Start from an existing XML schema and use a schema compiler to create a machine-generated Java application as shown by the blue box in the figure.

The top part of the figure illustrates the binding process. A Binding tool uses the type mappings to map between existing Java and generated XML schema definitions, or between existing XML schema definitions and the generated Java program elements. Irrespective at runtime, you use the machine-generated artifacts be they Java or XML schemas. These type mappings can be used always include either machine-generated Java or machine-generated XML schema. A mapping tool uses the type mappings between the existing Java and existing XML schema definitions. At runtime, you don’t use any machine-generated artifacts.

The lower part of the figure illustrates the Java/XML mapping process. In this situation, you are working with a Java/XML mapping tool and need to define the mappings between the XML and the Java. At runtime, the mapping tool uses your mapping definitions to serialize Java to XML or deserialize XML to Java.

You should consider JAXB 2.0 as primarily a binding tool. On startup, a JAXB user writes Java and annotates code in order to map to a particular schema. Otherwise, you can start with the XML schema and generate a Java code template. The template can then be customized and incorporated into your application. As you can see there is alot of flexibility with JAXB as to whether you start with an XML schema or by first working in Java, customizing the annotations as well as in terms of the schema to which you can map the Java code. Although, we can use the annotations to provide a mapping from the Java code to the schema, we classify JAXB as a binding tool,for the following reasons:
  • The ability to customize the use of annotations from the standard bindings.
  • There are lots of mappings that are not supported without custom coding with the XmlAdapter class.


The mapping defined by the annotations are static since you cannot implement a different mapping without changing the annotations and recompiling the Java. If this was a real mapping tool, the mappings could be defined separately from Java and the schema. This would allow you to change the mappings at runtime or even support multiple mappings at runtime. This is not possible with JAXB because a Java class can have only one set of annotations. Therefore with JAXB 2.0, you are limited in the type of mappings consistent with JAXB 2.0 Java/XML mappings or the type of customizations that can be expressed using the JAXB 2.0 annotations or JAXB 2.0 binding language.

An Overview of the Standard JAXB 2.0 Java/XML Binding

Now that we know the scope for overview of the standard JAXB 2.0 Java/XML binding, below is a schema for a simplified purchase order that we will use to show standard binding:

XML Code: Schema for SimpleOrder
<schema targetNamespace="http://www.acme.com/oms"
  elementFormDefault="qualified" xmlns="http://www.w3.org/2001/XMLSchema"
  xmlns:oms="http://www.acme.com/oms">
  <element name="simpleOrder">
    <complexType>
      <sequence>
        <element name="billTo">
          <complexType>
            <sequence>
              <element name="name" type="string"/>
              <element name="street" type="string"/>
              <element name="city" type="string"/>
              <element name="state" type="string"/>
              <element name="zip" type="string"/>
              <element name="phone" type="string"/>
            </sequence>
          </complexType>
        </element>
        <element name="items">
          <complexType>
            <sequence>
              <element name="item" type="oms:ItemType" maxOccurs="unbounded"/>
            </sequence>
          </complexType>
        </element>
      </sequence>
    </complexType>
  </element>
  <complexType name="ItemType">
    <sequence>
      <element name="quantity" type="positiveInteger"/>
      <element name="price" type="double"/>
    </sequence>
    <attribute name="productName" use="required" type="string"/>
  </complexType>
</schema>

In the listing, there is a schema with a single global element named simpleOrder which defines an anonymous purchase order type that has a sequence of two elements:
billTo representing the billing address with an defines an anonymous type called an address type.
items representing the list of items being purchased. The elements of the list are defined within the items element’s anonymous type as element called item of named type ItemType.

In the lower part of the listing is the definition for ItemType. It hold two elements:
  • quantity - an xs:positiveInteger
  • price - an xs:double.

And it contains an attribute called productName of type xs:string. The figure below shows the standard JAXB 2.0 binding for this schema:

Tutorial:Review of JAXB and SAAJ for the Web Service Developer Exam-b11-standardjaxbbinding.jpg
Figure: Standard JAXB 2.0 binding for SimpleOrder Schema

The figure shows the standard binding defines two top-level classes from this SimpleOrder schema (i.e. SimpleOrder and ItemType) in red. The class SimpleOrder also has two inner classes shown in blue; SimpleOrder.BillTo and SimpleOrder.Items.

We also illustrate key parts of the standard JAXB 2.0 binding by providing an overview of the standing. The binding concept is demonstrated by each arrow:
  1. The namespace http://www.acme.com/oms is mapped to the Java package com.acme.oms.You can also use the jaxb:package declaration or the @XmlSchema.namespace annotation element if you want a different package mapping.
  2. The anonymous complex type defined by the element simpleOrder is mapped to the top-level class SimpleOrder. Anonymous complex types are mapped to top-level classes only when they are defined by global element definitions for default mapping.
  3. The element billTo is mapped to the property named billTo in the SimpleOrder class. Local element declarations are bound to properties by default.
  4. The access method for the billTo property defaults to JavaBeans style setter/getter methods. There is no standard JAXB binding language declaration for binding an element to a public or protected Java field without the getter/setters. But you could generate schema where an element definition is bound to a Java field using the @XmlElement annotation.
  5. The anonymous complex type defined by the element billTo gets mapped to the inner class SimpleOrder.BillTo. The standard binding for anonymous complex types not defined by global elements is to make them inner classes. You could also force all classes to be generated as top-level by using the <jaxb:globalBindings localScoping="toplevel"/> global declaration.
  6. The element item gets mapped to a List<ItemType> property named item. This is the standard binding when the element definition has a maxOccurs value greater than 1. The Java type parameter for List<T> is equal to the Java type that is bound to the element definition’s type attribute or in this case, T = ItemType. The collection type defaults to java.util.List. It can be customized using the binding language collectionType attribute at either the global or the property level.
  7. The ItemType which is a named complex type is mapped to the top-level Java class ItemType. The standard mapping for named complexType definitions is to a top-level Java value class.
  8. The default mapping for xs:positiveInteger and xs:integer is to java.math.BigInteger. You can customize this mapping by using the binding language jaxb:javaType declaration or the @XmlElement.type annotation.
  9. The productName attribute is mapped to the element’s children of ItemType. The default mapping for attribute declarations such as element declarations is to properties with JavaBeans style setter/getter methods.

Below is a fragment of the JAXB-generated code for the SimpleOrder class where the annotations introduced into the class in order to facilitate the run-time marshalling are shown:

Java Code: SimpleOrder Class
@XmlAccessorType(AccessType.FIELD)
@XmlType(name = "", propOrder = {
    "billTo",
    "items"
})
@XmlRootElement(name = "simpleOrder")
public class SimpleOrder {

    @XmlElement(namespace = "http://www.acme.com/oms")
    protected BillTo billTo;
    @XmlElement(namespace = "http://www.acme.com/oms")
    protected Items items;
The listing shows that the annotations are key for run-time marshalling and unmarshalling. JAXB 2.0 is able to achieve portability across implementations due to annotations. This is because you are able to go back and forth between using the schema compiler for one implementation but use the generated classes for another implementation at run-time while JAXB 2.0 engine handles all the marshalling and unmarshalling based on introspection of the annotated classes. The annotations are used in conjunction with JAXB 2.0 standard mapping to tell the run-time engine how to map Java program elements to XML schema definition instances. The annotations and their run-time interpretations for SimpleOrder are shown in the table below:

Tutorial:Review of JAXB and SAAJ for the Web Service Developer Exam-b11-jaxbannotations.jpg
Table: JAXB 2.0 Java Annotations

To find more information, please see the specification, JSR-222. Next we show the listing for the code generated for SimpleOrder.BillTo. As it’s name implies, this is an inner class. The element definitions containing anonymous complex types are by default mapped to inner classes. The listing is shown below:

Java Code: SimpleOrder.BillTo Class
@XmlAccessorType(AccessType.FIELD)
@XmlType(name = "", propOrder = {
    "name"
    "street",
    "city",
    "state",
    "zip",
    "phone"
})
public static class BillTo {

  @XmlElement(namespace = "http://www.acme.com/oms", required = true)
  protected String name;
  @XmlElement(namespace = "http://www.acme.com/oms")
  protected String street;
  @XmlElement(namespace = "http://www.acme.com/oms")
  protected String city;
  @XmlElement(namespace = "http://www.acme.com/oms")
  protected String state;
  @XmlElement(namespace = "http://www.acme.com/oms")
  protected String zip;
  @XmlElement(namespace = "http://www.acme.com/oms")
  protected String phone;

This listing shows the @XmlElement.propOrder annotation element specifies the order in which the Java class properties map to the <sequence> definition of elements in the anonymous complex type. This is similar to the previous listing. It is required because a Java class through reflection provides no ordering information so it has no concept of ordering on its fields and properties.

The next listing is for the SimpleOrder.Items class. It is shown below:

Java Code: SimpleOrder.Items Class
@XmlAccessorType(AccessType.FIELD)
@XmlType(name = "", propOrder = {
    "item"
})
public static class Items {
  
  @XmlElement(namespace = "http://www.acme.com/oms")
  protected List<ItemType> item;
  
  /**
   * Gets the value of the item property.
   * 
   * <p>
   * This accessor method returns a reference to the live list,
   * not a snapshot. Therefore any modification you make to the
   * returned list will be present inside the JAXB object.  This is 
   * why there is not a <CODE>set</CODE> method for the item property.
   * 
   * <p>
   * For acme, to add a new item, do as follows:
   * <pre>
   *getItem().add(newItem);
   * </pre>
   * 
   * 
   * <p>
   * Objects of the following type(s) are allowed in the list
   * {@link ItemType }
   * 
   * 
   */
  public List<ItemType> getItem() {
    if (item == null) {
      item = new ArrayList<ItemType>();
    }
    return this.item;
  }
      
}
The listing shows the default mapping between the class and the anonymous complex type defined by the oms:item element. What is also interesting is that it illustrates the default mapping for elements that may occur multiple times (i.e., maxOccurs > 1). The element oms:item, defined inside the anonymous complex type for the element oms:items, is mapped to the item property with type List<ItemType>. The java.util.List<T> is the default mapping for elements that occur multiple times. T in this case is the default class mapped to the elements’ type.

Note that in this listing there is no setter for the item property. Instead the getter returns a live list, instead of a copy. Therefore to update the item property requires the live list to add, update, or delete individual elements. You can set the entire collection as well with JAXB 2.0 by using the binding language to specify an alternative collection type. One means of accomplishing this is by using a <property> declaration like the following:

XML Code: Using property Declaration To Set Entire Collection
<sequence> 
   <annotation><appinfo>
      <jaxb:property collectionType="indexed"/> 
   </appinfo></annotation> 
   <element name="item" type="oms:ItemType" maxOccurs="unbounded"/>
</sequence>
The listing shows that setting collectionType="indexed" will map the item element to an array property (i.e., ItemType[]), instead of a list. You then will have the following type of setter method available:

Java Code: Setter Method for A Collection
public void setItem(ItemType[] values) { ... }
Also remember that if you use collectionType="indexed" that the array returned from the getter is a copy. For more information about mapping to collections, review the JAXB (i.e. JSR 222) documentation.

Next we illustrate the default mapping for a named complex type. In our case this is the type, ItemType. Named complex types are not mapped to inner classes like anonymous complex types.Therefore the type oms:ItemType is mapped to com.acme.oms.ItemType. The listing for ItemType JAXB generated class is shown below:

Java Code: ItemType Class
@XmlAccessorType(AccessType.FIELD)
@XmlType(name = "ItemType", propOrder = {
    "quantity",
    "price"
})
public class ItemType {

    @XmlElement(namespace = "http://www.acme.com/oms")
    protected BigInteger quantity;
    @XmlElement(namespace = "http://www.acme.com/oms", type = Double.class)
    protected double price;
    @XmlAttribute(required = true)
    protected String productName;
In this listing the @XmlAttribute annotation is used to map its associated property, productName to an attribute rather than an element. The "required = true" annotation element indicates that the XML definition must have the "required" attribute to indicate that the attribute is required for instances of the XML schema. Also note that the quantity property with type of xs:positiveInteger is mapped to a property with class java.math.BigInteger. This allows you to set the JAXB value of this property to a negative number, and then marshal it out to XML without an error being generated by the marshalling process. This is because by default, it doesn’t validate its output against the source schema. Instead, JAXB 2.0 has delegated such validation to the JAXP 1.3 API. If you want to activate such validation, you can use the setSchema(javax.xml.validation.Schema schema) method on either a Marshaller or Unmarshaller instance. You can also find more information on validation by going through the JAXB (i.e. JSR 222) specification.

Implementing Type Mappings with JAXB 2.0

It is also possible to combine custom Java code with the JAXB standard mapping to implement any type mapping you desire. The value of this approach is that it does not require any understanding of the JAXB annotations. You can work with the Java that gets generated and ignore the annotations. For the default type mappings provided by JAXB 2.0, we used the following to represent type mapping:

XML Code: Default Type Mapping by JAXB 2.0
< {http://www.acme.com/oms}simpleOrder, com.acme.oms.SimpleOrder >
The notation is of form < XML-Type, Java-Type > where the XMLType is the qualified name of an XML Schema-defined element or type and Java-Type is the name of the Java class. To implement custom type mappings, we will implement the following:

XML Code: Custom Type Mapping
< oms:simpleOrder, com.acme.domain.MySimpleOrder >
Here, we have com.acme.domain.MySimpleOrder which is not a JAXB-generated class but a previously existing class that I want to map to oms:simpleOrder. MySimpleOrder is just a representation of its anonymous complex type. Therefore with this custom type mapping, we will lose the element information (i.e., tag name) that is captured in JAXB by the JAXBElement representation. We will implement the mapping by letting JAXB create an instance of com.acme.oms.SimpleOrder and then mapping SimpleOrder to MySimpleOrder. The listing below shows the Java class MySimpleOrder:

Java Code: MySimpleOrder Java Class
public class MySimpleOrder {

  private MyAddress billTo;
  private List<MyItem> itemList;
  
  public MySimpleOrder(String name, String street, String city, String state,
      String zip, String phone) {
    this(new MyAddress(name, street, city, state, zip, phone));
  }
  
  public MySimpleOrder(MyAddress addr) {
    this.billTo = addr;
    itemList = new ArrayList<MyItem>();
  }

  public MyAddress getBillTo() {
    return billTo;
  }

  public List<MyItem> getItemList() {
    return itemList;
  }
  
}
First note the differences between this class and its JAXB-generated counterpart are the following:
  • The billTo variable, which represents the billing address, has the Java type MyAddress instead of the JAXB-generated type com.acme.oms.BillTo.
  • An instance of List<MyItem> represents the items being purchased instead of the JAXB-generated type, SimpleOrder.Items.

So the mapping between com.acme.domain.MySimpleOrder and an instance of oms:simpleOrder requires that you map the billTo property with Java type com.acme.domain.MyAddress to an oms:billTo element, and its itemList property with Java type List<samples.MyItem> to an oms:items element. Next you combine the elements, oms:billTo and oms:items, to create an instance of oms:simpleOrder. The lisiting below shows the the MyAddress.class in order to clarify the mapping code that implements this procedure:

Java Code: MyAddress Java Class
public class MyAddress {
  
  protected String name;
  protected String street;
  protected String city;
  protected String state;
  protected String zip;
  protected String phone;
  
  public MyAddress(String name, String street, String city, String state,
      String zip, String phone) {

    this.name = name;
    this.street = street;
    this.city = city;
    this.state = state;
    this.zip = zip;
    this.phone = phone;
    
  }
  
}
Note that the MyAddress class is not a JavaBean since there are no getter or setter methods. It is rather a container for its fields. So the mapping from the SimpleOrder.BillTo class to MyAddress is very simple since they just match up the corresponding properties to the MyAddress fields.

Next we show the listing for the com.acme.domain.MyItem class:

Java Code: MyItem Class
public class MyItem {
  
  private int quantity;
  private float price;
  private String productName;
  
  public MyItem(int quantity, float price, String productName) 
  throws Exception {
    if (productName == null) {
      throw new Exception("productName cannot be null");
    }
      this.productName = productName;
      this.price = price;
      this.quantity = quantity;
  }

  public float getPrice() {
    return price;
  }

  public void setPrice(float price) {
    this.price = price;
  }

  public String getProductName() {
    return productName;
  }

  public void setProductName(String productName) {
    this.productName = productName;
  }

  public int getQuantity() {
    return quantity;
  }

  public void setQuantity(int quantity) {
    this.quantity = quantity;
  }

}
In the listing, the major differences between this class and the JAXB schema-generated class com.acme.oms.ItemType are about the property types. MyItem.quantity is a primitive type int, whereas ItemType.quantity is a java.math.BigInteger. The property price is a primitive type float, but type double in the JAXB class. Finally the property productName is a java.lang.String in both classes.

The mapping code then takes an instance of com.acme.domain.MySimpleOrder and returns it as an XML document that is an instance of oms:simpleOrder. The XML document is returned as a javax.xml.transform.Source. This is shown in the listing below:

Java Code: Mapping Code for MySimpleOrder
public Source getXML(MySimpleOrder order) {

    // create the JAXB SimpleOrder
    SimpleOrder jaxbSimpleOrder = new SimpleOrder();
    // map the addresses
    MyAddress myAddress = order.getBillTo();
    SimpleOrder.BillTo billTo = new SimpleOrder.BillTo();
    billTo.setName(myAddress.name);
    billTo.setCity(myAddress.city);
    billTo.setPhone(myAddress.phone);
    billTo.setState(myAddress.state);
    billTo.setStreet(myAddress.street);
    billTo.setZip(myAddress.zip);
    jaxbSimpleOrder.setBillTo(billTo);
    // map the items
    jaxbSimpleOrder.setItems(new SimpleOrder.Items()); // needed to avoid NPE
    List<ItemType> jaxbItemList = jaxbSimpleOrder.getItems().getItem();
    for (MyItem myItem : order.getItemList()) {
      ItemType jaxbItem = new ItemType();
//    jaxbItem.setPrice((double) myItem.getPrice());
      jaxbItem.setPrice(Double.parseDouble(Float.toString(myItem.getPrice())));
      jaxbItem.setProductName(myItem.getProductName());
      jaxbItem.setQuantity(BigInteger.valueOf((long) myItem.getQuantity()));
      jaxbItemList.add(jaxbItem);
    }
    try {
      JAXBContext jaxbContext = JAXBContext.newInstance("com.acme.oms");
      Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
      SchemaFactory sf = 
        SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
      Schema schema = sf.newSchema(
          new URL("http://acmesoa.com/oms/simpleorder.xsd"));
      jaxbMarshaller.setSchema(schema);
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      jaxbMarshaller.marshal(jaxbSimpleOrder, baos);
      return new StreamSource(new StringReader(baos.toString()));
    } catch (Exception e) {
      throw new RuntimeException(e);
    } 
    
  }
The listing shows the mapping code creating an instance of the JAXB-generated class com.acme.oms.SimpleOrder and populating it from the MySimpleOrder class, and then marshalling the JAXB instance to get the XML document. It does this by first creating an instance of the JAXB-generated class SimpleOrder.BillTo (i.e. jaxbSimpleOrder) from the instance of the MyAddress obtained from order.getBillTo(). The setter methods of the class are used to set the properties. Next, the oms:items property is set to an empty list using:

Java Code: Setting of oms:items Property
jaxbSimpleOrder.setItems(new SimpleOrder.Items());
You need to do this step because oms:items does not get mapped to a List<T> by JAXB 2.0 and is therefore not initialized. If you then called, jaxbSimpleOrder.getItems() before initializing oms:items it would return a null. Once initialized, a for loop is used to populate the members of the JAXB-generated List<ItemType> from the members of order.getItemList() which are of type List<MyItem> using setters for ItemType.

An item of note is how property types are converted from float to double. This is shown in the line:
jaxbItem.setPrice(Double.parseDouble( Float.toString(myItem.getPrice())));

which converts the price property from float (in MyItem) to double (in ItemType). The technique of converting through String instances is used to avoid the underflow/overflow issues that can occur when changes in precision occur. For a straightforward conversion, you could use:

Java Code: Straight Conversion
jaxbItem.setPrice((double) myItem.getPrice());
The XML would then look like the following:

XML Code: Straight Conversion Result
<price>2.990000009536743</price>
rather than:

XML Code: Conversion through String Instance Result
<price>2.99</price>
Once the SimpleType instance of jaxbSimpleOrder is completely populated, it is marshaled out to XML. You use JAXBContext for this process by creating it in the following manner:

Java Code: JAXBContext Creation
JAXBContext jaxbContext = JAXBContext.newInstance("com.acme.oms");
The argument for creating a new instance of JAXBContext (i.e. com.acme.oms) in the factory method is the package name of the JAXB-generated classes. The JAXBContext then creates a Marshaller instance, then the validation is activated by setting the schema on the Marshaller instance in the following manner:

Java Code: Setting Validation for Marshaller
Schema schema = sf.newSchema( new URL("http://acmesoa.com/oms/simpleorder.xsd"));
jaxbMarshaller.setSchema(schema);
Finally, the jaxbSimpleOrder is marshaled into memory using a ByteArrayOutputStream and then returned as a StreamSource. The listing below was used to test the mapping:

Java Code: Test for Mapping Code
MySimpleOrderSerializerNonRecursive serializer = 
      new MySimpleOrderSerializerNonRecursive();
    MySimpleOrder myOrder = new MySimpleOrder(
        "James Doherty",
        "531 Main Street",
        "New York", "NY", "10044",
        "(212) 456-5092");
    myOrder.getItemList().add(new MyItem(6, (float) 2.99, "Banana Strawberry Smoothe"));
    myOrder.getItemList().add(new MyItem(4, (float) 3.99, "Rocket, Tomato and Avocado Salad"));
    myOrder.getItemList().add(new MyItem(2, (float) 5.34, "Humous with Pita Bread"));
    Transformer xform = TransformerFactory.newInstance().newTransformer();
    xform.setOutputProperty(OutputKeys.INDENT, "yes");
    xform.transform(
        serializer.getXML(myOrder),
        new StreamResult(System.out));
In this listing we create an instance of MySimpleOrder with three items and then a javax.xml.transform.Transformer is used to write the XML source returned from the getXML() mapping method to System.out. Here we wrote the code that does a mapping of MyItem to oms:item. It would be preferred to write each type mapping only once, and then reuse it whenever it is used as part of another type mapping.

Reading and Writing SOAP Messages with SAAJ

The SOAP with Attachments API for Java (SAAJ) is used mainly for the SOAP messaging that goes on behind the scenes with JAX-WS handlers and JAXR implementations as well as being an API that developers can use to write SOAP messaging applications directly instead of using JAX-WS. With SAAJ API you can do XML messaging from the Java platform making method calls using the SAAJ API that allow you to read and write SOAP-based XML messages, and/or send and receive such messages over the Internet.

The SAAJ 1.3 API is consistent with SOAP 1.1 and 1.2 specifications as well as the SOAP with Attachments specification. It defines the javax.xml.soap package, containing the API for creating and populating a SOAP message. The package contains all the necessary API calls for sending request-response messages.

High Level View of SAAJ

There are two key aspects of SAAJ that you need to know. These are messages and connections. Let’s look at each perspective.

SAAJ Messages

Consistent with SOAP standards, SAAJ messages prescribe the format for messages as well as things that are required, optional, or not allowed. You use SAAJ API to create XML messages that conform to the SOAP 1.1 or 1.2 specification and the WS-I Basic Profile 1.1 specification by making Java API calls. You may want to review our previous article, “Tutorial:Review of SOAP Web Services for the Web Service Developer Exam” since we build on a lot of the things about SOAP web services that we discussed in that article.

The Structure of an XML Document

All XML documents have a hierarchical structure consisting of elements, subelements, subsubelements, etc. Many of the SAAJ classes and interfaces represent XML elements in a SOAP message and contain the word element and/or SOAP in their names. An element is a node in the world of XML. Therefore the base class for all classes and interfaces that represent XML elements in a SOAP message have a Node interface in the SAAJ API.

Types of SOAP Messages

There are two key types of SOAP messages. Messages with attachments and messages with no attachments.

Messages with No Attachments

These messages must have the following as required parts of every SOAP message except for the SOAP header:
  • SOAP message
  • SOAP part
  • SOAP envelope
  • SOAP header (optional)
  • SOAP body

The SOAPMessage class represents the SOAP message in the SAAJ API. The SOAPPart class represents the SOAP part, the SOAPEnvelope interface represents the SOAP envelope, and the SOAPHeader interface represents the SOAP header and finally the SOAPBody interface represents the SOAP body. This is shown in the figure below:

Tutorial:Review of JAXB and SAAJ for the Web Service Developer Exam-b11-soapwoattachments.jpg
Figure: SOAP Message with No Attachments

A new SOAPMessage object automatically will have the parts that are required to be in a SOAP message when it is created.The chain is a new SOAPMessage object has a SOAPPart object containing a SOAPEnvelope object. The SOAPEnvelope object automatically contains an empty SOAPHeader object followed by an empty SOAPBody object.As the SOAPHeader object which can contain one or more headers with metadata about the message is optional, you can delete it if it is not required. The SOAPBody object, which follows the optional SOAPHeader object contains the message content or a SOAPFault object.

Messages with Attachments

When a SOAP message includes attachments in addition to the SOAP part, the SOAP part should only contain XML content. If any of the content of a message is not in XML format, it should be in the attachment part. For example a message with a binary file, must have an attachment part for it. The attachment part can contain any kind of content or even XML content. The figure below shows the structure of a SOAP message with attachments:

Tutorial:Review of JAXB and SAAJ for the Web Service Developer Exam-b11-soapwattachment.jpg
Figure: SOAP Message with Attachments

The attachment part of the SOAP message is represented by the AttachmentPart class in SAAJ API. The AttachmentPart objects are optional and you must create and add them yourself. Each AttachmentPart object must have a MIME header to indicate the type of data it contains. It can also have additional MIME headers to identify it or to give its location. These headers are optional but are quite useful when there are multiple attachments. SOAPMessage objects that have one or more AttachmentPart objects may or may not contain message content in the SOAPPart object.

SAAJ and DOM

The SAAJ APIs extend their counterparts in the org.w3c.dom package:
  • The Node interface extends the org.w3c.dom.Node interface.
  • The SOAPElement interface extends both the Node interface and the org.w3c.dom.Element interface.
  • The SOAPPart class implements the org.w3c.dom.Document interface.
  • The Text interface extends the org.w3c.dom.Text interface.

In addition the SOAPPart of a SOAPMessage is a DOM Level 2 Document which can be manipulated by applications, tools, and libraries that use DOM. See SAAJ documentation on how to use DOM documents with the SAAJ API

SAAJ Connections

A SAAJ API connection is represented by a SOAP Connection. Similar to all SOAP messages, SOAP messages with attachments are sent directly from the sender to its destination via a point to point connection. Messages sent using the SAAJ API are called request-response messages. They are sent over a SOAPConnection object with the call method, which sends a message (a request) and then blocks until it receives the reply (a response). Below is a simple example showing this type of request response message:

Java Code: Request Response Message with SAAJ API
SOAPConnectionFactory factory = SOAPConnectionFactory.newInstance(); SOAPConnection connection = factory.createConnection();
. . .// create a request message and give it content
java.net.URL endpoint = new URL("http://www.acme.com/oms/order"); 
SOAPMessage response = connection.call(request, endpoint);
The listing shows the creation of the SOAPConnection object connection, the creation and population of the message and then the sending of the message. Messages sent over a SOAPConnection object are sent using the call method, which both sends the message and blocks until it receives the response. So the return value for the call method is the SOAPMessage object that is the response to the message that was sent. The request parameter is the message being sent and the endpoint, the second argument to the call method identifies where the message is being sent. This can be a String object or a URL object. Therefore the last two lines of code from the previous listing could also have been the following:

Java Code: Request Response Message Using String
String endpoint = "http://www.acme.com/oms/order"; 
SOAPMessage response = connection.call(request, endpoint);
When a web service is implemented for request-response messaging, it must return a response to any message it receives. In our case, the response is a SOAPMessage object, similar to the request. The request message is an update and the response is an acknowledgment that the update was received. The acknowledgment implies that the update was successful unless there is a SOAPFault in the body of the response.

OK. That is it for JAXB and SAAJ. Next we move on to JAXR.