Building Interoperable Web Services With Attachment Support Using JAXRPC 1.1.2

by Rama Pulavarthi and Vivek Pandey

Attachments feature in JAXRPC 1.1.2 discusses how attachments are  supported in JAXRPC SI in conformance with JAXRPC 1.1 specification and WS-I Basic Profile 1.1 and Attachments Profile 1.0. This article gives step-by-step instructions on building interoperable Web Services using JAXRPC 1.1.2 SI.  It assumes familiarity with using JAXRPC tools to develop Web Services. If you are new to this you may like to refer Understanding your JAX-RPC SI Environment.

With JAXRPC, building Web Services with attachments is easy. All you need to do is design a WSDL with wsdl:binding element using MIME Binding on either the input or ouput  and use wscompile tool to generate corresponding  Java artifacts. JAXRPC takes care of generating type mappings and serialization/deserialization of Java types to MIME types. We will go over this through a simple Web Service: Photo Catalog.

The PhotoCatalogService WSDL, Service implementaion, and Client implementation and configuration files are available in the AttachmentsSample.zip. Follow the instructions in Running the AttachmentsSample section for building and running the sample.

Photo Catalog Web Service has two operations.
    * Add Photo: Accepts a photo as input and adds it to the photo catalog and returns the status of the operation in plain text or xml.
    * Replace Photo: Replaces a photo in the catalog with a new photo and returns the old photo if the operation is successful.

PhotoCatalogService WSDL

The following Listing 1 shows an excerpt from the PhotoCatalogservice.wsdl.

Listing 1. PhotoCatalogService.wsdl
    <wsdl:types>
........
<xsd:import namespace="http://ws-i.org/profiles/basic/1.1/xsd" schemaLocation="WS-ISwA.xsd"/>

<!-- Status contains the references the old photo available as attachment. -->
<xsd:element name="Status" type="wsi:swaRef" />
........
</wsdl:types>


<wsdl:binding name="PhotoCatalogBinding" type="tns:PhotoCatalog">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
     <wsdl:operation name="addPhoto">
      <wsdl:input>
        <mime:multipartRelated>
          <mime:part>
            <soap:body use="literal"/>
          </mime:part>
          <mime:part>
            <mime:content part="photo" type="image/jpeg"/>
          </mime:part>
        </mime:multipartRelated>
      </wsdl:input>
      <wsdl:output>
        <mime:multipartRelated>
          <mime:part>
            <soap:body use="literal"/>
          </mime:part>
          <mime:part>
            <mime:content part="status" type="text/plain"/>
            <mime:content part="status" type="text/xml"/>

          </mime:part>
        </mime:multipartRelated>
      </wsdl:output>
    </wsdl:operation>  

    <wsdl:operation name="replacePhoto">
      <wsdl:input>
        <mime:multipartRelated>
          <mime:part>
            <soap:body parts="oldPhoto" use="literal"/>
          </mime:part>
          <mime:part>
            <mime:content part="newPhoto" type="image/jpeg"/>
          </mime:part>
        </mime:multipartRelated>
      </wsdl:input>
      <wsdl:output>
        <mime:multipartRelated>
          <mime:part>
            <soap:body parts="status" use="literal"/>
          </mime:part>
        </mime:multipartRelated>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
In the binding section of the WSDL, we specify whether a message part goes as part of the SOAP message or as a MIME attachment. Refer to WSDL 1.1 section 5.0 for more details on MIME binding.

For the operation addPhoto, the input is "photo" with MIME content type image/jpeg. The output "status" is either "text/plain" or "text/xml". This is just to show that it can take alternative MIME types.

For the operation replacePhoto, the input "oldPhoto" id is sent in the soap:body and "newPhoto" with MIME content type image/jpeg is sent as an attachment. The output "Status" is of type "wsi:swaRef",  which means it holds a  reference to an attachment in the SOAP message package. The reference to the attachment it refers to is not known until runtime.

Service Endpoint Interface

The next step is to use wscompile tool, available in jaxrpc 1.1.2 on the WSDL and it generates all the Java artifacts. All the MIME types specified in the WSDL are mapped to Java parameters. Listing 2 shows the Service Endpoint Interface generated for the PhotoCatalogServcie WSDL.

Listing 2. Service Endpoint Interface
package com.example.photo; 
public interface PhotoCatalog extends java.rmi.Remote {
public javax.activation.DataHandler addPhoto(java.awt.Image photo)
throws java.rmi.RemoteException;
public java.net.URI replacePhoto(com.example.photo.PhotoInfo oldPhoto, java.awt.Image newPhoto)
throws java.rmi.RemoteException;
}
The addPhoto method takes java.awt.Image as input and returns javax.activation.Datahandler. Notice that, the  message part "photo" defined in the wsdl:types section as xsd:hexBinary is just a hint, the mapping to Java type is decided by the MIME type. So in this case, "photo" of MIME type "image/jpeg" is mapped to java.awt.Image instead of Java Byte[] type. As per JAXRPC specification, when alternative MIME content types are specified for mime:part, it maps to javax.activation.DataHandler.

JAXRPC specification uses the JavaBeans Activation Framework to support various MIME content types. The DataHandler class provides a consistent interface to the data represented in various MIME types. A DataHandler class uses the DataContentHandler interface to convert between a stream and specific Java object based on the MIME type. JAXRPC uses SAAJ, which provides DataContentHandlers for the MIME types supported by JAXRPC. If the MIME type is not one of the JAXRPC supported MIME types, then the user has to register corresponding DataContentHandlers. Here "text/plain" and "text/xml" are both JAXRPC supported MIME types and is taken care of automatically. A DataHandler can be instantiated using the constructor DataHandler(Object obj, String mime_type). The method DataHandler.getContentType returns the MIME type of the encapsulatd data and DataHandler.getContent method retruns a Java object based on the MIME type of the encapsulated data. If you do not want the MIME types to map to coresponding Java types, you can use wscompile with -datahandleronly option to map all MIME types to DataHandler.

The replacePhoto method takes old photo information and a new photo (java.awt.Image) and returns a URI. Notice that "status" of type wsi:swaRef is mapped to java.net.URI. The application has to resolve this URI to get hold of the attachment it refers to from the SOAP message package.


Service Implementation

The following Listing 3 shows the service implementation of the Photo Catalog Web Service.

Listing 3. Service Implementation
public class PhotoCatalogImpl implements PhotoCatalog, ServiceLifecycle   { 
ServletEndpointContext servletEndpointContext = null;
ServletContext servletContext = null;    

public void init(Object context) {
servletEndpointContext = (ServletEndpointContext) context;
servletContext = servletEndpointContext.getServletContext();
}

public void destroy() {
servletEndpointContext = null;
servletContext = null;
}  

public javax.activation.DataHandler addPhoto(java.awt.Image photo)
throws java.rmi.RemoteException{
  DataHandler dh = new DataHandler(getStatusInXml(), "text/xml");             
  return dh;
}

public java.net.URI replacePhoto(PhotoInfo photoinfo,java.awt.Image newPhoto)
throws java.rmi.RemoteException{
   try{                
MessageContext mc = servletEndpointContext.getMessageContext();                
String imageName = photoinfo.getCustomerName() + photoinfo.getPhotoID()+".jpg";                
java.awt.Image oldPhoto = getImage(imageName);                
AttachmentPart att = MessageFactory.newInstance().createMessage().createAttachmentPart();                
att.setContentId("<" + imageName + ">");                
att.setContent(oldPhoto,"image/jpeg");                
ArrayList list = new ArrayList();                
list.add(att);                
mc.setProperty(ServerPropertyConstants.SET_ATTACHMENT_PROPERTY, list);                
java.net.URI retVal = new java.net.URI("cid:" + imageName);                
return retVal;
}catch (Exception e){             
e.printStackTrace();             
//  replacePhoto Operation failed, return null
}
return null;
}
}
The implementation for addPhoto method is straight forward. It accepts an Image and returns a Datahandler. JAXRPC SI takes care of sending the "status" as an attachment in the SOAP Message package.

For the method replacePhoto, it sends the oldPhoto as an attachment and returns the reference to the old photo. Note that this attachment information is not specified in the WSDL. For sending/receiving attachments not defined in the WSDL, JAXRPC SI provides a mechanism through  implementation specific server-side and client-side properties to access the collection of attachments in the Message Context. In this sample, the server implementation implements ServiceLifeCycle interface to get access to ServletEndpointContext from which it gets the MessageContext. Once the Message Context is obtained, it uses SAAJ API to construct AttachmentPart and links it to the MessageContext using "com.sun.xml.rpc.server.ServerPropertyConstants.SET_ATTACHMENT_PROPERTY" on the server side.. Similarly to access attachments not specified in the WSDL in the Server implementation, "com.sun.xml.rpc.server.ServerPropertyConstants.GET_ATTACHMENT_PROPERTY" can be used.

Also note the correlation between the Content-Id for the attachment part (oldPhoto) and return URI. The same Content-Id(imageName) is used to refer to the attachment(cid:imageName). Notice in Listing 9., the value of Status "cid:duke1.jpg" and Content-Id of the image "duke1.jpg" are related in the SOAP message. The application developer should set the swaref Java type(java.net.URI) value and resolve the attachment according to the CID URL scheme as defined by RFC 2111. The application should also make sure that a part defined as swaRef type refers to a MIME part with in the same SOAP message package.

Client Implementation


The following Listing 4 shows a snippet from client implementation for the PhotoCatalog Service.

Listing 4. Client Implementation
public class PhotoCatalogClient { 
public static AttachmentHelper helper = new AttachmentHelper();
 
   .......
public void invokeAddPhoto() throws Exception {
    java.awt.Image newPhoto = getImage("duke1.jpg");
    javax.activation.DataHandler dh = stub.addPhoto(newPhoto);
    ............
}

 public void invokeReplacePhoto()throws Exception {
     java.awt.Image newPhoto = getImage("duke2.jpg");
java.net.URI oldPhotoRef= stub.replacePhoto(new PhotoInfo("duke",1), newPhoto);
Collection c = (Collection) ((PhotoCatalog_Stub)stub)._getProperty(                       
StubPropertyConstants.GET_ATTACHMENT_PROPERTY);

if(oldPhotoRef != null) {
 AttachmentPart att = helper.getAttachment(oldPhotoRef,c.iterator());
..................  
}
}
}
Invoking the addPhoto operation sends a photo(java.awt.Image) and JAXRPC automatically handles the serialization of this into corresponding MIME type using SAAJ API. The utility class AttachmentHelper provides some helper methods for retrieving an attachment using swaRef and comparing two streams or images.

The replacePhoto operation on the stub is invoked by sending old photo information and a new photo to replace it. The return URI holds the reference to the photo sent as an attachment. To access the attachments not specified in WSDL from the SOAP Message package, the client can use the  "com.sun.xml.rpc.client.StubPropertyConstants.GET_ATTACHMENT_PROPERTY" property from the stub. Similarly one can use "com.sun.xml.rpc.client.StubPropertyConstants.SET_ATTACHMENT_PROPERTY", to send attachments not specified in WSDL on the client side. In this sample, the client uses the com.sun.xml.rpc.client.StubPropertyConstants.GET_ATTACHMENT_PROPERTY to access the collection of attachments in the SOAP message package.

The return URI (status, a swaRef type, refers to an attachment) can be used to retrieve the attachment. The following Listing 5 from Attachment Helper class shows how a URI referring to a SOAP attachment can be used to retrieve the attachment.

Listing 5. getAttachment from swaRef URI
public AttachmentPart getAttachment(java.net.URI ref, Iterator iter) {
if(iter == null || ref == null){           
System.out.println("null Iterator for AttachmentPart");           
return null;
    }
    while(iter.hasNext()) {           
AttachmentPart tempAttachment = (AttachmentPart)iter.next();   
if(ref.isOpaque()&& ref.getScheme().equals("cid")) {               
String refId = ref.getSchemeSpecificPart();               
String cId = tempAttachment.getContentId();               
if(cId.equals("<"+refId+">") || cId.equals(refId)) {                   
return tempAttachment;               
}           
}
    }
return null;
}

Running the AttachmentsSample

The sample works with Java Web Servcies Developer Pack1.4 with the Sun Java System Application Server 8.0.0_01, the Sun Java System Web Server 6.1 SP2, and Tomcat 5 for JWSDP. Install JWSDP1.4. Unzip the AttachmentsSample.zip into {jwsdp.home}/jaxrpc/samples and follow the docs/README.html for instructions on running the sample. It also includes a small utility class AttachmentHelper for some common operations while using attachments.

For Solaris/Linux:

   1. Edit build.properties and set the container specific properties (container and its root)
   2. %export JAVA_HOME=<your J2SE installation directory>
   3. %export JWSDP_HOME=<your JWSDP installation directory>
   4. %export ANT_HOME=$JWSDP_HOME/apache-ant
   5. %export PATH =$ANT_HOME/bin:$JAVA_HOME/bin:$PATH
   6. %cd $JWSDP_HOME/jaxrpc/samples/AttachmentsSample
   7. %ant deploy-war
   8. Wait for the application to finish deploying. You can browse http://localhost:8080/jaxrpc-AttachmentsSample/photocatalog to check.
   9. ant run-client


For WindowsNT/2000/XP

   1. Edit build.properties and set the container specific properties (container and its root)
   2. > set JAVA_HOME=<your J2SE installation directory>
   3. > set JWSDP_HOME=<your JWSDP installation directory>
   4. > set ANT_HOME=%JWSDP_HOME%\apache-ant
   5. > set PATH =%ANT_HOME%/bin;%JAVA_HOME%/bin;%PATH%
   6. > cd %JWSDP_HOME%\jaxrpc\samples\AttachmentsSample
   7. > ant deploy-war   
   8. Wait for the application to finish deploying. You can browse http://localhost:8080/jaxrpc-AttachmentsSample/photocatalog to check.
   9. > ant run-client

To log the SOAP messages, uncomment out.println(logSOAPMessage(context)) statement in the client-side logging handler (AttachmentsSample/src/client/photocatalog/LoggingHandler.java) and run the client. The SOAP messages are logged in logs/messages.log.

SOAP Messages

The following listings show SOAP messages generated by JAXRPC when the service is invoked.

Listing 6. addPhoto Request
Content-type: multipart/related; type="text/xml";
boundary=------=
_Part_0_12621140.1087420061562
-----=_Part_0_12621140.1087420061562
Content-Type: text/xml; charset=utf-8
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://examples.com/PhotoCatalog/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body/>
</env:Envelope>
------=_Part_0_12621140.1087420061562
Content-Type: image/jpeg
Content-Id: <photo=8332e4ae-0e86-484d-b660-0a90714df7a4@jaxrpc.sun.com>
&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz asdjlk3q4q3924.d
[adk' ......................
------=_Part_0_12621140.1087420061562--
 

Listing 7. addPhoto Response
Content-type: multipart/related; type="text/xml";
boundary=------=
_Part_2_26285048.1087420063468
------=_Part_2_26285048.1087420063468
Content-Type: text/xml; charset=utf-8

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://examples.com/PhotoCatalog/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body/>
</env:Envelope>
------=_Part_2_26285048.1087420063468
Content-Type: text/xml
Content-Id: <status=badb71b6-e5da-4c82-989b-f0d459cfba82@jaxrpc.sun.com>
<?xml version="1.0" encoding="UTF-8"?>
<order>
     <customerName>duke</customerName>
<photoID>1</photoID>
</order>

------=_Part_2_26285048.1087420063468--

Listing 8. replacePhoto Request
Content-type: multipart/related; type="text/xml";
boundary=------=
_Part_3_28616523.1087420063593
------=_Part_3_28616523.1087420063593
Content-Type: text/xml; charset=utf-8
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://examples.com/PhotoCatalog/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns0:PhotoInfo>
<customerName>duke</customerName>
<photoID>1</photoID>
</ns0:PhotoInfo>

</env:Body>
</env:Envelope>
------=_Part_3_28616523.1087420063593
Content-Type: image/jpeg
Content-Id: <newPhoto=ea6a9d2e-25f4-4f50-8657-112bafef1f00@jaxrpc.sun.com>
Y<�adfm;lsdf  e t/r235tkjm.f .sdfk-   0l23.
dasf;cvjfg      s09423............................
------=_Part_3_28616523.1087420063593--

Listing 9. replacePhoto response
Content-type: multipart/related; type="text/xml";
boundary=------=
_Part_5_25675100.1087420063921
------=_Part_5_25675100.1087420063921
Content-Type: text/xml; charset=utf-8
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns0="http://examples.com/PhotoCatalog/types"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns0:Status>cid:duke1.jpg</ns0:Status>
</env:Body>
</env:Envelope>
------=_Part_5_25675100.1087420063921
Content-Type: image/jpeg
Content-Id: <duke1.jpg>
Y<�adfm;lsdf  e t/r235tkjm.f .sdfk- 0l23.DEFGHIJSTUVWXYZcdefghijstuvwxyz
asdjlk3q4q3924.d [adk' .
.......
------=_Part_5_25675100.1087420063921--

Conclusion

In this article, We have shown how interoperable Web Services  with attachment support can be done with JAXRPC 1.1.2. JAXRPC 1.1.2 has made using attachments in Web Services a breeze. All the programmer needs to know is to design the WSDL with MIME binding. For now JAXRPC SI supports attachment support from WSDL to Java only. Attachment support from Java to WSDL will be available in JAXRPC 2.0 implementation.

About the Authors

Rama Pulavarthi is a Member of Techincal Staff with the Java Web Services group at Sun Microsystems. He currently works on JAXRPC technology. Rama can be reached at Rama.Pulavarthi@Sun.COM.

Vivek Pandey is a Staff Engineer with the Java Web Services group at Sun Microsystems. He focuses on the design and development of JAXRPC SI and represents Sun at WS-I Basic Profile Working Group. Vivek can be reached at Vivek.Pandey@Sun.COM.


References