Web services in Java SE, Part 4: SOAP with Attachments API for Java

Learn about SAAJ and other advanced Java SE Web service features

Parts 1 through 3 of this four-part series on developing Web services in Java SE first presented an overview of Web services and Java SE's support for developing them. The series then focused on developing SOAP-based and RESTful Web services via this support. Part 4 wraps up this series by focusing on advanced topics.

This article first introduces Java SE's SAAJ API for working with SOAP-based Web services at a lower level. It then discusses how to create a JAX-WS handler to log the flow of SOAP messages. Next, the article teaches how to create and install a custom lightweight HTTP server to perform authentication.

Moving on, you're shown how to create a RESTful Web service that returns attachments (e.g., a JPEG image) to its clients. You then dig deeper into JAX-WS by exploring the interplay between providers and dispatch clients, and learn how to create a dispatch client that accesses the javax.xml.transform.Source instance returned from a Web service provider's invoke() method via a different Source instance in another version of Part 3's LibraryClient application.

Working with SAAJ

Soap with Attachments API for Java (SAAJ) is the Java API for creating, sending, and receiving SOAP messages that may or may not have MIME-typed attachments. SAAJ is a lower-level alternative to JAX-WS for sending and receiving SOAP messages.

This section first presents an overview of SOAP message architecture. It then takes you on a tour of SAAJ. When this tour finishes, I present an application that uses this API to access a SOAP-based Web service (written in PHP) for converting between integer values and Roman numerals. This application reinforces your understanding of SAAJ.

SOAP message architecture

A SOAP message is an XML document sent from an initial SOAP sender node to an ultimate SOAP receiver node, mostly likely passing through intermediate SOAP sender/receiver nodes along its path. A SOAP node is processing logic that operates on a SOAP message.

The SOAP document consists of an Envelope root element that encapsulates an optional Header element and a nonoptional Body element -- see Figure 1.

Figure 1. A SOAP message's architecture consists of an optional Header element and a mandatory Body element within an Envelope element

A SOAP message's architecture consists of an optional Header element and a mandatory Body element within an Envelope element.

The Header element specifies application-related information (such as authentication details to verify who sent the message) via immediate child elements known as header blocks. A header block represents a logical grouping of data that can target an intermediate SOAP node or the ultimate receiver node.

Although header blocks are defined by the application, their start tags may contain the following SOAP-defined attributes to indicate how SOAP nodes should process them:

  • encodingStyle identifies the rules used to serialize parts of a SOAP message
  • role identifies the SOAP node (via a URI) to which the header block is targeted -- this SOAP 1.2-introduced attribute replaces the SOAP 1.1 actor attribute, which performs the same function
  • mustUnderstand indicates whether processing of the header block is mandatory (value 1 in SOAP 1.1; true in SOAP 1.2) or optional (value 0 in SOAP 1.1; false in SOAP 1.2)
  • relay indicates whether the header block targeted at a SOAP receiver must be relayed to another node if not processed -- this attribute was introduced in SOAP 1.2

The Body element contains information that targets the ultimate receiver node. This information is known as the payload, and consists of a SOAP-defined Fault child element describing a fault (an error being reported by the Web service), or child elements that are specific to the Web service.

The Fault element contains error and status information that a Web service returns to a client. SOAP 1.1 specifies the following child elements of Fault:

  • faultcode: This mandatory element provides information about the fault in a form that can be processed by software. SOAP defines a small set of SOAP fault codes that cover basic faults; this set can be extended by applications.
  • faultstring: This mandatory element provides information about the fault in a human-readable format.
  • faultactor: This element contains the URI of the SOAP node that generated the fault. A SOAP node that is not the ultimate SOAP receiver must include faultactor when creating a fault; an ultimate SOAP receiver doesn't have to include this element, but might choose to do so.
  • detail: This element carries application-specific error information related to the Body element. It must be present when Body's contents couldn't be processed successfully. The detail element must not be used to carry error information belonging to header blocks; detailed error information belonging to header blocks is carried within these blocks.

SOAP 1.2 specifies the following child elements of Fault:

  • Code: This mandatory element provides information about the fault in a form that can be processed by software. It contains a Value element and an optional Subcode element.
  • Reason: This mandatory element provides information about the fault in a human-readable format. Reason contains one or more Text elements, each of which contains information about the fault in a different language.
  • Node: This element contains the URI of the SOAP node that generated the fault. A SOAP node that's not the ultimate SOAP receiver must include Node when creating a fault; an ultimate SOAP receiver doesn't have to include this element, but might choose to do so.
  • Role: This element contains a URI that identifies the role the node was operating in when the fault occurred.
  • Detail: This optional element contains application-specific error information related to the SOAP fault codes describing the fault. Its presence has no significance as to which parts of the faulty SOAP message were processed.

Listing 1 presents an example SOAP message.

Listing 1. A SOAP message for calling a SOAP-based library Web service's getTitle() function to retrieve a book's title when given its ISBN

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
                   xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <SOAP-ENV:Header />
   <SOAP-ENV:Body>
      <lns:getTitle xmlns:lns="http://javajeff.ca/library">
         <isbn xsi:type="xsd:string">9781484219157</isbn>
      </lns:getTitle>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

This SOAP message describes a request to a library Web service to execute its getTitle() function. Furthermore, it describes the type and value of the ISBN argument passed to this function's isbn parameter.

The message begins with the SOAP-ENV-prefixed <Envelope> tag, which describes the SOAP message's envelope. The commonly used SOAP-ENV prefix corresponds to the SOAP 1.1 namespace that provides the schema for SOAP envelopes. The xsd and xsi prefixes correspond to the XML Schema structures and XML Schema Instance namespaces, and are used to denote the XML Schema type that describes the kind of data being passed to getTitle() (a string) via the isbn element.

The empty Header element signifies that there is no SOAP header. In contrast, the Body element identifies a single getTitle operation request.

The getTitle element is namespace-qualified, as recommended by the SOAP 1.1 and 1.2 specifications. In contrast, the isbn child element of getTitle isn't namespace-qualified because it inherits getTitle's namespace -- the SOAP 1.1 and 1.2 specifications don't mandate that such child elements be namespace-qualified.

SAAJ API overview

SAAJ is a small Java SE API that lets you perform the following tasks:

  • create an endpoint-to-endpoint connection
  • create a SOAP message
  • create an XML fragment
  • add content to the header of a SOAP message
  • add content to the body of a SOAP message
  • create attachment parts and add content to them
  • access/add/modify parts of a SOAP message
  • create/add/modify SOAP fault information
  • extract content from a SOAP message
  • send a SOAP request-response message

SAAJ is associated with the javax.xml.soap package, which contains 14 interfaces and 13 classes. Various interfaces and classes extend their counterparts in the org.w3c.dom package, implying that part of a SOAP message is organized as a tree of nodes.

The following classes and interfaces are used to specify the structure of a SOAP message:

  • SOAPMessage represents the entire SOAP message. It contains a single SOAPPart instance and zero or more AttachmentPart instances.
  • SOAPPart contains a SOAPEnvelope instance, which represents the actual SOAP Envelope element.
  • SOAPEnvelope optionally contains a SOAPHeader instance and also contains a mandatory SOAPBody instance.
  • SOAPHeader represents the SOAP message's header block(s).
  • SOAPBody contains either a SOAPFault object or a SOAPBodyElement object containing the actual SOAP payload XML content.
  • SOAPFault stores a SOAP fault message.

Working with SAAJ involves creating a SOAP connection, creating SOAP messages, populating each message with content and optional attachments, sending the messages to an endpoint and retrieving replies, and closing the connection.

Creating a SOAP connection

You create a connection by working with the SOAPConnectionFactory and SOAPConnection classes. As its name implies, SOAPConnectionFactory is a factory class for retrieving SOAPConnection instances (actually, instances of subclasses of the abstract SOAPConnection class). A SOAPConnection instance represents an endpoint-to-endpoint connection to the Web service; the client and Web service exchange messages over this connection. The following example shows you how to instantiate the factory and obtain a SOAP connection:

SOAPConnectionFactory soapcf = SOAPConnectionFactory.newInstance();
SOAPConnection soapc = soapcf.createConnection();

Instantiate the factory by calling SOAPConnectionFactory's SOAPConnectionFactory newInstance() method. This method throws SOAPException when a SOAPConnectionFactory instance cannot be created. If a nonOracle Java implementation doesn't support the SAAJ communication infrastructure, this method throws an instance of the java.lang.UnsupportedOperationException class.

After instantiating SOAPConnectionFactory, call this instance's SOAPConnection createConnection() method to create and return a new SOAPConnection object. This method throws SOAPException when it's unable to create this object.

Creating a SOAP message

Create a SOAP message by working with the MessageFactory and SOAPMessage classes. MessageFactory provides a pair of methods for returning a MessageFactory instance:

  • MessageFactory newInstance() creates a MessageFactory object based on the default SOAP 1.1 implementation. This method follows an ordered lookup procedure to locate the MessageFactory implementation class. This procedure first examines the javax.xml.soap.MessageFactory system property, and lastly calls an instance of the SAAJMetaFactory class's MessageFactory newMessageFactory(String protocol) method to return that factory. This method throws SOAPException when it's unable to create the factory.
  • MessageFactory newInstance(String protocol) creates a MessageFactory object that's based on the SOAP implementation specified by the protocol argument, which is one of the SOAPConstants interface's DEFAULT_SOAP_PROTOCOL, DYNAMIC_SOAP_PROTOCOL, SOAP_1_1_PROTOCOL, or SOAP_1_2_PROTOCOL constants. This method throws SOAPException when it's unable to create the factory.

After instantiating MessageFactory, call one of the following methods to create a SOAPMessage instance:

  • SOAPMessage createMessage() creates and returns a new SOAPMessage object (actually, an instance of a concrete subclass of this abstract class) with default SOAPPart, SOAPEnvelope, SOAPBody (initially empty) and SOAPHeader objects. This method throws SOAPException when a SOAPMessage instance cannot be created, and UnsupportedOperationException when the MessageFactory instance's protocol is DYNAMIC_SOAP_PROTOCOL.
  • SOAPMessage createMessage(MimeHeaders headers, InputStream in) internalizes the contents of the given java.io.InputStream object into a new SOAPMessage object and returns this object. The MimeHeaders instance specifies transport-specific headers that describe the various attachments to the SOAP message. This method throws SOAPException when a SOAPMessage instance cannot be created, java.io.IOException when there's a problem reading data from the input stream, and java.lang.IllegalArgumentException when the MessageFactory instance requires one or more MIME headers to be present in the argument passed to headers and these headers are missing.

The following example shows you how to instantiate the factory and create a SOAPMessage object that's ready to be populated:

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage soapm = mf.createMessage();

Populating a SOAP message with content and optional attachments

SOAPMessage describes a SOAP message optionally followed by MIME-typed attachments. The SOAP message part of this object is defined by an instance of a concrete subclass of the abstract SOAPPart class.

1 2 3 4 5 6 Page 1
Page 1 of 6
InfoWorld Technology of the Year Awards 2023. Now open for entries!