Appendix F. SAAJ Attachments

In addition to the resources it gives you when building simple SOAP messages, SAAJ can help you build SOAP Messages with Attachments (SwA). Appendix E pointed out that SwA is not supported by the WS-I Basic Profile 1.0. Supported or not, SwA is a major piece of functionality in SAAJ, so it's covered by this book. Still, I recommend you use SwA only when your project requires you to.

SwA was covered in detail in Appendix E, but a review of the basic concepts here will be helpful. SwA uses the MIME message format to allow SOAP documents to refer to non-XML data, such as images, documents, digital signatures, and serialized objects. A MIME message (also called a package) is divided into parts, each of which is a block of raw data and MIME headers, separated by a line of unique boundary characters. An SwA message has a MIME type of multipart/related. The root part of an SwA MIME message is the actual SOAP message. Listing F-1 shows an example of an SwA message (the bulk of the raw data in each MIME part is omitted for brevity).

Example F-1. A Sample SwA Message

------=_Part_0_8994558.1029754184304
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
Content-Id: cid:[email protected]

<soap:Envelope
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <mh:submitBook xmlns:mh="http://www.Monson-Haefel.com/jwsbook/mh">
         <isbn>0596002262</isbn>
         <coverImage href="cid:[email protected]"/>
         <manuscript href="cid:[email protected]"/>
      </mh:submitBook>
   </soap:Body>
</soap:Envelope>
------=_Part_0_8994558.1029754184304
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-Id: [email protected]

ÿøÿà ÔJ+ñ á Ëïør‡ü C_'Ñû# á–_ÿ +ëüŒ¿à¥^_—ÃÏø(qÛø§ìj(
A Sample SwA Message ò£â-ìmû@|X´ø_¨xËâwÃh_#~Ô-
...
°_ÁΫÙâ÷‰|;£kúfo_sR_î»ñãVøÓñö_ÆA Sample SwA Message qâgÔ‡ÄHÿ h© __fã
A Sample SwA Message rvNR˧&>N_|_é~_É·tϸ_′¥'Oë
------=_Part_0_8994558.1029754184304
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Id: [email protected]

%PDF-1.3
...
%%EOF

------=_Part_0_8994558.1029754184304--

You learned in Chapter 13 that SAAJ is a SOAP API that models the structure of an SwA message. SAAJ treats all SOAP messages as SOAP messages with attachments. You can build a simple SOAP message without attachments, but you still need to use the SAAJ SwA programming model to do so. In this section we'll develop SwA messages with real attachments, but before we can start building SwA messages, you need to learn a little about the Java Activation Framework, which is central to SAAJ's support for SwA.

The Java Activation Framework

The Java Activation Framework (JAF) was finalized in 1998, back when Sun was primarily focused on Java's role as a GUI programming language. At that time JavaBeans was still in vogue, Swing was hot, servlets were new, and the J2EE platform didn't even exist. Back then Sun needed a standard discovery API for both file viewers and JavaMail, which provided the impetus for creating JAF. By “discovery” I mean a mechanism that can dynamically find the right components to handle data that's arbitrary, but typed. In particular, JAF was developed as a discovery API to enable GUI developers to discover viewers and editors for documents and image data dynamically.

If you have used a GUI file browser, a modern e-mail application, or a Web browser, then you have used a discovery mechanism. All of these applications allow you to view and edit a file or embedded data (such as a document or image) using programs associated with that type of data. For example, when you receive an e-mail with a PDF attachment, double-clicking on the attachment will, if you're like most people, launch Adobe Acrobat viewer. In most modern operating systems, double-clicking on an HTML file in a file browser will launch your default Web browser. That's discovery.

Discovery depends on a registry of some kind that can associate each file type with an appropriate application. Microsoft Windows, for example, allows you to associate file extensions (.html, .doc, .gif, and so on) with specific applications, which is how Windows Explorer knows what application to launch when you double-click on a file. Another example is an e-mail application like Microsoft's Outlook Express or Mozilla (open source Netscape), which maps MIME types to software applications. When you choose to view an e-mail attachment, the e-mail program examines the MIME content-type to find out which application to launch.

Simply put, JAF provides a framework for dynamically discovering visual widgets to handle (view, edit, print, and so on) any kind of data described by MIME headers. While JAF is focused on the GUI side of things, as a framework for dynamically discovering objects that can manipulate specific MIME types, it's useful for non-visual systems like SAAJ as well. In particular JAF can map Java types, like java.awt.Image, to special handlers that seamlessly convert them to streams of data. This mechanism is important in SOAP messaging because it allows SAAJ to convert Java objects (such as AWT images, DOM Document objects, and files) into raw data contained by SwA MIME parts, automatically. For example, using SAAJ, you can add an Image object to a SOAP message without having to convert the image to a stream of bytes first—JAF will take care of that for you, behind the scenes. The following code snippet illustrates.

MessageFactory mf = MessageFactory.newInstance();
SOAPMessage message = mf.createMessage();
java.awt.Image image = ...; // get an image form somewhere
AttachmentPart jpegAttach =
               message.createAttachmentPart(image ,"image/jpeg");

While JAF makes it simple to add attachments to SAAJ messages, it's also full of pitfalls that can cause a lot of unexpected problems. The rest of this section will explain in detail how JAF accomplishes its magic so that you are aware of its strengths and weaknesses and can avoid some of the traps that less knowledgeable developers are sure to encounter when using attachments.

DataHandler

Central to the JAF framework is the javax.activation.DataHandler class, which is also the central figure in SAAJ facilities for creating and adding attachments to SOAP messages. Whenever an attachment is added to a SAAJ message, the attached object is invariably embedded in a DataHandler object. This may be hidden, occurring behind the scenes, as was the case in the first example, or it can be done explicitly, as in the following snippet.

AttachmentPart pdfAttach = message.createAttachmentPart();
FileDataSource file = new FileDataSource("manuscript.pdf");
DataHandler pdfDH = new DataHandler(file);
pdfAttach.setDataHandler(pdfDH);

The DataHandler class, in Listing F-2, can be instantiated to represent just about any kind of data: an image, a PDF document, a DOM Document object—whatever you like. The DataHandler provides methods for reading and writing data streams, accessing the MIME type of the data, and creating a Java object that represents the data in a stream.

Example F-2. The javax.activation.DataHandler Class (Abbreviated)

package javax.activation;
import java.io.*;
...
public class DataHandler implements java.awt.datatransfer.Transferable {
  ...
  public java.io.InputStream  getInputStream()  ...
  public java.io.OutputStream getOutputStream() ...
  public void writeTo(java.io.OutputStream os) ...
  public String getContentType() ...
  public Object getContent() ...
  public DataSource getDataSource() ...
  ...
}
  • The getInputStream() method provides access to the data contained by the DataHandler as an InputStream, which you can use to read the data.

  • The getOutputStream() provides access to the data contained by the Data Handler via an OutputStream, which you can use to overwrite the data.

  • The writeTo() method writes the data contained by the DataHandler to any output stream that you provide.

  • The getContentType() method returns the MIME content-type, such as image/jpeg, application/pdf, or text/xml, of the data contained by the DataHandler.

  • The getContent() method returns an object that represents the data contained by the DataHandler. If the data is an image, for example, it might return a java.awt.Image object.

  • The getDataSource() method returns a javax.activation.DataSource object, which is discussed in detail later.

DataHandler also defines many other methods related to GUI operations that use the JAF CommandInfo object, which identifies viewers, editors, and printers, and transfers data to AWT components. These are not important to SAAJ, however, because it's not a GUI tool, and won't be covered in this overview of JAF.

DataHandler follows the Delegation pattern, providing a consistent interface to data available from many different sources and in many different formats. The term “Delegation” is taken from the lexicon of basic design patterns, and refers to an object that delegates some or all of its work to other objects.[1] When you invoke a method of a DataHandler, a completely different and hidden object may do the work. DataHandler defines three constructors, shown in Listing F-3. The type of object to which it delegates tasks depends largely on the constructor used.

Example F-3. Constructors for the javax.activation.DataHandler Class (Abbreviated)

package javax.activation;

public class DataHandler ...

    public DataHandler(DataSource ds) ...
    public DataHandler(Object obj, String mimeType) ...
    public DataHandler(java.net.URL url) ...
    ...
}

When DataHandler is instantiated using the constructor that expects an Object and a String identifying a MIME type, it will dynamically discover and use a specialized content handler, which is an implementation of the class javax.activation.DataContentHandler. If it's instantiated with either of the other two constructors, the DataHandler will delegate to a subtype of the javax.activation.DataSource interface. The difference between these delegates is significant, so understanding their strengths and weaknesses is important. The next two sections describe the DataContentHandler class and the DataSource interface in detail.

DataContentHandler

An implementation of the DataContentHandler interface is used to convert a Java object into a stream of data, or vice versa. Each different combination of Java object type and data stream type has its own DataContentHandler type (called a DCH for short). For example, if you need to convert java.awt.Image objects into JPEG data streams, you configure your system to use a DCH designed specifically for that purpose. Similarly, DCHs can be used to convert a DOM Document object into a character stream, a serializable Java object into a Java standard serialized stream, and so on. The possibilities are limitless.

When DataHandler is instantiated using the DataHandler(Object obj, String mimeType) constructor, it will delegate exclusively to one specialized type of DCH. Which type depends on the MIME type associated with the DataHandler when it was created. In the following code snippet, the data contained by a Data Handler that handles the image/jpeg MIME type is written to a stream. In order for this to work, the DataHandler must discover and delegate to a DCH that can handle Image type objects and JPEG data.

java.awt.Image image = ...; // get image from somewhere
DataHandler dataHandler = new DataHandler( image, "image/jpeg" );
FileOutputStream outStream = FileOutputStream( "image.jpg" );
dataHandler.writeTo( outStream );

The DataHandler discovers the type of DCH it will use from a registry called a mailcap file. (Vendors can implement registries in other ways, but mailcap is the default.) A mailcap file maps MIME types to DCHs and other components. The mailcap file format is defined by RFC 1524, which specifies a generic format for associating MIME content-types with specific applications.[2] RFC 1524 has been around since 1993—before the Java platform was released—but it's well understood and flexible, so it was adopted for use in JAF. Because JAF is a Java API, the only kinds of entries it can understand in a mailcap file are those that pertain to Java types, so JAF defines a standard naming system to identify JAF DCHs and other types of components in a mailcap file. For example, Listing F-4 shows two entries in a mailcap file that map DCHs provided by the SAAJ reference implementation to their respective MIME types.

Example F-4. A mailcap File

image/jpeg; ; x-java-content-handler=
com.sun.xml.messaging.saaj.soap.JpegDataContentHandler

text/xml; ; x-java-content-handler=
com.sun.xml.messaging.saaj.soap.XmlDataContentHandler

Each entry in a mailcap file appears as a single line (this book is not wide enough to display a whole line), and each new line designates a new mapping. An entry starts with the MIME type, followed by name-value pairs associating an application with a command. JAF defines several standard commands, such as x-java-view, x-java-print, x-java-edit, and x-java-content-handler. We are concerned with the x-java-content-handler command here, and not the GUI-related commands for viewing, editing, and printing.

The JAF framework will use a mailcap file located anywhere in the classpath, and will usually be able to use the ones in the java.home/lib directory or in the “users home” directory. The best place to put the mailcap file is in the META-INF subdirectory of the JAR file that contains the DCH class files. If the DCH's JAR file is included in the classpath, the mailcap will be found and its entries registered with JAF. The mailcap file should always be named mailcap with no extension. You can have as many mailcap files as you wish.[3]

The default behavior is for DataHandler to use the MailcapCommandMap class to load mailcap files from the classpath. When trying to find a DCH, it builds a list of mailcap files, and checks them in the order found. For JAF 1.0.2 (where the behavior was extended slightly to find all mailcap files), the mailcap list order is:

  1. user.home/mailcap

  2. java.home/lib/mailcap

  3. META-INF/mailcap

  4. /META-INF/mailcap.default

When looking for a content-type, the DataHandler traverses this list in order, and uses the first mailcap file that has a mapping for the desired MIME type to create the DCH. When you're adding your own DCH to override the behavior of a DCH included with some other API, such as JavaMail, it's vital to understand where it will appear in the above order—especially with the classpath ordering of the META-INF/mailcap case. A problem occurs when JavaMail is in the classpath, for example, because it defines the following mappings:

text/plain;;   x-java-content-handler=com.sun.mail.handlers.text_plain
text/html;;    x-java-content-handler=com.sun.mail.handlers.text_html
text/xml;;     x-java-content-handler=com.sun.mail.handlers.text_xml
image/gif;;    x-java-content-handler=com.sun.mail.handlers.image_gif
image/jpeg;;   x-java-content-handler=com.sun.mail.handlers.image_jpeg
multipart/*;;  x-java-content-handler=com.sun.mail.handlers.multipart_mixed

Each of these MIME types also has a mapping in SAAJ/JAX-RPC. If you want to use the mailcap files that come with a specific SAAJ/JAX-RPC implementation, you have to make sure that they are found before the JavaMail mailcap files. This is tricky business that can cause you some real headaches if mailcap files are not set up properly.

Another approach to mapping MIME types to DCHs, which avoids the use of mailcap files, is to implement a custom DataContentHandler Factory. There is a static reference to this type in the DataHandler class that is consulted before the mailcap files are checked, provided you set it programmatically. A big drawback of this approach is that the factory may be set only once in a VM process.

Once the mapping is discovered and the DCH type appropriate to the MIME type is instantiated, the DataHandler delegates to the DCH object. For example, the code snippet below creates a DataHandler instantiated with a MIME type of image /jpeg, and the writeTo() call should trigger the instantiation of a Data Content Handler for that MIME type.

java.awt.Image image = ... get image from somewhere
DataHandler dataHandler = new DataHandler( image, "image/jpeg");
FileOutputStream outStream = FileOutputStream( "image.jpg" );
dataHandler.writeTo( outStream );

For the writeTo() method to achieve that result, a DCH must exist that is mapped to the image/jpeg MIME type and is able to convert an Image object into a data stream. Figure F-1 illustrates the steps that a DataHandler will go through to locate and delegate to a DCH.

Sequence Diagram of DataHandler Delegation to DataContentHandler

Figure F-1. Sequence Diagram of DataHandler Delegation to DataContentHandler

The first time a DataHandler needs to delegate to a DCH, it will request an instance of the proper DCH from the JAF framework. The JAF framework checks the mailcap registry to see if a DCH type is registered for the DataHandler object's MIME type. If it finds such a DCH type, the JAF creates an instance of that type and returns it to the DataHandler, which will use the DCH as its delegate. If no DCH is associated with the MIME type, the JAF throws an exception. The DataHandler object will not attempt to find a DCH until it's actually necessary (lazily), which is why it doesn't throw an exception as soon as it's created with a MIME type that isn't mapped to a DCH.

An important thing to remember is that DCHs are limited to the types of Java objects they are programmed to handle. For example, the reference implementation of SAAJ, which is written by Sun Microsystems, has two DCHs for processing image data: GifDataContentHandler for the image/gif MIME type, and JpegData ContentHandler for the image/jpeg MIME type. Although JpegDataContent Handler works fine, the GifDataContentHandler is practically useless. It cannot convert any type of object (not even java.awt.Image) into a data stream. Of course, this DCH is part of the reference implementation, so it's not so bad that it's limited, but it's an excellent illustration of an important point: The types of Java objects you can attach to a SOAP message are limited by the types of DCHs you have available. In other words, you cannot arbitrarily attach Java objects to SAAJ messages. You have to be sure that there is a DCH mapped to that MIME type and that the DCH will be able to handle that type of Java object. This information can be obtained only by examining the mailcap files to discover which DCHs are registered, and then examining the documentation of the DCHs themselves to see what type of Java objects they support. This research takes time, but it can save you a lot of headaches.

If the MIME type or Java object you want to attach to SOAP messages is not supported, you can develop a new DCH and register it in a mailcap file. That may be a trivial effort or it may be a lot of work, depending on what you want to attach. Fortunately, DCHs are not your only option.

DataSource

In addition to the DataContentHandler type, JAF defines the interface type javax.activation.DataSource, which can be very useful when attaching data derived from some resource like a file or a Web server. When a DataHandler is created using a DataSource object, the DataHandler will not depend on the presence of a DCH to function properly—the DataSource will possess all the functionality necessary to perform the delegated operations. A DataHandler will use a Data Source instead of a DCH when it is constructed with a DataSource or URL parameter. The following recapituation of Listing F-3 highlights these two constructors.

package javax.activation;

public class DataHandler ...

    public DataHandler(DataSource ds) …
    public DataHandler(java.net.URL url) …
    public DataHandler(java.lang.Object obj, java.lang.String mimeType) ...
...
}

Careful—don't use a DataSource as the obj parameter in the constructor DataHandler(java.lang.Object obj, java.lang.String mime Type). The DataHandler will not recognize the DataSource and will attempt to delegate operations to a DCH—which will cause the Data Handler to throw an exception.

The DataSource interface follows the Adapter design pattern.[4] It provides a single abstraction for any kind of data source. The DataSource interface defines methods for accessing an InputStream, an OutputStream, the MIME type, and the name of the underlying source of data. It's defined as shown in Listing F-5.

Example F-5. The javax.activation.DataSource Interface

package javax.activation;
import java.io.InputStream;
import java.io.OutputStream
import java.io.IOException;

public interface DataSource {

    public String getName();
    public String getContentType();
    public InputStream getInputStream() throws IOException;
    public OutputStream getOutputStream() throws IOException;

}

You may have noticed that the methods defined in DataSource have the same signatures as some of the methods defined in DataHandler. When a DataHandler is created with a DataSource object, it will delegate to the DataSource the methods they have in common.

JAF defines two standard DataSource objects: javax.activation.FileDataSource and javax.activation.URLDataSource. As its name suggests, FileDataSource provides access to a file using its getInputStream() and getOutputStream() methods, and allows you to read from and write to that file. Under the covers, the FileDataSource uses java.io.FileInputStream and java.io.FileOutputStream to read and write. The following snippet shows you how to create a DataHandler that will use a FileDataSource.

// Create a FileDataSource that represents a JPEG file
FileDataSource jpegSource = new FileDataSource("someimage.jpeg");

// Create a DataHandler that delegates to the FileDataSource
DataHandler dataHandler = new DataHandler( jpegSource );

The URLDataSource represents a file at some URL. Under the covers the URL DataSource uses a java.net.HttpURLConnection if the file is at some distant Internet address, or a java.net.JarURLConnection if the URL points to a local JAR file, or any other implementation of URLConnection. The HttpURLConnection employs the HTTP protocol to read and write files on an HTTP Web server. The HttpURLConnection.getOutputStream() method will throw an exception, so this method won't work if you are accessing a URL at a distant Web server—but it does work with JAR files. The following shows how to create a DataHandler that will use a URLDataSource object.

// Create a URL that points to a remote PDF document
URL url = new URL("http://www.Monson-Haefel.com/jwsbook/document.pdf")

// Create a DataHandler that delegates to the URLDataSource
URLDataSource dataSource = new URLDataSource( url );
DataHandler pdfHandler = new DataHandler( dataSource );

The URL constructor uses a URLDataSource under the covers, so it's the same as calling the DataHandler class's DataSource constructor with the URLData Source parameter. The following code snippet also results in a DataHandler that will use a URLDataSource.

// Create a URL that points to a remote PDF document
URL url = new URL("http://www.Monson-Haefel.com/jwsbook/document.pdf")

// Create a DataHandler that delegates to the URLDataSource
DataHandler pdfHandler = new DataHandler( url );

Some DataSource implementations are designed to discover their MIME types dynamically. The FileDataSource, for example, discovers its MIME type using the MIME-type registry. The default MIME-type registry in JAF is the mimetypes. default file, which is included in the JAF binary JAR file. You can augment this registry with your own mime.types file, which should be stored in the same kinds of locations you'd place mailcap files (see Section F.1.2). The mime.types and mimetypes.default files map file extensions to MIME types. Listing F-6 shows the mimetypes.default file that is included with JAF.

Example F-6. The mimetypes.default File

text/html                 html htm HTML HTM
text/plain                txt text TXT TEXT
image/gif                 gif GIF
image/ief                 ief
image/jpeg                jpeg jpg jpe JPG
image/tiff                tiff tif
image/x-xwindowdump       xwd
application/postscript    ai eps ps
application/rtf           rtf
application/x-tex         tex
application/x-texinfo     texinfo texi
application/x-troff       t                    tr roff
audio/basic               au
audio/midi                midi mid
audio/x-aifc              aifc
audio/x-aiff              aif aiff
audio/x-mpeg              mpeg mpg
audio/x-wav               wav
video/mpeg                mpeg mpg mpe
video/quicktime           qt mov
video/x-msvideo           avi

Because the mimetypes.default file is included in the standard JAF binary JAR file, all these MIME-type/file-extension mappings are available by default. Each line in the MIME-type registry represents a different MIME-type/file-extension mapping. If a given file extension doesn't have a matching MIME type, then the default MIME type application/octet-stream is used.

In the next section, you will learn how to apply what you have learned about JAF to the task of creating attachments in SAAJ. As you will quickly discover, having a good understanding of DataHandler, DataContentHandler, and DataSource types will be indispensable to creating SOAP Messages with Attachments using SAAJ.

SAAJ and JAF: AttachmentPart

Adding attachments to a SAAJ SOAPMessage object is done using an Attachment Part object, which represents a MIME part—that is, an attachment in an SwA message. The AttachmentPart provides methods for manipulating the headers, as well as the raw data content of a MIME part. AttachmentPart objects are contained by the SOAPMessage and are siblings of the SOAPPart object, which models the root MIME part (the SOAP document) of the SwA message.

In Listing F-7, SaajExample_F1 creates an SwA that has two attachments: a JPEG image and a PDF document. The SwA generated by SaajExample_F1 is incomplete at this point. As the section progresses, I will fill in the missing pieces.

Example F-7. Creating a Simple SwA Message with SAAJ

package com.jwsbook.saaj;
import javax.xml.soap.*;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.FileOutputStream;
import javax.activation.FileDataSource;
import javax.activation.DataHandler;

public class SaajExample_F1 {
  public static void main(String [] args) throws Exception {
    // Create SOAPMessage
    MessageFactory mf = MessageFactory.newInstance();
    SOAPMessage message = mf.createMessage();

    // Attach java.awt.Image object to SOAP message.
    Image image = Toolkit.getDefaultToolkit().createImage("cover.jpg");
    AttachmentPart jpegAttach = message.createAttachmentPart();
    jpegAttach.setContent( image ,"image/jpeg");
    message.addAttachmentPart(jpegAttach);

    // Attach PDF FileDataSource to SOAP message
    FileDataSource file = new FileDataSource("manuscript.pdf");
    DataHandler pdfDH = new DataHandler(file);
    AttachmentPart pdfAttach = message.createAttachmentPart();
    pdfAttach.setDataHandler(pdfDH);
    message.addAttachmentPart(pdfAttach);

    // Write SOAPMessage to file
    FileOutputStream fos = new FileOutputStream("SaajExample_F1.out");
    message.writeTo(fos);
    fos.close();
  }
}

Executing SaajExample_F1 produces a file that contains the MIME message, including the default SOAP document as well as the JPEG and PDF MIME parts. An abbreviated version of the output file, SaajExample_F1.out, is shown in Listing F-8.

Example F-8. The Output of SaajExample_F1 (Abbreviated)

------=_Part_0_14746332.1029863145115
Content-Type: text/xml

<soap:Envelope 
    xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header/>
    <soap:Body/>
</soap:Envelope>
------=_Part_0_14746332.1029863145115
Content-Type: image/jpeg

ÿøÿà ÔJ+ñ á Ëïør‡ü C_'Ñû# á–_ ÿ +ëüŒ¿à¥^_—ÃÏø(qÛø§ìj(
The Output of SaajExample_F1 (Abbreviated) ò£â-ìmû@|X´ø_¨xËâwÃh_#~Ô-
...
°_ÁÿÙâ÷‰|;£kúfo_sR_î»ñãVøÓñö _ÆThe Output of SaajExample_F1 (Abbreviated) qâgÔ‡ÄHÿ h© __fã
The Output of SaajExample_F1 (Abbreviated) rvNR˧&>N_|_é~_É·tϸ_′¥'Oë
------=_Part_0_14746332.1029863145115
Content-Type: application/octet-stream

%PDF-1.3
...
%%EOF

------=_Part_0_14746332.1029863145115--

If you compare this SwA message to the one at the beginning of this Appendix, you'll notice some things are missing, like the Content-Transfer-Encoding and Content-Id MIME headers. In addition, the SOAP document itself is devoid of any meaningful content; it's just a skeleton. Later we'll fix these problems, but for now we want to focus on how attachments are added to the SOAPMessage object.

To run SaajExample_F1 and the rest of the examples in this book, you'll need to have a J2EE platform installed, the proper classpaths set up, and supporting Web services deployed.

Data Objects

SaajExample_F1 (Listing F-7) starts out by creating a SOAPMessage and then proceeds to add two attachments: a JPEG image and a PDF document. To create an attachment, you'll first need an object that represents the data you want to attach—we can refer to this object generically as the data object. The JPEG attachment uses an Image object, while the PDF attachment uses a FileDataSource as shown in this snippet from SaajExample_F1.

// Attach with java.awt.Image object to the SOAP message
Image image = Toolkit.getDefaultToolkit().createImage("cover.jpg");
...

// Attach with PDF FileDataSource to SOAP message
FileDataSource file = new FileDataSource("manuscript.pdf");
...

Image and FileDataSource are very different kinds of data objects, which il lustrates the variety of types you can use to create attachments. As you will soon see, however, this variety depends largely on the types of DataContentHandler and DataSource available.

The createAttachmentPart() Method

You create an AttachmentPart using the SOAPMessage.createAttachment Part() method. This method creates an empty, disconnected AttachmentPart with no data or MIME type. Once an empty AttachmentPart is created, its content must be set explicitly using either the setContent() or setDataHandler() method, as shown in this snippet from SaajExample_F1 (Listing F-7).

// Attach java.awt.Image object to SOAP message
Image image = Toolkit.getDefaultToolkit().createImage("cover.jpg");
AttachmentPart jpegAttach = message.createAttachmentPart();
jpegAttach.setContent( image ,"image/jpeg");
message.addAttachmentPart(jpegAttach);

// Attach PDF FileDataSource to SOAP message
FileDataSource file = new FileDataSource("manuscript.pdf");
DataHandler pdfDH = new DataHandler(file);
AttachmentPart pdfAttach = message.createAttachmentPart();
pdfAttach.setDataHandler(pdfDH);
message.addAttachmentPart(pdfAttach);

The SOAPMessage class also provides two more createAttachmentPart() methods, each of which consolidates the operations of creating the Attachment Part and setting the data object in one operation. The following code snippet shows how these alternative createAttachmentPart() methods might have been used in SaajExample_F1.

// Attach java.awt.Image object to SOAP message
Image image = Toolkit.getDefaultToolkit().createImage("cover.jpg");
AttachmentPart jpegAttach = message.createAttachmentPart(image ,"image/jpeg");
message.addAttachmentPart(jpegAttach);

// Attach with PDF FileDataSource to SOAP message
FileDataSource file = new FileDataSource("manuscript.pdf");
DataHandler pdfDH = new DataHandler(file);
AttachmentPart pdfAttach = message.createAttachmentPart(pdfDH);
message.addAttachmentPart(pdfAttach);

The setContent() and setDataHandler() Methods

There are two methods for adding data objects and MIME types to an empty AttachmentPart. You can use the AttachmentPart.setContent() or the AttachmentPart.setDataHandler() method. SaajExample_F1 uses both methods, as illustrated in the following snippet from Listing F-7.

AttachmentPart jpegAttach = message.createAttachmentPart();
jpegAttach.setContent(image ,"image/jpeg");
...
AttachmentPart pdfAttach = message.createAttachmentPart();
DataHandler pdfDH = new DataHandler(file);
pdfAttach.setDataHandler(pdfDH);

The setDataHandler() and setContent() methods accomplish exactly the same result. Under the covers, the setContent() method actually creates a new DataHandler object and then calls its own setDataHandler() method. For example, Listing F-9 shows an implementation of AttachmentPart by a fictitious vendor, XYZ Corporation, that represents the most likely implementation of these methods by a vendor.

Example F-9. A Hypothetical AttachmentPart Implementation

package com.xyz.saaj;

import javax.xml.soap.*;
import javax.activation.DataHandler;

/*
 This is a hypothetical example of a vendor's implementation of
 AttachmentPart.

 It illustrates that the most likely implementation of the setContent and
 getContent methods is to reuse the setDataHandler and getDataHandler methods.
*/
public class XyzAttachmentPart implements javax.xml.soap.AttachmentPart{

    DataHandler dataHandler;

    public DataHandler getDataHandler() throws SOAPException {
        return dataHandler;
    }

    public void setDataHandler(DataHandler dh)
    throws java.lang.IllegalArgumentException{
        dataHandler = dh;
    }

    public Object getContent() throws SOAPException {
        try{
            return this.getDataHandler().getContent();
        }catch (IOException ex){
            throw new SOAPException(ex);
        }
    }

    public void setContent(Object object, String mimeType)
    throws java.lang.IllegalArgumentException{
        DataHandler dh = new DataHandler(object, mimeType);
        this.setDataHandler(dh);
    }

    ...
}

As this example illustrates, both setDataHandler() and setContent() have corresponding get methods. setContent() and getContent() are more convenient to use in some cases because they don't require that you work directly with a DataHandler object. In most cases they save you a couple of lines of code. Of course, if your data is contained in a DataSource object, you'll have to use the setData Handler() method because the setContent() method works only with simple data objects like java.awt.Image. Never pass a DataSource or DataHandler object into the setContent() method.

The getContent() Method

As you can see in Listing F-9, AttachmentPart.getContent() delegates its work to the underlying DataHandler object, which will attempt to delegate the call to a DataContentHandler object. If the DataHandler was constructed with a data object, then it will delegate to a DCH—provided it can find a match in the mailcap registry; if it can't locate a DCH, it will throw an exception. If the DataHandler was constructed with a DataSource object, it will first attempt to delegate the getContent() method to a DCH; if it can't find a DCH, it will return an InputStream obtained from the DataSource. Interestingly, the DataHandler always attempts to delegate to a DCH first, even if it was created using a DataSource.

This seemingly minor point is quite important. It illustrates the purpose of the getContent()method, which is to return a Java object that represents the attachment. The InputStream of the DataSource is returned only as a last resort. For example, if when using the SAAJ reference implementation you create an Attachment Part using a FileDataSource based on a JPEG file, and then call AttachmentPart.getContent(), the method returns a java.awt.image.BufferedImage object (a subtype of java.awt.Image). Although the attachment is created with a DataSource, a DCH found in the mailcap file is used to generate a Java object for the JPEG data. The following snippet shows how the JPEG is added to an AttachmentPart as a FileDataSource, and subsequently accessed as an Image object.

FileDataSource file = new FileDataSource("cover.jpg");
DataHandler jpegDH = new DataHandler(file);
AttachmentPart jpegAttach = message.createAttachmentPart(jpegDH);
...
Image image = (Image)jpegAttach.getContent();

The type of object returned from a DCH will depend on how that DCH is coded, but SAAJ specifies a set of return types for five specific MIME types that must be supported by a JAX-RPC-compliant implantation of SAAJ. Because you are likely to use SAAJ in combination with JAX-RPC, this requirement is important. Your vendor may support additional MIME types; check your vendor's documentation for details. Table F-1 shows five MIME types and their corresponding Java types.

Appendix G provides a more detailed explanation of each of these mappings.

Notice that the MimeMultipart class is part of the JavaMail API, so JavaMail will be in the classpath, with its mailcap file—which could cause problems when you add your own mailcap file—as I described in Section F.1.2.

Table F-1. Minimum AttachmentPart.getContent() Return Types

MIME Type

AttachmentPart.getContent() Return Type

text/plain

java.lang.String

text/xml or application/xml

javax.xml.transform.Source

image/jpeg

java.awt.Image

image/gif

java.awt.Image

multipart/*

javax.mail.internet.MimeMultipart

In essence the type returned by AttachmentPart.getContent() depends on the DCHs that are installed. One consequence is that developing portable SAAJ applications may force you to register the same DCHs on every platform you expect your SAAJ application to run on. You can, however, expect compliant J2EE Web Services platforms to support, at the very least, the MIME-to-Java mappings shown in Table F-1.

The MIME Header Methods

AttachmentPart offers a number of methods for adding, finding, removing, and replacing the MIME headers associated with an attachment. Listing F-10 shows these methods as they're declared in the AttachmentPart class. Most of these methods are self-describing and are also well documented by the SAAJ API documentation, so I'll discuss only a few of them here.

Example F-10. The javax.xml.soap.AttachmentPart Class (Abbreviated)

package javax.xml.soap;
import java.util.Iterator;
...
public abstract class AttachmentPart {

    // Commonly used MIME header methods
    public String getContentId() ...
    public void setContentId(String contentId)
      throws IllegalArgumentException ...

    public String getContentLocation()...
    public void setContentLocation(String contentLocation)
      throws IllegalArgumentException ...

    public String getContentType() ...
    public void setContentType(String contentType)
      throws IllegalArgumentException ...

    // Generic MIME header methods
    public abstract void addMimeHeader(String name,String value)
      throws IllegalArgumentException ...
    public abstract void setMimeHeader(String name,String value)
      throws IllegalArgumentException ...
    public abstract String[] getMimeHeader(String name) ...
    public abstract Iterator getAllMimeHeaders() ...
    public abstract Iterator getMatchingMimeHeaders(String[] names)...
    public abstract Iterator getNonMatchingMimeHeaders(String[] names) ...
    public abstract void removeAllMimeHeaders() ...
    public abstract void removeMimeHeader(String header) ...
    ...
}

Appendix E: SOAP Messages with Attachments explains that SwA employs the Multipart /Related MIME message style when packaging a SOAP document with its attachments. This means that it is the root MIME part, the SOAP document, that refers to the attachments in an SwA, using either Content-Id or Content-Location headers. As an example, let's take a closer look at the complete SwA message, shown in Listing F-11. The attachments are referred to by their Content-Id MIME headers, using href attributes in the SOAP document.

Example F-11. A Sample SwA Message

------=_Part_0_8994558.1029754184304
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
Content-Id: cid:[email protected]

<soap:Envelope 
 xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
   <soap:Body>
      <mh:submitBook xmlns:mh="http://www.Monson-Haefel.com/jwsbook/mh">
         <isbn>0596002262</isbn>
         <coverImage href="cid:[email protected]"/>
         <manuscript href="cid:[email protected]"/>
      </mh:submitBook>
   </soap:Body>
</soap:Envelope>
------=_Part_0_8994558.1029754184304
Content-Type: image/jpeg
Content-Transfer-Encoding: binary
Content-Id: [email protected]

ÿøÿà ÔJ+ñ á Ëïør‡ü C_'Ñû# á–_ ÿ +ëüŒ¿à¥^_—ÃÏø(qÛø§ìj(
A Sample SwA Message ò£âìmû@|X´ø_¨xËâwÃh_#~Ô-
...
°_ÁÿÙâ÷‰|;£küfo_sR_î»ñãVøÓñö _ÆA Sample SwA Message qâgÔ‡ÄHÿ h© __ã
A Sample SwA Message rvNR˧&>N_|_é~_É·tϸ_′¥'Oë
------=_Part_0_8994558.1029754184304
Content-Type: application/pdf
Content-Transfer-Encoding: binary
Content-Id:[email protected]

%PDF-1.3
...
%%EOF

------=_Part_0_8994558.1029754184304--

The SAAJ reference implementation doesn't create Content-Id headers automatically; you must add them manually. In addition, the developer can add other MIME headers, such as the Content-Transfer-Encoding, which tells us the format of the data (such as binary or base64). You can also change headers. For example, when SaajExample_F1 (Listing F-7) generates its output, it sets the PDF attachment with a Content-Type of "application/octet-stream", because no MIME type is associated with the .pdf extension in the mimetypes.default file. You can create a mime.types file with an entry for the .pdf extension, or you can change it manually, as SaajExample_F2 does in Listing F-12, using one of the MIME header methods.

SaajExample_F2 modifies SaajExample_F1 by employing methods for setting the Content-Id and the Content-Transfer-Encoding, and modifying the Content-Type header of the PDF attachment. This example also adds a SOAPPart with a complete SOAP message that refers to the attachments by way of their Content-Id headers.

Example F-12. Creating a Complete SwA Message

package com.jwsbook.saaj;
import javax.xml.soap.*;
import java.awt.Image;
import java.awt.Toolkit;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import javax.activation.FileDataSource;
import javax.activation.DataHandler;
import javax.xml.transform.stream.StreamSource;

public class SaajExample_F2 {
  public static void main(String [] args) throws Exception {
    // Create SOAPMessage
    MessageFactory mf = MessageFactory.newInstance();
    SOAPMessage message = mf.createMessage();

    // Create the SOAPPart
    createSOAPPart(message);

    // Attach with java.awt.Image object to the SOAP message
    Image image = Toolkit.getDefaultToolkit().createImage("cover.jpg");
    AttachmentPart jpegAttach = 
                   message.createAttachmentPart(image ,"image/jpeg");
    jpegAttach.addMimeHeader("Content-Transfer-Encoding","binary");
    jpegAttach.setContentId("[email protected]");
    message.addAttachmentPart(jpegAttach);

    // Attach with PDF FileDataSource to SOAP message
    FileDataSource file = new FileDataSource("manuscript.pdf");
    DataHandler pdfDH = new DataHandler(file);
    AttachmentPart pdfAttach = message.createAttachmentPart(pdfDH);
    pdfAttach.addMimeHeader("Content-Transfer-Encoding","binary");
    pdfAttach.setContentId("[email protected]");
    pdfAttach.setContentType("application/pdf");
    message.addAttachmentPart(pdfAttach);

    // Write SOAPMessage to file
    FileOutputStream fos = new FileOutputStream("SaajExample_F2.out");
    message.writeTo(fos);
    fos.close();
  }

  public static void createSOAPPart(SOAPMessage message)
  throws SOAPException, java.io.IOException {
      // implementation goes here
  }
}

The output of SaajExample_F2 will be similar to the output shown previously in Listing F-11.

Except for the implementation of the createSOAPPart() method, the example above is complete. The creation of the SOAPPart is very similar to that of the AttachmentPart. The next section will address the SOAPPart and show three different implementations of the createSOAPPart() method of SaajExample_F2.

The SOAPPart

A SOAPPart object represents the root MIME part of an SwA message, which is always a SOAP document. The SOAPPart class includes many of the same methods defined in AttachmentPart, but it doesn't implement the AttachmentPart interface, because the SOAPPart is more restrictive than the AttachmentPart. The SOAPPart is designed exclusively to handle SOAP documents (MIME type text /xml), and to provide a means for accessing the SOAPEnvelope and its children.

For example, in the AttachmentPart the setContent() and getContent() methods return the java.lang.Object type, so they can work with any kind of data object. The SOAPPart also defines setContent() and getContent() methods, but they set and get the javax.xml.transform.Source type instead. The Source type is the return type that JAX-RPC requires for the text/xml MIME type (see Table F-1), so it's the appropriate type for the SOAPPart object to set and get. Listing F-13 shows these method signatures in a partial listing of the SOAPPart class.

Example F-13. The javax.xml.soap.SOAPPart Class (Abbreviated)

package javax.xml.soap;
import javax.xml.transform.Source;
...
public abstract class SOAPPart {

    public abstract void setContent(Source source)
      throws SOAPException ...

    public abstract Source getContent()
      throws SOAPException ...
    ...
}

SOAPPart is also more restrictive than the AttachmentPart with its Content-Type MIME header. Unlike the AttachmentPart, the SOAPPart doesn't define a setContentType() method, because this MIME header must be text/xml for a SOAP 1.1 document. Like AttachmentPart, SOAPPart does provide general-purpose MIME methods (that is, addMimeHeader(), setMimeHeader(), and removeMimeHeader()), but any attempt to change the Content-Type using one of these methods will result in an IllegalArgumentException.

For all its restrictions a SOAPPart does represent a MIME part, and as such it contains a content section (data) and MIME headers. You can populate the content section of a SOAPPart manually by creating a SOAPEnvelope, SOAPBody, and SOAP BodyElement, and so on. The SOAPMessage.getSOAPPart() method allows you to access the SOAP MIME part directly as shown in this snippet.

MessageFactory msgFactory = MessageFactory.newInstance();
SOAPMessage message = msgFactory.createMessage();
SOAPPart soap = message.getSOAPPart();

As an alternative, you can populate the content section with an existing SOAP document using a javax.xml.transform.Source type object, which is how Saaj Example_F2 builds its SOAP message. It first reads a file containing a SOAP document using a StreamSource object (an implementation of Source) and then passes the StreamSource to the SOAPPart using the setContent() method. The following snippet is the implementation of the createSOAPPart() method, which was omitted from the previous listing of SaajExample_F2 (Listing F-12).

import java.io.FileInputStream;
import javax.xml.transform.stream.StreamSource;
...
public class SaajExample_F2 {
  ...
  public static void createSOAPPart(SOAPMessage message)
  throws SOAPException, java.io.IOException {
    SOAPPart soapPart = message.getSOAPPart();
    FileInputStream soapFile = new FileInputStream("soapwa.xml");
    StreamSource source = new StreamSource(soapFile);
    soapPart.setContent(source);
    soapPart.addMimeHeader("Content-Transfer-Encoding","8bit");
    soapPart.setContentId("[email protected]");
    soapFile.close();
  }
}

StreamSource is only one of three standard Source types (StreamSource, DOMSource, and SAXSource), that you can use to set the content of a SOAPPart. The Source type and its subtypes are members of the TrAX API, which is the standard Java API for XSLT.

XSLT

XSLT (Xtensible Stylesheet Language Transformation) is an XML technology that defines a grammar, called an XSLT stylesheet, and processing rules for mapping documents in one format to documents in some other format. For example, you might use XSLT to translate XML-RPC messages into SOAP, or DocBook into XHTML.[5] XSLT stylesheets rely heavily on another XML technology called XPath, which is a declarative language for describing elements in an XML document, something like SQL for XML (that's a loose analogy). The key point is that, using XSLT, you can convert one XML document into another XML document. While XSLT is very powerful and interesting in its own right, an in-depth discussion of it is outside the scope of this appendix. Fortunately, you can remain blissfully ignorant of XSLT without it hampering your use of SAAJ one bit.

TrAX

What we are interested in here are the Source types defined by TrAX (Transformation API for XML), which is the J2EE standard API for XSLT. TrAX is defined in the javax.xml.transformation package and its subpackages. One of the primary types defined by TrAX is javax.xml.transform.Source, which is not much more than an empty interface. The Source interface is used as an abstraction for an object that contains or has access to an XML document. In TrAX there are three standard implementations of the Source type:

  1. javax.xml.transform.stream.StreamSource

  2. javax.xml.transform.dom.DOMSource

  3. javax.xml.transform.sax.SAXSource

The TrAX Source subtypes provide a convenient mechanism for embedding an XML SOAP document in a SOAPPart. It should be noted that the TrAX Source type doesn't provide any common methods for extracting the XML data from the Source object. Each implementation is completely different, which means the SOAPPart won't be able to handle arbitrary implementations of the Source type; it supports only the standard TrAX implementations: StreamSource, DOMSource, and SAXSource.

Using a StreamSource

You use a StreamSource when an XML document is accessible via some sort of data stream, specifically with java.io.InputStream and java.io.Reader types. SaajExample_F2 uses a FileInputStream, a subtype of InputStream. It could just as easily have used the FileReader type, which is better at dealing with international character sets.[6] The following snippet shows how SaajExample_F2 could be implemented to use a FileReader instead of a FileInputStream.

import java.io.FileReader;
import javax.xml.transform.stream.StreamSource;
...
public static void createSOAPPart(SOAPMessage message) 
throws SOAPException, java.io.IOException {
    SOAPPart soapPart = message.getSOAPPart();
    FileReader soapFile = new FileReader("soapwa.xml");
    StreamSource source = new StreamSource(soapFile);
    soapPart.setContent(source);
    soapPart.addMimeHeader("Content-Transfer-Encoding","8bit");
    soapPart.setContentId("[email protected]");
    soapFile.close();
}

Of course a file represents only one type of data stream; you can use StreamSource to read SOAP messages from network streams or JDBC or JMS or some other resource.

DOMSource

You use a DOMSource when the XML document is contained in a DOM Node object, specifically the org.w3c.dom.Node type, which is a part of the JAXP (Java API for XML Processing) family of technologies. If you work with DOM, a DOMSource is an excellent means to pass the SOAP document to the SOAPPart. In the following code snippet the createSOAPPart() method is modified to build a DOM Document (a subtype of Node) object from a file, then set the Document object as the content of the SOAPPart.

import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
...
public static void createSOAPPart(SOAPMessage message)
    throws SOAPException, java.io.IOException, org.xml.sax.SAXException,
        javax.xml.parsers.ParserConfigurationException {

    DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    DocumentBuilder builder = factory.newDocumentBuilder();
    Document document = builder.parse("soapwa.xml");
    document.getChildNodes();
    DOMSource domSource = new DOMSource(document);

    SOAPPart soapPart = message.getSOAPPart();
    soapPart.setContent(domSource);
    soapPart.addMimeHeader("Content-Transfer-Encoding","8bit");
    soapPart.setContentId("[email protected]");
}

SAXSource

You can also fill the contents of a SOAPPart object from a SAX parser using a SAX Source as shown in the following snippet.

import javax.xml.transform.sax.SAXSource;
import org.xml.sax.InputSource;
import java.io.FileReader;
...
public static void createSOAPPart(SOAPMessage message)
throws SOAPException, java.io.IOException, org.xml.sax.SAXException {
    FileReader soapFile = new FileReader("soapwa.xml");
    InputSource stream = new InputSource(soapFile);
    SAXSource saxSource = new SAXSource(stream);

    SOAPPart soapPart = message.getSOAPPart();
    soapPart.setContent(saxSource);
    soapPart.addMimeHeader("Content-Transfer-Encoding","8bit");
    soapPart.setContentId("[email protected]");
}

The SOAPEnvelope

The SOAPEnvelope interface represents the root of the XML SOAP document. It includes methods for accessing or creating the SOAPHeader and SOAPBody. It also includes two methods for creating Name objects. Listing F-14 shows the complete definition of the SOAPEnvelope interface.

Example F-14. The javax.xml.soap.SOAPEnvelope Interface

package javax.xml.soap;

public interface SOAPEnvelope extends SOAPElement {

    public SOAPBody getBody() throws SOAPException;
    public SOAPHeader getHeader() throws SOAPException;
    public SOAPBody addBody() throws SOAPException
    public SOAPHeader addHeader() throws SOAPException;
    public Name createName(String localName) throws SOAPException;
    public Name createName(String localName, String prefix, String uri)
    throws SOAPException;

}

SOAPEnvelope is a subtype of the SOAPElement interface, which is a subtype of the Node interface. The same is true of the other SAAJ types (SOAPBody, SOAP Header, and so on) that represent elements of the SOAP document. The SOAPElement and Node interfaces are covered in Chapter 13.

The getHeader(), getBody(), addHeader(), and addBody() Methods

I noted in Chapter 13 that a new SOAPMessage already contains the framework of an XML SOAP document including the Envelope, Body, and Header elements. The getBody() and getHeader() methods of the SOAPEnvelope interface return SAAJ objects of type SOAPBody and SOAPHeader, which represent the empty Header and Body elements.

SOAPEnvelope envelope = soap.getEnvelope();
envelope.getHeader().detachNode();
...
SOAPBody body = envelope.getBody();

The call to getHeader() is chained to a detachNode() call, which effectively removes the Header element from the SOAP document—useful when header blocks are not used in the SOAP message. Calling the detachNode() method removes the empty Header element and creates a tighter SOAP document.

You can also add a new Body or Header element using the corresponding add Body() or addHeader() method, but you use them only if you have already removed the Body or Header element from the SOAP document; if you haven't, these methods will throw a SOAPException.

The createName() Method

The SOAPEnvelope also provides two factory methods for creating Name type objects. These behave the same as the createName() method defined in the SOAPFactory class discussed in Chapter 13. As you already know from Section 2.2: XML Name spaces, the name of an element or attribute dictates which XML namespace it belongs to. A Name object is simply an abstraction of an XML qualified name. For example, in Listing F-15 SaajExample_F3 shows how the SOAPEnvelope is used to create the getBookPrice and isbn elements in the GetBookQuote SOAP message.

Example F-15. Creating and Using Name Objects

package com.jwsbook.saaj;
import javax.xml.soap.*;

public class SaajExample_F3 {
  public static void main(String [] args)
  throws SOAPException, java.io.IOException{

    MessageFactory msgFactory = MessageFactory.newInstance();
    SOAPMessage message = msgFactory.createMessage();
    SOAPPart part = message.getSOAPPart();
    SOAPEnvelope envelope = part.getEnvelope();

    Name getBookPrice_Name = envelope.createName("getBookPrice","mh",
                            "http://www.Monson-Haefel.com/jwsbook/BookQuote");
    Name isbnName = envelope.createName("isbn");

    SOAPBody body = message.getBody();
    SOAPBodyElement getBookPrice_Element =
                    body.addBodyElement(getBookPrice_Name);
    getBookPrice_Element.addChildElement( isbnName );

    SaajOutputter.writeToScreen(message);
  }
}

Wrapping Up

Creating SwA messages is fairly easy with SAAJ, provided you have the right kinds of DCHs and DataSource objects. If you don't, then you'll need to change the types of attachments you use or find or develop DCHs or DataSource objects that fulfill your needs. Developing and registering a new DCH is not very complicated; all you do is implement the javax.activation.DataContentHandler interface, then register the implementation in a mailcap file. In many cases you may not need a DCH. For example, if you obtain data objects from files, you can use the javax.activation.FileDataSource, as SaajExample_F1 and SaajExample_F2 did with the PDF file, in Listings F-7 and F-12.

Although SAAJ can be used independently of JAX-RPC, in many cases they're used together. Use of SAAJ with JAX-RPC is covered in Chapter 14: Message Handlers.



[1] Erich Gamma, et al. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995, p. 20.

[2] Borenstein and Bellcore, “RFC 1524: A User Agent Configuration Mechanism for Multimedia Mail Format Information” (1993). Available at http://www.ietf.org/rfc/rfc1524.txt.

[3] Each mailcap file must be in a different directory, because you can't store two files with the same name in a single directory.

[4] Erich Gamma, et al. Design Patterns: Elements of Reusable Object-Oriented Software. Reading, MA: Addison-Wesley, 1995, p. 139.

[5] Elliotte Rusty Harold, Processing XML with Java. Boston: Addison-Wesley, 2002.

[6] Elliotte Rusty Harold, Java I/O. Beijing: O'Reilly & Associates, 1999.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.103.219