Sun AppServer-based Service

Back to Web Services Resources and Tutorials

If you are new to this topic, you should read the Overview page first. This tutorial will describe attachments which are described directly as WSDL message parts and attachments which are referenced with the WS-I "swaRef" reference. This approach will allow us to explore, with one example, the differences between the direct DataHandler approach and the reference approach. Another good introduction to the structure of SOAP messages with attachments (and the usage of SAAJ) is Chapter 9 of the J2EE 1.4 Tutorial (see Resources), "SOAP with Attachments API for Java".

Setting Up Your Environment

This tutorial was built using the latest update of J2SE 5, Ant, and the Sun Application Server. See the following:

After installing these, prepend the bin directories of each of these packages to your path (e.g., setenv.bat). If you will be using the Ant build environment supplied with this tutorial (see build.xml and build.properties), then update your build.properties file to point to the Sun Application Server home directory.

Optionally, you can download and install Apache Axis (version 1.3 used here). Axis is used in the SAAJ-based Client example because it has a nicely-documented base64 encoding/decoding class (and whose documentation is easy to find). If you won't be using the SAAJ-based client, or will use it but don't need the base64 encoding and are willing to remove it from the example, then you don't need the Axis download.

Back to Top

Developing the WSDL File

Here we will develop a simple service for uploading a personal profile (for example, for an instant messaging service). The main SOAP body will include some generic information related to the document upload and a reference to an optional photograph, which, if present, will be included as a MIME attachment:

<xsd:complexType name="PersonalProfileUpdateType">
  <xsd:sequence>
    <xsd:element name="profileID" type="xsd:string" />
    <xsd:element name="profilePhotoRef" type="bp11:swaRef" minOccurs="0" />
  </xsd:sequence>
</xsd:complexType>
The service will use the profilePhotoRef reference to locate the attachment. Additionally, the profile update message will include a part which is an XML document containing the profile itself:
<message name="ProfileUpdateMessage">
  <part name="profileUpdateRequest" element="swa-schema:personalProfileUpdate" />
  <part name="profileDetails" element="swa-schema:personalProfile" />
</message>
This part, being defined directly as a part of the update message, is identified by name as a MIME attachment in the input of the binding operation:
<input>
  <mime:multipartRelated>
    <mime:part>
      <soap:body use="literal" parts="profileUpdateRequest" />
    </mime:part>
    <mime:part>
      <mime:content part="profileDetails" type="text/xml" />
    </mime:part>
  </mime:multipartRelated>
</input>
Note that the optional photograph is not referenced in the binding, as it was not defined as a part of the input message. In other words, every part mentioned in the MIME binding has a corresponding part in the associated message. Here, the "profileUpdateRequest" part is identified as the SOAP body and the only other part of the "profileUpdateMessage", the "profileDetails" element (an XML document defined with a schema in the "types" section) is defined as a MIME attachemtn with content type "text/xml". The optional photograph, being defined only as a reference within the SOAP body, is not included as a MIME attachment in the binding. These two scenarios cover the two cases described in the Usage Scenarios that accompany the Attachments Profile, and they conform to the Profile requirement that an attachment either be defined as a part and bound to MIME content, or defined via reference and not bound to MIME content. The WSDL file used to develop this tutorial can be found here. The following paragraphs will discuss the WSDL file in more detail.

The first thing you should notice that is different in this WSDL file is an additional namespace, http://schemas.xmlsoap.org/wsdl/mime/. This is the schema used to specify the multipart/related SOAP message we'll be sending to the document upload service. Another namespace is http://ws-i.org/profiles/basic/1.1/xsd, a very simple schema which defines a URI reference type; we will use it in the SOAP body to reference the attachment document. We need to actually import this schema; it is available at its specified URL, but we copy it to a file to make it available locally to the wscompile utility.

As alluded to earlier, you can see from the types section that the binary attachment is not directly defined; however, there is a document reference to the attachment defined in the service input message's schema. The URI reference in the SOAP body will correspond to the document ID in the attachment, as we'll see later.

As mentioned earlier, the "ProfileUpdateMessage" contains two parts, one of which is identified as the SOAP body, and the other as an XML attachment, in the binding for the input operation. The optional photograph will be obtained by the service via the optional reference defined in the schema for the SOAP body of the input message.

A document generated following this WSDL document should look like the following:

------=_MIME_Boundary
Content-Type: text/xml; charset=utf-8

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP-ENV:Body>
    <swa:personalProfileUpdate xmlns:swa="http://example.com/swa/wsdl">
      <profileID>wmadams824</profileID>
      <profilePhotoRef>BigUUID:photo</profilePhotoRef>
    </swa:personalProfileUpdate>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
------=_MIME_Boundary
Content-Type: text/xml

<swa:personalProfile xmlns:swa="http://example.com/swa/wsdl">
  <realName>Wayne</realName>
</swa:personalProfile
------=_MIME_Boundary
Content-Type: text/plain
Content-Id: BigUUID:photo

iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMA
...
H3ABF0I0VWXbmVpJRgrAj7iIcyGa/Nf+zRzMs+SE3FRTTTVKPwFMB4uc7mE45AAAAABJRU5ErkJggg==
------=_MIME_Boundary--
The above message is what we'll be creating, transmitting and processing. The following sections step through the creation and deployment of the service, and invoking it via a couple of example clients.

Back to Top

Generating and Implementing the Service

As a first step, create the following directory structure under the top-level directory of this project:

 tutorialDirectory
  - descriptors
  - src
    - client
    - service
If you are going to follow the supplied Ant build file, place the WSDL file, the Sun-specific configuration file and the WS-I SwA reference schema into the "descriptors" directory (giving the SwA reference schema file the same name that you use in the WSDL file; in this case I used "wsi_bp_11.xsd"). Now, generate the tie (service-side) implementation and schema Java bindings with
wscompile -import -keep -f:documentliteral,wsi -d src/service -mapping descriptors/jaxrpc-mapping.xml descriptors/config.xml
(or use the "wscompile-tie" target in the sample build file). Using the example configuration file supplied here, the classes will be generated in the "com.example.swa" package. Your next step will be to supply the details of the implementation "updateProfile" class.

Before you flesh out the service implementation, it is interesting to note a couple of things. First, the signature of the "updateProfile", as expected, looks like:

public com.example.swa.UpdateConfirmationType 
  updateProfile(com.example.swa.PersonalProfileUpdateType profileUpdateRequest, 
                javax.xml.transform.Source profileDetails)
  throws java.rmi.RemoteException 
{
  com.example.swa.UpdateConfirmationType _retVal = null;
  return _retVal;
}
where the SOAP body is associated with a generated Java class and the MIME attachment is associated with an XML transform Source. Note that no Java class was generated for the "personalProfile" schema element, as it is not listed as a "part" of any WSDL message. While it is possible to generate a corresponding class (with JAXB, for example) on the service side, clients of the web service will either have to perform a similar class-generation action or generate and manipulate this document as raw XML. SOAP Attachment parts may be either XML or non-XML data; in practice, if you have a schema-defined fragment to pass to a web service, it places less burden on your consumers to include it as a part of the SOAP body, because your web service compiler will generate a corresponding Java representation. In this tutorial, however, we are using a simple XML document simply to illustrate the process of describing WSDL message parts as attachments; it could just as easily have been a binary file like a JPEG image (in which case you would have been supplied a DataHandler as an argument to the method call).

Also note that there is no reference to the optional photograph, other than a java.net.URI property in the generated class for the "personalProfileUpdate" element:

protected java.net.URI profilePhotoRef;
This URI, as expected, will be used to identify the attachment corresponding to the profile photograph.

Using the procedure described in the Overview, modify the service implementation class to implement javax.xml.rpc.server.ServiceLifecycle. ServiceLifecycle specifies two methods: init(Object), which initializes an Object which in the case of a servlet endpoint is a javax.xml.rpc.server.ServletEndpointContext, and destroy(). The javax.xml.rpc.handler.MessageContext can then be obtained from the ServletEndpointContext. Once you have a reference to the MessageContext, you can retrieve and set attachments via the Sun-specific constants

com.sun.xml.rpc.server.ServerPropertyConstants.GET_ATTACHMENT_PROPERTY
com.sun.xml.rpc.server.ServerPropertyConstants.SET_ATTACHMENT_PROPERTY
which expect a java.util.List as the type of the property.

Since we are just verifying the ability to transmit SOAP messages with attachments, we don't actually update anything when the profile update request comes in, but we do verify that we can extract the attachments (and, in the case of the text/xml attachment, parse it), verify the integrity of the optional attached photograph, and return a confirmation to the consumer. An example implementation class can be found here.

In the example, retrieval of the SOAP body is achieved through the Java class binding to the SOAP body document schema. So the ID of the person requesting the profile update is found from

profileUpdateRequest.getProfileID()
and the swaRef to the optional attached photo is found from
profileUpdateRequest.getProfilePhotoRef()

Retrieving the message-bound attachment 'profileDetails' is only slightly more complex, since it is supplied in the 'updateProfile()' method as a parameter (in the form of a javax.xml.transform.Source object). You can obtain an InputStream on the Source and append the byte stream to a StringBuffer, then output the contents as a String, which you could either process further or store in an XML database, for example. Example code to perform this follows:

if (profileDetails instanceof StreamSource)
{
  StreamSource ss = (StreamSource)profileDetails;
  StringBuffer sb = new StringBuffer();
  if (ss != null)
  {
    InputStream inStream = ss.getInputStream();
    if (inStream != null)
    {
      int cnt = 0;
      byte buffer[] = new byte[1024];
      try
      {
        while ((cnt = inStream.read(buffer)) > 0)
        {
          sb.append(new String(buffer, 0, cnt));
        }
      }
      catch (IOException ioe)
      {
        throw new RemoteException(ioe.getMessage());
      }
    }
    else
    {
      throw new RemoteException("InputStream of 'profileDetails' attachment is null");
    }
  }
  else
  {
    throw new RemoteException("StreamSource of 'profileDetails' attachment is null");
  }
  System.out.println("'profileDetails' document:\n" + sb);
  // having got this, you can process it further in a real application; e.g.,
  // store it to an XML database...
}

The final, and most complex, part of processing the incoming document is locating, retrieving and processing the optional attached photograph. We get the name of the attachment from the 'profileUpdateRequest', then follow the procedure outlined in the Overview for obtaining an ArrayList of the attachments of the message, as follows:

MessageContext messageContext = seContext.getMessageContext();
ArrayList<AttachmentPart> attachments = (ArrayList<AttachmentPart>)messageContext.getProperty(ServerPropertyConstants.GET_ATTACHMENT_PROPERTY);
For our example web service, once we find the correct attachment, we need to determine whether or not it was base64 encoded before transmission; we get this information from the MIME header named "Content-Transfer-Encoding" from the AttachmentPart. Once we've determined what type of transfer encoding was used, we can use the following code to base64 decode the file and write it to disk:
base64Data = (String)attachPart.getContent();
FileOutputStream fos = new FileOutputStream(filename);
Base64.decode(base64Data, fos);
fos.close();
or use the following for simple binary encoding:
DataHandler dh = new DataHandler(attachPart.getContent(), attachPart.getContentType());
FileOutputStream fos = new FileOutputStream(filename);
dh.writeTo(fos);
fos.close();

After processing the attachments, the service creates an update confirmation code (in a real application, it would of course update a profile first) and returns the UpdateConfirmationType object to the requestor.

Back to Top

Packaging and Deploying the Service

Once you've completed fleshing out the service implementation, compile the sources to a directory suitable for packaging a J2EE webapp. This directory should look like the following:

  webappDirectory
  - WEB-INF
    - classes
    - lib
    - wsdl
The WSDL file should go in the wsdl directory of the webapp, as should the WS-I Basic Profile 1.1 XML schema file. The compiled classes should go under the classes directory. Furthermore, in the WEB-INF directory you should have the following files: appropriate to this application. Examples of the first two files can be found at web.xml and webservices.xml. The jaxrpc-mapping.xml file was generated by wscompile; if you used the supplied Ant build file, you generated this file in the descriptors directory. If you use the supplied Ant build file, the target to compile the source files to the webapp classes directory is just "compile-service".

Since this example service uses the Axis Base64 class, you will need to place the axis.jar class into the lib directory. If you do not use this class in your service, then the lib directory is not needed at all.

Finally, package the application as a .war file. This simply involves grabbing everything under your webapp directory and jar'ing it into a .war file. The supplied Ant build file has a target called "build-service" which does this, and a target called "deploy-sun" which copies the .war file to the "autodeploy" directory of the standard "domain1" domain on your local Sun AppServer directory structure.

To test the webservice, go back to the top-level page of this site and select one of the client tracks under "SOAP with Attachments".

Back to Top
Back to Web Services Resources and Tutorials