How to pass SOAP Attachments with JAX-RPC Web Service
Date: 30-Sept-2003
After reading this how-to document you should be able
to:
- Understand what is a JAX-RPC Web Service
- Write a JAX-RPC Service that receives and sends SOAP attachments.
Introduction
This document demonstrates how to send SOAP attachments with Document literal JAX-RPC Web Service.
Overview of JAX-RPC Web Services
The Java API for XML-based remote procedure calls (JAX-RPC)
provides standard way of building portable and interoperable SOAP based
Web Services. It simplifies the process of building Web services that
incorporate XML-based RPC. In JAX-RPC, a remote procedure call is represented
by an XML-based protocol such as SOAP. The SOAP specification defines
envelope structure, encoding rules, and a convention for representing
remote procedure calls and responses. These calls and responses are transmitted
as SOAP messages over HTTP. Read more on JAX-RPC Web Services here.
The SOAP Attachment
A SOAP message package with attachments is constructed
using the MIME multipart/related type. The primary SOAP 1.1 message is
carried in the root body part of the multipart/related structure. The
primary SOAP 1.1 message may reference additional entities (termed as
attachment or MIME parts) in the message package. These entities may contain
data in formats other than XML.
Thus the root part of message contains the original
message(in the SOAP body) and any attachment goes as referenced MIME parts.
The referenced MIME parts must contain either a Content-ID MIME header
or a Content-Location MIME header to uniquely identify it. Following is
a sample SOAP message with attachment.
MIME-Version: 1.0
Content-Type: Multipart/Related; boundary=MIME_boundary; type=text/xml; start="<http://img5422a.xml.oracle.com>" --MIME_boundary--
Content-Type: text/xml; charset=UTF-8 Content-Transfer-Encoding: 8bit Content-ID: <img5422a.xml.img.oracle.com> <?xml version='1.0' ?> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <SOAP-ENV:Body> .. <theSignedForm href="cid:img5422a.xml.oracle.com"/> .. </SOAP-ENV:Body> </SOAP-ENV:Envelope> --MIME_boundary--
Content-Type: image/tiff Content-Transfer-Encoding: binary Content-ID: <img5422a.xml.oracle.com>
--MIME_boundary--
The attached entity is referenced from the SOAP body
as a resource referenced by a URI given as the value of an href attribute.
While processing the message, all the URI references are converted to
absolute references and then absolute references are resolved.
The JAX-RPC runtime maps the MIME types to Java types
as follows:
image gif/jpeg type ==> java.awt.image
text/plain type ==> java.lang.String
text/xml or application/xml ==>javax.xml.transform.Source
Java class javax.activation.DataHandler
can be used for content with any MIME type.
Section 7 of the JAX-RPC
specification gives more details on SOAP attachments with JAX-RPC.
Software Requirements
-
Oracle Application Server Containers for J2EE
10g or later. You can download
the OC4J from Oracle Technology Network.
-
JDK1.4.x or above This can be downloaded
here .
Apache Ant v1.5 or later.
- How To Example Source code zip
Description
The SOAP Attachment
The JAX-RPC services provide Handler mechanism to modify
SOAP request and response messages at client and service side. This handler
class mechanism can be effectively used to send and receive SOAP attachment
from a JAX-RPC Web Service.
The handler class provides access to SOAP message that
is being transferred between the client and the service. javax.xml.soap.SOAPMessage
class can be used inside the handler to manipulate the SOAP message. A
SOAPMessage object consists of a SOAPPart object that contains the actual
SOAP XML document (or the envelope) sent in SOAP request. The SOAPEnvelope
object can be used to access SOAP body and header.
The javax.xml.soap.AttachmentPart
object represents each attachment in the SOAP message. A SOAPMessage object
may contain zero, one, or many AttachmentPart objects. The SOAPMessage
class provides various methods to manipulate attachments. Some important
methods are as follows:
createAttachmentPart(): create an empty
AttachmentPart object .
createAttachmentPart(javax.activation.DataHandler
datahandler): Creates an AttachmentPart object and populates
it using the given DataHandler object.
-
getAttachments(): Retrieves all the
AttachmentPart objects in the SOAPMessage object.
addAttachmentPart(AttachmentPart attachmentpart)
: Adds the given AttachmentPart object to this SOAPMessage object.
countAttachments() : Gets a count of
the number of attachments in this message.
The How-To Example
The example with this how to explains how to send and
receive attachments with JAX-RPC Web Services.
In the example we start with a WSDL and write the implementation
classes after the WSDL has been processed and the necessary service classes
and interfaces are generated. Once the generation process is complete
the Web Service implementation class can be compiled and included into
the Web Service archive (EAR file) to be deployed to OC4J
The client invokes a simple method on Web Service.
The SOAP request is intercepted and modified by the client handler class
to attach the image file to SOAP message being sent to the Web Service.
The Web Service has a handler class tied to it that intercepts the request,
reads the attachment sent by the client and sends the SOAP request to
the Web Service for further processing. It also intercepts the response
from the Web Service and attaches a plain text message to the response.
This response message is intercepted by the client handler to read the
text attachment sent by the Web Service and then the SOAP response message
is received by the client.
The how to uses Oracle's Web Services Assembler tool
to generate the service artifacts. The Web Service assembler tool takes
as input, an XML config file that describes the service. It uses the definitions
in the WSDL file to generate service artifacts like types, interfaces,
tie and stub classes, compiles all the classes and produces an Enterprise
Archive file that can be deployed to an application server to run the
Web Service.
Listing 1 shows the WSDL file that will be used to
the generate service artifacts.
<?xml version="1.0" encoding="UTF-8"?>
<definitions name="Attachments" targetNamespace="urn:Foo"
xmlns:tns="urn:Foo" xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
<types/> <message name="Attachments_sayHello"> <part name="String_1" type="xsd:string"/>
</message> <message name="Attachments_sayHelloResponse"> <part name="result" type="xsd:string"/>
</message>
<portType name="AttachmentsIF"> <operation name="sayHello" parameterOrder="String_1"> <input message="tns:Attachments_sayHello"/> <output message="tns:Attachments_sayHelloResponse"/></operation>
</portType>
<binding name="AttachmentsBinding" type="tns:AttachmentsIF"> <operation name="sayHello"> <input> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
use="encoded" namespace="urn:Foo"/>
</input> <output> <soap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
use="encoded" namespace="urn:Foo"/>
</output>
<soap:operation soapAction=""/></operation> <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="rpc"/>
</binding>
<service name="Attachments"> <port name="AttachmentsIFPort" binding="tns:AttachmentsBinding"> <soap:address location="REPLACE_WITH_ACTUAL_URL"/> </port> </service>
</definitions>
|
|
Listing 1
Listing 2 shows the definition of ClientAttachmentHandler.java
class. The handler class implements the javax.xml.rpc.Handler
interface. The handleRequest method
attaches an image file to the SOAP request and the handleResponse
method retrieves the text attachment received in the Web Service response.
View complete code here.
public class ClientAttachmentHandler implements Handler {
..
..
/* * This method is implementation of handleRequest method in Handler * interface. It extracts the SOAP message from the request and attaches * image to it. */ Public boolean handleRequest(MessageContext messageContext) { //-- If there was an image URL specified,
// create an attachment and add it to the message if (imageURL != null) { try { //get SOAPMessageContext SOAPMessageContext soapContext = (SOAPMessageContext)messageContext;
// get SOAP message SOAPMessage msg = soapContext.getMessage();
// create an attachment using DataHandler class AttachmentPart att = msg.createAttachmentPart(
new DataHandler(new URL(imageURL)));
// add attachment to message msg.addAttachmentPart(att); } catch (MalformedURLException e) { System.out.println("Bad URL: " + imageURL); } }
else { System.out.println("No attachment URL specified, not"+
"attaching image.");
}
return true; }
/* * This method is implementation of handleResponse method in Handler * interface. It intercepts the SOAP response received from the Web Service * and displays the contents of text file attached with the message. */ Public Boolean handleResponse(MessageContext messageContext) {
//get SOAPMessageContext SOAPMessageContext soapContext = (SOAPMessageContext)messageContext;
// get SOAP message SOAPMessage MSG = soapContext.getMessage();
//-- Get and display the first attachment which should
// be a plain text document
// get iterator of attachments Iterator attIter = msg.getAttachments();
if (attIter.hasNext()) { try { // get next attachment AttachmentPart att = (AttachmentPart)attIter.next(); // get the content of attachment String textDoc = (String)att.getContent(); System.out.println("Response Attachment:"); System.out.println("Received attachment of type "+att.getContentType()+
" from Web Service");
System.out.println("Attachment Content: "+textDoc); } catch (SOAPException se) { System.out.println("Error getting attachment:"); se.printStackTrace(); } }
else { System.out.println("No Attachment found in the response."); }
Return true; }
}
|
|
Listing 2
The client handler class is configured in stubs-config.xml
file that is used by WSA tool to generate Stub classes to access Web Service.
Listing 3 shows the part of stub-config.xml
file that defines the client handler class and passes image file name
as parameter.
...
...
<wsdl-port> <port-name>AttachmentsIFPort</port-name> <handler> <handler-name>ServiceAttachmentHandler</handler-name> <handler-class>oracle.demo.attachments.ClientAttachmentHandler</handler-class>
<init-param> <param-name>ImageURL</param-name> <param-value>file:data/JohnDoe.jpg</param-value> </init-param> </handler> </wsdl-port>
...
...
|
|
Listing 3
Listing 4shows the definition of ServiceAttachmentHandler.java
class. The handler class implements javax.xml.rpc.handler.Handler
interface. The handleRequest method retrieves
the image attachment from the SOAP request coming from the client and
the handleResponse method attaches a text
message to the Web Service response before sending it to the client. View
complete code here.
The Web Service handler class is specified in the service-config.xml
file that is used by the WSA tool to generate service artifacts.
Public class ServiceAttachmentHandler implements Handler {
..
..
/* * This method is implementation of handleRequest method in Handler * interface. It extracts the SOAP message and checks if the attachment * is received correctly. */ Public Boolean handleRequest(MessageContext messageContext){
//get SOAPMessageContext SOAPMessageContext smc = (SOAPMessageContext)messageContext;
// get SOAP message SOAPMessage MSG = smc.getMessage();
// get iterator of attachments Iterator attIter = msg.getAttachments();
// iterate through attachments if (attIter.hasNext()) { while (attIter.hasNext()) { // get the next attachment AttachmentPart att = (AttachmentPart)attIter.next();
// check if attachment is received if (document != null) document += "\n"; document = "The Web Service received attachment of "+
"type: " + att.getContentType(); System.out.println(document); }
}
else { document = "Did not receive any attachments."; }
Return true; }
/* * This method is implementation of handleResponse method in Handler * interface. It intercepts the SOAP response received from the Web Service * an */ public Boolean handleResponse(MessageContext messageContext) { //get SOAPMessageContext SOAPMessageContext smc = (SOAPMessageContext)messageContext; // get SOAP message SOAPMessage MSG = smc.getMessage(); // create an attachment of plain text type AttachmentPart att = msg.createAttachmentPart(document,"text/plain"); // attach it to the message msg.addAttachmentPart(att);
return true; }
}
|
|
Listing 4
Preparing and Running the Example
Extract the soapattachment.zip file. This
will create soapattachment directory containing
documentation and source code of the how-to.
In the following steps <OC4J> refers
to directory where OC4J is installed.
| Step a: |
Start OC4J from <OC4J>/j2ee/home
directory on a command prompt as follows:
java -jar oc4j.jar
|
| Step b: |
Navigate to soapattachment, the sample
home directory on new command window . Include ANT_HOME/bin
in the PATH environment variable. Set the following properties required
by the ant build script
-
Set the ORACLE_HOME environment
variable to your <OC4J> root
directory.
Example:
Windows : set ORACLE_HOME=c:\oc4j10g
Unix : ORACLE_HOME=/home/oc4j10g
export ORACLE_HOME
-
Set the J2EE_HOME environment
variable to your <OC4J>/j2ee/home
directory.
Example:
Windows : set J2EE_HOME=c:\oc4j10g\j2ee\home
UNIX : J2EE_HOME=/home/oc4j10g/j2ee/home
export J2EE_HOME
-
Set the JAVA_HOME environment
variable to your Java installation directory.
Example:
Windows : set JAVA_HOME=c:\j2sdk1.4
UNIX : JAVA_HOME=/home/j2sdk1.4
export JAVA_HOME
|
| Quick
Start |
|
Once you have the server configured and running, execute the following
command from the prompt in Step b:
>ant
The service artifacts and implementation class will be compiled
and placed into a WAR which is then placed into an EAR.
The EAR will be deployed to OC4J. Next the stubs and client will
be compiled. Finally the service will be invoked by the client and
you should see the output on the stdout of the client.
|
| Step
by Step |
| Step 1: |
Generate the service artifacts. Execute the following command:
This step will generate the service artifacts which include interfaces
and Tie classes. Examine the service-config.xml
file. Notice that the implementation-class-name element is filled
in with the implementation class provided by the demo. This class
is not compiled yet so WSA will generate the artifacts and use the
class name as a place holder. Once the artifacts are generated,
the implementation class will have to be compiled and WSA will have
to be run again with the same config file.
To generate the service artifacts type: ant
gen-service
The source code for the service-artifacts will be placed in the
soapattachment/build/src/service directory
using the package name oracle.demo.attachments.service.
Examine the soapattachment/config/type-mapping.xml
file. This file is used to instruct WSA how to map types to package
name. A namespace URI is mapped to a specific package name. Notice
that this example maps its types to the package oracle.demo.attachments.types.
So for service artifacts the types are generated in soapattachment/src/oracle/demo/attachments/types.
|
| Step 2: |
Generate the stubs (client artifacts)
This step will generate the stubs for the service. A client application
uses a stub to invoke operations on a remote service. Examine the
file soapattachment/config/client-config.xml.
This is the configuration file that the WebServices Assembler (WSA)
tool uses to generate the stubs.
To generate the stubs type: ant gen-stubs
The source for the stubs will be placed in soapattachment/build/src/client.
|
| Step 3: |
Update the implementation.
This step will compile the implementation classes and regenerate
the EAR file that will now contain both service artifacts and the
implementation class. Examine the file AttachmentsImpl.java
under soapattachment/src/service/oracle/demo/attachments/service.
Notice that the class implements AttachmentsIF
which is an interface to the Web Service. Before the first step
there were no generated files so compiling this class would have
failed. Now that you have already ran the gen-service task you are
ready to compile the class and update the EAR.
To update the implementation type: ant update-impl
|
| Step 4: |
Deploy the Service
The service is now ready to be deployed. Examine the attachments.ear
in the soapattachment/dist directory
using WinZip or any zip file browser. It should contain a WAR file
named attachments-web.war. This WAR
file contains all the service artifacts, implementation classes,
as well as the web deployment descriptor (web.xml)
.
To deploy this ear to a running instance of oc4j type: ant
deploy-demo
After this task is complete you can check your Web Service by
typing in the following URL into a Web browser: http://localhost:8888/attachments/attachments
You should be able to invoke some of the methods on this service
from the Web browser. Currently, invoking services using the browser
is restricted to simple types.
|
| Step 5: |
Run the demo.
You are now ready to run the client. Examine the file
AttachmentsClient.java in soapattachment/src/client/oracle/demo/attachments
directory. Notice that this class uses the stubs to set an endpoint
address and to invoke methods on the remote service.
To run the client type: ant run-demo
You should see some sample output on client and server output
screens.
|
To undeploy the Web Service execute the command :
ant teardown-demo
Resources
Summary
This how-to document explained how write a JAX-RPC
Web Service that sends and receives SOAP messages with attachments.
|