Table of Contents
This chapter describes the SAAJ programming model and the message handler that uses JAX-RPC and SAAJ together.
Developers can produce and consume SOAP messages by using SAAJ (SOAP with Attachments API for Java). This section briefly describes the concept behind the SAAJ programming model, but it does not discuss the details of the API. For more information, refer to the SAAJ specification.
SAAJ can handle complex SOAP messages with MIME attachments as well as simple SOAP messages, like simple XML documents, without attachments. SAAJ is based on the Abstract Factory pattern, and it creates a SOAP message from the MessageFactory. A SOAPMessage includes the SOAPPart that represents the SOAP document and zero or more AttachmentPart(s) that represent MIME attachments.
In order to create an empty SOAP message, SOAPMessage object needs to be instantiated by using the MessageFactory object.
The following is an example of creating a SOAPMessage object.
MessageFactory msgFactory = MessageFactory.newInstance(); SOAPMessage message = msgFactory.createMessage();
SAAJ provides many interfaces for creating and handling SOAP messages.
A SOAP message is an XML instance that consists of elements and attributes. For convenience, the main parts of a SOAP message are typed corresponding to the SAAJ. Envelope, Header, and Body correspond to the SOAPEnvelope, SOAPHeader, and SOAPBody, respectively. The SOAPElement type corresponds to the application-specific element that is not included in the SOAP namespace.
The SOAPPart and SOAPEnvelope are frequently used when working with SwA (SOAP with Attachment) messages. The SOAPPart is the root level of the MIME part in a SwA message, and it can be accessed by using the SOAPMessage.getSOAPPart() method.
The SOAPEnvelope is accessed through the getEnvelope() method of the SOAPPart. The SOAPEnvelope is the root level of an XML SOAP message, and it includes methods for accessing the SOAPHeader and SOAPBody.
The SOAPElement type is used to directly represent an application-specific element that is not expressed in a namespace for SOAP 1.1 or 1.2 XML. This type can represent an XML element.
The SOAPElement can include other SOAPElement objects as an XML element can contain other XML elements. The type is the super type of other SOAP types (SOAPEnvelope, SOAPBody, SOAPBodyElement, SOAPHeader, SOAPHeaderElement, and fault elements).
The header element of a SOAP message can have zero or more header blocks.
The SOAPHeader type represents the header element and the SOAPHeaderElement type represents each header block. The SOAPHeader type provides methods for adding, deleting, and checking a SOAPHeaderElement object.
The SOAPHeaderElement is abstracted from a header block to modify and check an attribute of a particular header block and its child elements. The SOAPHeaderElement type may contain one or more SOAPElement objects, as well as attributes like 'actor' and 'mustUnderstand'.
SOAPBody Type and SOAPBodyElement Type
The SOAPBody type represents the body element of a SOAP message. The SOAPBodyElement extends the SOAPElement, and does not have additional methods except those that are inherited.
The following shows an example of using of the SOAPBody and SOAPBodyElement types.
Name echo_Name = soapFactory.createName(“echo”, “tmax”, “http://www.tmax.com/saaj/test”); SOAPBody body = message.getSOAPBody(); SOAPBodyElement echo_Element = body.addBodyElement(echo_Name);
Once a SOAP message has been created through SAAJ, it is ready to be sent.
SAAJ has its own simple way of sending and receiving SOAP messages, which is available as a part of the API. SAAJ enables exchanging request/response style SOAP messages through a web service.
The following is an example of instantiating a SOAPConnection object used for sending a message.
//Build a SOAPMessage from a file
MessageFactory msgFactory = MessageFactory.newInstance();
MimeHeaders mimeHeaders = new MimeHeaders();
MimeHeaders.addHeader(“Content-Type”, “text/xml; charset=UTF-8”);
FileInputStream file = new FileInputStream(“soap.xml”);
SOAPMessage requestMsg = msgFactory.createMessage(mimeHeaders,file);
file.close();
String address = “…”;
//Send the SOAP message to the BookQuote Web Service
SOAPConnectionFactory conFactory = SOAPConnectionFactory.newInstance();
URL url = new URL(address);
SOAPMessage replyMsg = connection.call(requestMsg, url);
A SOAP message handler processes SOAP messages that are exchanged by a JAX-RPC client and web service endpoint, and it can be used with a statically created stub, a dynamically created proxy, a DII (Dynamic Invocation Interface), a Java class endpoint, or an EJB endpoint.
The most important role of a message handler is to provide a mechanism for adding, reading, and handling a header block of a SOAP message, which is sent and received by the JAX-RPC client and web service endpoint.
The SOAP message handlers are controlled by the Java EE container. To transmit a SOAP message by using JAX-RPC client API, the SOAP message is processed through the message handler chain before the JAX-RPC runtime is sent out to the network connected with the web service. When the response is received by a JAX-RPC client, it is processed through the same message handler chain before the result returns to the client application program.
The handler is used for many different purposes. For example, a SOAP message handler can be used is to encrypt SOAP messages for more secure communication. A client can use a SOAP message handler that encrypts a SOAP message before it is sent out. Then the target web service can use a SOAP message handler to decrypt the received message, and forward it to the appropriate back-end. The response is processed in the reverse order.
Another example of using a handler is to access information from the header part of the SOAP message. The SOAP header can be used to store web service specific information, which can be manipulated by using a handler.
A SOAP message handler provides a mechanism for intercepting the SOAP message during both the request and response processing of the web service. You can create handlers from both the web service endpoint and the client applications that invoke the web service.
The following table lists the major classes and interfaces of the javax.xml.rpc.handler API.
The following are the high-level steps for adding SOAP message handlers and handler chains to update the web service.
Design the handlers and handler chains.
Create a Java class to implement the javax.xml.rpc.handler.Handler interface, and implement the handler chain.
Compile Java code.
Configure the web services deployment descriptor file (webservices.xml).
Package and deploy the web service.
A Web service client can also use SOAP message handlers that is described in the last part of this chapter.
The following information is required when designing SOAP message handlers and handler chains.
The number of handlers needed
The sequence of executions
Whether the web service includes message handlers or back-end components.
Handlers must be set in the webservices.xml file, and a handler chain is a group of handlers whose execution order is fixed. Each handler in a handler chain has one method for handling the SOAP request and another for handling the SOAP response.
When invoking a web service, a JEUS web service executes the handlers by using the following mechanism.
The handleRequest() method of the handlers in the handler chain are all executed in the order specified in the webservices.xml file.
When the handleRequest() method of the last handler in the handler chain executes, the JEUS web service invokes the back-end component that implements the web service (if the backend exists).
When the back-end component has finished executing, the handlers in the handler chain are called in the reverse order, and the handleResponse() method of each handler is invoked.
When the handleResponse() method of the first handler specified in the webservices.xml file executes, JEUS server returns the SOAP message response to the client application that invoked the web service.
A SOAP message handler class can be implemented by directly implementing the javax.xml.rpc.handler.Handler interface, or inheriting the javax.xml.rpc.handler.GenericHandler that implements the javax.rpc.xml.handler.Handler interface.
The message handler class must implement the following methods of the Handler interface.
Various SOAP message handler information and the handler processing order is defined in webservices.xml. The following is an example of configuring the message handler information.
[Example 23.1] Message Handler Settings in <port-component>
<port-component> <port-component-name>FileAttPort</port-component-name> . . . <handler> <handler-name>ServerAttachmentHandler</handler-name> <handler-class> filetransfer.ServerAttachmentHandler </handler-class> <init-param> <param-name>directory</param-name> <param-value>/temp</param-value> </init-param> </handler> </port-component>
It is also possible to use a SOAP message handler from the web services client.
Using SOAP message handler from the client is identical to a web service using a SOAP message handler. First, create a Java class that implements the javax.xml.rpc.handler.Handler interface.
The tasks after implementing a client handler is different from that of implementing a server handler. The difference is that the client handler is directly registered within the program by using the javax.xml.rpc.handler.HandlerInfo and javax.xml.rpc.handler.HandlerRegistry classes, instead of being defined in the DD. Refer to the example in the next section.
In this example, a web service client sends a file named “File_Send.txt” to the web service server. The web service then saves the file in a specified folder and sends a confirmation message to the client that the file has been received successfully.
A client is created in the following steps.
Implement a message handler that receives a file.
Implement a web service back-end that receives a file and sends a confirmation message.
Configure the web service deployment descriptor, and instantiate and deploy the web service.
Implement a client-side handler.
Implement a web service client.
Execute the client.
The following is the implementation of a web service message handler that is executed before the handleRequest() method sends the SOAP message to the back-end. The handleRequest() method uses the SAAJ API to handle the file attached to the SOAP message.
[Example 23.2] << ServerAttachmentHandler.java >>
package filetransfer;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.rpc.handler.GenericHandler;
public final class ServerAttachmentHandler
extends GenericHandler
{
private File dir;
private final String DIR_PROP="directory";
public void init(HandlerInfo info) {
super.init(info);
Map m = info.getHandlerConfig();
String dirName = (String) m.get(DIR_PROP);
if (dirName == null) {
throw new JAXRPCException("Property named: "
+ DIR_PROP + " was not found");
}
dir = new File(dirName);
if (! dir.exists()) {
if (! dir.mkdirs()) {
throw new JAXRPCException("Unable to create directory: " + dirName);
}
}
if (! dir.canWrite()) {
throw new JAXRPCException(
"Don't have write permission for " + dirName);
}
}
private String getFileName(SOAPMessage request)
throws SOAPException
{
SOAPBody body =
request.getSOAPPart().getEnvelope().getBody();
Object obj = body.getChildElements().next();
SOAPElement opElem = (SOAPElement) obj;
SOAPElement paramElem = (SOAPElement)opElem.getChildElements().next();
return paramElem.getValue();
}
private void copyFile(InputStream is, OutputStream os)
throws IOException
{
byte [] b = new byte[8192];
int nr;
while ((nr = is.read(b)) != -1) {
os.write(b, 0, nr);
}
}
public boolean handleRequest(MessageContext mc) {
SOAPMessageContext ctx = (SOAPMessageContext) mc;
SOAPMessage request = ctx.getMessage();
if (request.countAttachments() == 0) {
throw new JAXRPCException("** Expected attachments");
}
try {
Iterator it = request.getAttachments();
while(it.hasNext()) {
AttachmentPart part = (AttachmentPart) it.next();
String fileName = getFileName(request);
System.out.println("Received file named: " + fileName);
File outFile = new File(dir, fileName);
OutputStream os = null;
InputStream is = null;
try {
os = new FileOutputStream(outFile);
is = part.getDataHandler().getInputStream();
copyFile(is, os);
} catch (IOException ioe) {
ioe.printStackTrace();
throw new JAXRPCException("Exception writing file " + fileName,
ioe);
} finally {
try {
if (is != null) is.close();
} catch (IOException ignore) {}
try {
if (os != null) os.close();
} catch (IOException ignore) {}
}
}
} catch (SOAPException e) {
e.printStackTrace();
throw new JAXRPCException(e);
}
return true;
}
public QName[] getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
The following is a web service back-end implementation class that is executed when the message handler receives a file. It sends out a confirmation message to the client.
[Example 23.3] << FileTransfer.java >>
package filetransfer; public class FileTransfer implements FileTransferIF{ public String receiveFile(String s) { return "Received file named: " + s; } }
The following is an example of implementing an SEI(Service
Endpoint Interface).
[Example 23.4] << FileTransferIF.java >>
package filetransfer; import java.rmi.Remote; import java.rmi.RemoteException; public interface FileTransferIF extends Remote { public String receiveFile(String s) throws RemoteException; }
The following is an example of the web service DD File (webservices.xml).
[Example 23.5] << webservices.xml >>
<?xml version="1.0"?>
<webservices version="1.1" xmlns="http://java.sun.com/xml/ns/j2ee">
<webservice-description>
<webservice-description-name>
FileAttachmentService
</webservice-description-name>
<wsdl-file>
WEB-INF/wsdl/FileAttachmentService.wsdl
</wsdl-file>
<jaxrpc-mapping-file>
WEB-INF/FileAttachmentService-mapping.xml
</jaxrpc-mapping-file>
<port-component>
<port-component-name>FileAttPort</port-component-name>
<wsdl-port xmlns:ns2="urn:FileAttachmentService">
ns2:FileTransferIFPort
</wsdl-port>
<service-endpoint-interface>
filetransfer.FileTransferIF
</service-endpoint-interface>
<service-impl-bean>
<servlet-link>FileAttachmentServlet</servlet-link>
</service-impl-bean>
<handler>
<handler-name>ServerAttachmentHandler</handler-name>
<handler-class>
filetransfer.ServerAttachmentHandler
</handler-class>
<init-param>
<param-name>directory</param-name>
<param-value>/temp</param-value>
</init-param>
</handler>
</port-component>
</webservice-description>
</webservices>
After configuring the webservices.xml file, configure the
web.xml and jeus-webservices-dd.xml files as in the following
example.
[Example 23.6] << web.xml >>
<?xml version="1.0"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"> <servlet> <servlet-name>FileAttachmentServlet</servlet-name> <servlet-class>filetransfer.FileTransfer</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>FileAttachmentServlet</servlet-name> <url-pattern>/FileAttachmentService</url-pattern> </servlet-mapping> </web-app>
[Example 23.7] << jeus-webservices-dd.xml >>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <jeus-webservices-dd xmlns="http://www.tmaxsoft.com/xml/ns/jeus"> <service> <webservice-description-name> FileAttachmentService </webservice-description-name> <port> <port-component-name>FileAttPort</port-component-name> </port> </service> </jeus-webservices-dd>
After all deployment descriptors are configured, package them into an EAR file and deploy it to JEUS server.
The following is an example of a web service address.
http://localhost:8088/FileAttachmentService/FileAttachmentService
The client handler processes the message context with the handleRequest() method, and adds attachments to a SOAP message by using SAAJ.
[Example 23.8] << ClientAttachmentHandler.java >>
package filetransfer;
import javax.activation.DataHandler;
import javax.activation.FileDataSource;
import javax.xml.namespace.QName;
import javax.xml.rpc.JAXRPCException;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPMessage;
import javax.xml.rpc.handler.GenericHandler;
public final class ClientAttachmentHandler
extends GenericHandler
{
private String getFileName(SOAPMessage request)
throws SOAPException
{
SOAPBody body = request.getSOAPPart().getEnvelope().getBody();
SOAPElement opElem = (SOAPElement)body.getChildElements().next();
SOAPElement paramElem = (SOAPElement)opElem.getChildElements().next();
return paramElem.getValue();
}
public boolean handleRequest(MessageContext mc) {
SOAPMessageContext ctx = (SOAPMessageContext) mc;
SOAPMessage request = ctx.getMessage();
try {
String fileName = getFileName(request);
AttachmentPart part = request.createAttachmentPart();
part.setContentType("application/x-zip-compressed");
FileDataSource fds = new FileDataSource(fileName);
part.setDataHandler(new DataHandler(fds));
request.addAttachmentPart(part);
} catch(SOAPException e) {
e.printStackTrace();
throw new JAXRPCException(e);
}
return true;
}
public QName[] getHeaders() {
// TODO Auto-generated method stub
return null;
}
}
The implemented client-side handler must be registered in the handler registry. The client handler includes the implementation for sending a file.
[Example 23.9] << Client.java >>
package filetransfer; import java.io.File; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import javax.xml.rpc.Service; import javax.xml.rpc.Stub; import javax.xml.rpc.handler.HandlerInfo; import javax.xml.rpc.handler.HandlerRegistry; import FileAttachmentService_pkg.*; public final class Client { public static void main(String[] args) throws Exception { Service svc = new FileAttachmentService_Impl(); HandlerRegistry registry = svc.getHandlerRegistry(); List list = new ArrayList(); list.add(new HandlerInfo(ClientAttachmentHandler.class, null, null)); QName portName = new QName("urn:FileAttachmentService","FileTransferIFPort"); registry.setHandlerChain(portName, list); FileTransferIF port = ((FileAttachmentService_Impl)svc).getFileTransferIFPort(); String result = port.receiveFile("File_send.txt"); System.out.println("** File transfer result: "+result); } }
A client stub must be generated to execute the previous example. Run the following command to generate the stub.
JEUS_HOME/samples/webservice/jaxrpc_saaj_msg_handler/fileAttachment/fileAttachment-c
lient$ ant build
Execute the client.
JEUS_HOME/samples/webservice/jaxrpc_saaj_msg_handler/fileAttachment/fileAttachment-c
lient$ ant run
The following result will be displayed.
** File transfer result: Received file named: File_send.txt
The previous result shows that a file named “File_send.txt” has been successfully transmitted to the server, and the file is saved under the '/temp' directory as specified in the webservices.xml file.