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 Pack
1.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.