Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

Process attachments using JAX-RPC handlers

Boris Lublinsky (boris.lublinsky@cna.com), Enterprise Architect, CNA Insurance
Boris Lublinsky is an enterprise architect at CNA Insurance where he is involved in design and implementation of CNA's integration strategy, building application frameworks, and implementing Service-Oriented Architecture (SOA). Boris has more than 20 years' experience in software engineering and technical architecture. You can reach Boris at boris.lublinsky@cna.com.

Summary:  Discover an implementation that provides a very flexible mechanism for both sending and receiving arrays of attachments of arbitrary types. The author shows you an implementation for attachments processing based on JAX-RPC handlers and the SOAP with Attachments API for Java (SAAJ). He also shows how the proposed solution interoperates with the .NET implementation of attachments support.

Date:  18 Mar 2005
Level:  Advanced

Activity:  5799 views
Comments:  

Introduction

Attachments are a very important element for extending the functionality of Web services. Typical applications of Web services with attachments are:

  • Implementation of a command pattern with Web services, where a SOAP message defines the command and attachments contained in supporting XML documents. This approach is becoming increasingly popular for implementation of Web services in cases where all data is defined as one very large schema (see, for example, the ACORD proposal for Web services.)
  • Sending pictures and documents in support of Web services requests.

The simplest solution for dealing with attachments is base-64 data encoding, which allows for placing attachments directly inside the SOAP message. The data that needs to be attached is encoded, converted to a string representation, and placed inside the SOAP body of an XML payload. This works across all possible transports, is known to interoperate well across different implementations, and is pretty much guaranteed to work with small amounts of data. Yet base-64 encoding has its limits. It is inefficient; by using only 6 bits of each byte, it adds about a third to the length of a message. It also causes problems for processing of received messages due to the fact that XML parsers are not usually engineered to deal with very large strings.

A better approach, especially as the size of attachments grows, is to use messages with attachments. In this case the message itself contains multiple parts, one of which is the SOAP message, and other parts of the message contain additional (attachments) information. Unfortunately, when it comes to messages with attachments, there is not much agreement in the industry today. In fact, there are multiple standards currently on the market (see Resources for more information on these standards):

  • Multipurpose Internet Mail Extensions (MIME) is a standard used for e-mail and widely adopted by Java implementations of Web services.
  • Direct Internet Message Encapsulation (DIME) is a new standard proposed by Microsoft and IBM and the only implementation of attachments, available from Microsoft as part of Web Services Extensions (WSE) framework.
  • Web Service with attachments (WS-Attachment) based on DIME, currently submitted as an internet draft.
  • SOAP Message Transmission Optimization Mechanism (MTOM) describes an abstract feature and a concrete implementation of it for optimizing the transmission and wire format of SOAP messages, which is currently a proposed recommendation of the W3C.

To make the situation even worse, the Web Services Definition Language (WSDL), although it supports attachments definitions, specifies them differently, depending on whether MIME or DIME attachments are used. The WSDL definition is also rather limited, in the sense that it does not allow for specifying arrays of attachments of arbitrary types. Every possible attachment has to be explicitly specified as part of the WSDL service definition.

On another hand, SOAP with Attachments API for Java (SAAJ -- see Resources) allows for direct manipulation of attachments which are part of the SOAP message. Combined with the JAX-RPC handlers -- a standard mechanism for JAX-RPC extensibility, usage of SAAJ allows for generic support for attachments processing.

In this paper, I show you a generic JAX-RPC handler implementation, capable of sending and receiving arrays of an arbitrary attachment type. I first demonstrate the creation and usage of an attachment handler that supports a Web service implementation and client based on IBM® WebSphere® Application Server (Application Server) and then extend the implementation to support a .Net client, based on the Web Services Enhancements (WSE) add-on to Microsoft® Visual Studio .NET and the Microsoft .NET Framework from Microsoft.


Architecture of the proposed implementation

Message handlers are one the most powerful features of the JAX-RPC specification. They provide additional message-handling facilities to Web service endpoints (both client and server) as extensions to the basic service implementation logic. Handlers can manage encryption and decryption, logging and auditing, and so on. I use message handlers as a foundation for this proposed implementation, which will look as Figure 1 shows:


Figure 1. Overall implementation architecture
Implementation Architecture

The proposed solution in Figure 1 works as follows:

  • A service consumer creates an attachment container which contains all of the attachments that are destined to the service implementation, and appends it to the SOAP message context.
  • As part of the client request pipeline execution, the client request handler is invoked. This method checks the SOAP message context for an attachment container. If the container is found, then its content is copied to the request SOAP message.
  • A SOAP message is delivered to the service implementation pipeline.
  • As part of the service request pipeline execution, the service request handler is invoked. This method checks if an incoming message contains any attachments, and if it does, it repackages the attachments into the attachments container, which is appended to the SOAP message context.
  • Web services implementations check the SOAP message context for the attachment container, and if one is found, it is used for processing the request.
  • As part of the execution, the service implementation creates a response attachment container and adds it to the SOAP message context.
  • As part of the service reply pipeline execution, a service response handler is invoked. This method checks the SOAP message context for the attachment container. If the container is found, then its content is copied to the reply SOAP message.
  • The SOAP message is then delivered to the service client pipeline.
  • As part of the client response pipeline execution, the client response handler is invoked. This method checks if the incoming message contains any attachments, and if it does, it repackages the attachments into the attachments container, which is appended to the SOAP message context.
  • Web services client implementations check the SOAP message context for the attachment container, and if one is found, it is used for processing the reply.

The rest of this paper discusses and provides the details of the proposed implementation.


Implement a basic service

In order to keep implementation simple, I start by creating a very simple Echo Service. I use generation capabilities of WebSphere Studio Application Developer Integration Edition V5.1 (Application Developer). The simplest way to generate both the implementation and client programming support for Web services is to start from a Java bean, implementing the service (Listing 1) and then generating all of the artifacts that are required for deploying this bean as a service and to access this service from the client side.


Listing 1. Echo Service bean
package com.cna.services;

public class EchoService{
	
    public String echo(String request) {
		
	  System.out.println("Inside echo service");
      
  	  // Return
        return request;
    }
}
		 

Based on the generated Java code, a simple test client for this service can be implemented as follows (Listing 2):


Listing 2. Simple service testing client
package com.cna.service.tester;

import com.cna.services.EchoService;
import com.cna.services.EchoServiceProxy;

public class serviceTester {

	public void testServices(){
		
		EchoServiceProxy portProxy = new EchoServiceProxy();
		EchoService port = portProxy.getEchoService();
		String reply = null;
		try{
			reply = port.echo("my echo");
		}
		catch(Exception e){
			System.out.println("Error invoking service");
			e.printStackTrace();
		}
		System.out.println("Done invoking service " + reply);
	}
}
		 

A simple JSP (Listing 3) can also be created to invoke the tester (Listing 2) from the Web page:


Listing 3. Simple JSP for service tester invocation
	<![CDATA[
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding=
"ISO-8859-1"%>
<jsp:useBean id="serviceTester" scope="session" class=
"com.cna.service.tester.serviceTester" />
<META http-equiv="Content-Type" 
content="text/html; charset=ISO-8859-1">
<META name="GENERATOR" content="IBM WebSphere Studio">
<META http-equiv="Content-Style-Type" content="text/css">
<LINK href="../../theme/Master.css" rel="stylesheet" type="text/css">
<TITLE>serviceTester.jsp</TITLE>
</HEAD>
<BODY>
<P>Testing Service ... </P>
<% serviceTester.testServices(); %>
<P>Done </P>
</BODY>
</HTML>
   ]]>
   


Communicating between handlers and implementations

One of the underpinnings of the proposed solution is the ability of handlers to communicate with both service implementation and service invocation clients. Because there is no direct linkage between handler implementations and either a service implementation or a service client implementation, the only way to establish this communication is through the properties on the SOAPMessageContext. Unfortunately the access to this context (in Application Server V5.1) is supported differently on the client and service implementation sides.

Access SOAPMessageContext from the service implementation

In the current version of the JAX-RPC implementation, there is no direct way to access SOAPMessageContext. Fortunately this functionality is available through the javax.xml.rpc.server.ServiceLifecycle interface, which is a part of a JAX-RPC specification. Because service implementations can be derived from the ServiceLifecycle interface, SOAPMessageContext can be obtained in the init method of the ServiceLifecycle interface and then used by the service implementation. Listing 4 shows a simple implementation, which makes SOAPMessageContext available to the service implementations that extends this class.


Listing 4. ServiceLifecycle interface implementation
package com.cna.service.context;

import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.server.ServiceLifecycle;
import javax.xml.rpc.server.ServletEndpointContext;

public class CfContextHandler implements ServiceLifecycle{
	
	private MessageContext context;
	
	public void init(Object ctx){

		System.out.println("Inside Service init method");

		// Initialize message context for the service use 
		ServletEndpointContext sc = (ServletEndpointContext)ctx;
		if(ctx != null)
			context = sc.getMessageContext();
		else
			context = null;
	}

	public void destroy(){
	
		System.out.println("Inside Service destroy method");
	}

	public MessageContext getContext() {
		return context;
	}
}

		 

Access SOAPMessageContext from the service consumer

On the service consumer side, there is also no direct way to access SOAPMessageContext. The only way of doing this is by setting or getting properties on the javax.xml.rpc.Stub interface, defined as part of the JAX RPC standard. Every port implementation class (Listing 2) generated by Application Developer can be type-casted to the Stub interface, thus allowing for setting and getting Stub's properties.

One limitation of the Application Server and JAX-RPC implementation is that the property set on the Stub prior to service invocation is copied to the SOAPMessageContext, but changes to the SOAPMessageContext during service invocation and is not copied back to the Stub properties. This means that no changes to the SOAPMessageContext that are done during service invocation are visible to the service consumer. The simple way to get around this limitation is by using containers as properties (for the parameters that have to be set as part of a service invocation) before service invocation. In this case, instead of setting new properties on SOAPMessageContext, values are set in the container, which is directly available to the service consumer.


Implement attachments handlers

In order to implement an attachments handler you need to define a container class for multiple attachments. All of the attachments are based on two main things:

  • The attachment identifier: uniquely identifies this particular attachment inside a container. This identifier can also be used for referencing attachments in the SOAP message body.
  • The attachment data source: implementing javax.activation.DataSource interface and representing attachment content.

Java 1.4 provides several standard implementations for data source interface -- the FileDataSource class implements a simple data source object that encapsulates a file, and the URLDataSource class provides an object that wraps a URL object as a data source interface. Additional types of data sources can be created as needed. Based on the above, the attachments container (Listing 5) can be implemented as a map that contains attachment data sources and are keyed by attachments identifiers.


Listing 5. Attachment container class
package com.cna.service.attachments;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.activation.DataSource;
import com.ibm.ws.webservices.engine.attachments.Attachments;

public class CfAttachmentsContainer  implements Serializable{
	
	private Map map = null;
	
	public CfAttachmentsContainer(){
		
		map = new HashMap();
	}
	
	public void addAttachment(DataSource source, String ID){
		
		map.put(ID,source);
	}
	
	public Map getAttachments(){
		
		return map;
	}

	public DataSource getAttachment(String ID){
		
		return (DataSource)map.get(ID);
	}
}

		 

An additional class, a two-way attachments container (Listing 6), is also required to support a service consumer.


Listing 6. Two-way attachments container
package com.cna.service.attachments;

public class CfTwoWayAttchmentsContainer {
	
	private CfAttachmentsContainer requestContainer = null;
	private CfAttachmentsContainer responseContainer = null;

	public CfAttachmentsContainer getRequestContainer() {
		return requestContainer;
	}

	public CfAttachmentsContainer getResponseContainer() {
		return responseContainer;
	}

	public void setRequestContainer(CfAttachmentsContainer container) {
		requestContainer = container;
	}

	public void setResponseContainer(CfAttachmentsContainer container) {
		responseContainer = container;
	}
}

		 

Based on the above designed attachments container, the basic functionality of the attachments handler is for two-way conversions between a SOAP message with attachments and an attachments container. Because the attachments handler operates on the javax.xml.soap.SOAPMessage class, you can use SAAJ to manipulate the content of the SOAP message directly. The base handler, which implements conversion operations, is presented in Listing 7.


Listing 7. Base attachments handler
package com.cna.service.attachments.handler;

import java.util.Iterator;
import java.util.Map;

import javax.activation.DataHandler;
import javax.xml.namespace.QName;
import javax.xml.rpc.handler.GenericHandler;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPMessage;

import com.cna.service.attachments.CfAttachmentsContainer;

public class CfAttachmentHandler extends GenericHandler {

   public static String HANDLER_REQUEST_ATTACHMENT_PROPERTY = "ReqAttachments";
   public static String HANDLER_RESPONSE_ATTACHMENT_PROPERTY = "RespAttachments";
   public static String HANDLER_REQUEST_RESPONSE_ATTACHMENT_PROPERTY = "Attachments";

   // Metod Required by Generic Handler
   public QName[] getHeaders() {
	return null;
   }
	
protected void addContainerToMessage(MessageContext mc,CfAttachmentsContainer aContainer){
	try{
	   SOAPMessageContext smc = (SOAPMessageContext) mc;
	   SOAPMessage sMessage = smc.getMessage();
			
	   // Go through all attachments
	   Map attachments = aContainer.getAttachments();
	   Iterator aIterator = attachments.keySet().iterator();
	   while(aIterator.hasNext()){
		String ID = (String)aIterator.next();
		AttachmentPart attachment = sMessage.createAttachmentPart();
		DataHandler dh = new DataHandler(aContainer.getAttachment(ID));
		attachment.setDataHandler(dh);
		attachment.setContentId(ID);
		sMessage.addAttachmentPart(attachment);
	   }
	}
	catch(Exception e){
	   System.out.println("Error appending attachments on the consumer");
	   e.printStackTrace();
	}
   }
	
   protected CfAttachmentsContainer extractContainerFromMessage(MessageContext mc){	
	try{
	   SOAPMessageContext smc = (SOAPMessageContext) mc;
	   SOAPMessage sMessage = smc.getMessage();		
	   Iterator attachments = sMessage.getAttachments();
	   if(!attachments.hasNext())
		return null;
	   System.out.println("Storing Attachments to container");
	   CfAttachmentsContainer aContainer = new CfAttachmentsContainer();
			
	   // Process all attachments			
	   while(attachments.hasNext()){
		AttachmentPart attachment = (AttachmentPart)attachments.next();
		DataHandler dh = attachment.getDataHandler();
		aContainer.addAttachment(dh.getDataSource(),attachment.getContentId());
	   }
	   return aContainer;
	}
	catch(Exception e){
	   System.out.println("Error retrieving attachments on the consumer");
	   e.printStackTrace();
	   return null;
	}
   }
}

		 

Specific attachment handlers for the client (Listing 8) and server-side (Listing 9) extend the base attachment header class (Listing 7) by implementing handleRequest and handleResponse methods, which are invoked by the JAX-RPC pipeline.


Listing 8. Client attachments handler
package com.cna.service.attachments.handler;

import javax.xml.rpc.handler.MessageContext;
import com.cna.service.attachments.CfAttachmentsContainer;
import com.cna.service.attachments.CfTwoWayAttchmentsContainer;

public class CfClientAttachmentHandler extends CfAttachmentHandler{

   // Request handler. Takes container and adds it to the message	
   public boolean handleRequest(MessageContext mc){
		
	System.out.println("Inside Client Hand?e Request");
	CfTwoWayAttchmentsContainer tContainer = 
	(CfTwoWayAttchmentsContainer)mc.getProperty(
	HANDLER_REQUEST_RESPONSE_ATTACHMENT_PROPERTY);
	if(tContainer != null){
		CfAttachmentsContainer aContainer = tContainer.getRequestContainer();
		if(aContainer != null)
		   addContainerToMessage(mc, aContainer);
	}
	return true;
   }

   public boolean handleResponse(MessageContext mc){
		
	System.out.println("Inside Client Handle Response");
	CfTwoWayAttchmentsContainer tContainer = 
	(CfTwoWayAttchmentsContainer) mc.getProperty(
	HANDLER_REQUEST_RESPONSE_ATTACHMENT_PROPERTY);
	if(tContainer != null){
	   CfAttachmentsContainer aContainer = extractContainerFromMessage(mc);
	   if(aContainer != null)
		tContainer.setResponseContainer(aContainer);
	}
	return true;
   }
}

		 


Listing 9. Server attachments handler
package com.cna.service.attachments.handler;

import javax.xml.rpc.handler.MessageContext;
import com.cna.service.attachments.CfAttachmentsContainer;

public class CFServerAttachmentHandler extends CfAttachmentHandler {

   public boolean handleRequest(MessageContext mc){
		
	System.out.println("Inside server Handle Request");
	CfAttachmentsContainer aContainer = extractContainerFromMessage(mc);
	if(aContainer != null)
	   mc.setProperty(HANDLER_REQUEST_ATTACHMENT_PROPERTY, aContainer);
	return true;
   }

   public boolean handleResponse(MessageContext mc){
		
	System.out.println("Inside Server Handle Response");
	CfAttachmentsContainer aContainer = 
	(CfAttachmentsContainer)mc.getProperty(HANDLER_RESPONSE_ATTACHMENT_PROPERTY);
	if(aContainer != null)
	   addContainerToMessage(mc, aContainer);
	return true;
   }
}
		 

Notice that the server handler uses CfAttachmentsContainer and two distinct properties: one for inbound attachments and another one for the outbound. The client handler also uses a container -- CfTwoWayAttchmentsContainer and a single property. This container is used for both incoming and outgoing attachments and is set prior to service invocation.


Put it all together

To put it all together, you need to modify both the service and client implementations to send and receive attachments. You also need to make sure that the service implementation is extending the CfContextHandler class (Listing 4), so that it has access to the SOAPMessageContext.


Listing 10. Complete implementation of the service
package com.cna.services;

import java.util.Iterator;
import java.util.Map;

import javax.activation.DataSource;
import javax.xml.rpc.handler.MessageContext;

import com.cna.service.attachments.CfAttachmentsContainer;
import com.cna.service.attachments.handler.CfAttachmentHandler;
import com.cna.service.context.CfContextHandler;
import com.cna.services.datasources.CfDataSourceReader;
import com.cna.services.datasources.CfOctetDataSource;
import com.cna.services.datasources.CfPlainTextDataSource;

public class EchoService extends CfContextHandler{

   private static String attachment1 = "WAS Server weird attachment";
   private static String attachment2 = "WAS Server other weird attachment";
   private DataSource ds1 = null;
   private DataSource ds2 = null;

   public EchoService(){
	ds1 = new CfPlainTextDataSource("attachment1",attachment1);
	ds2 = new CfOctetDataSource ("attachment2",attachment2.getBytes());
   }
	
    public String echo(String request) {

	System.out.println("Inside echo service");

	// Process incoming attachments
		
	MessageContext context = getContext();
	CfAttachmentsContainer reqContainer = null;
	if(context != null)
	   reqContainer = 
	   (CfAttachmentsContainer)context.getProperty(
	   CfAttachmentHandler. HANDLER_REQUEST_ATTACHMENT_PROPERTY);
	   System.out.println("Got attachments container " + reqContainer);
	   if(reqContainer != null){	
		Map attachments = reqContainer.getAttachments();
		Iterator aIterator = attachments.keySet().iterator();
		while(aIterator.hasNext()){
		   String ID = (String)aIterator.next();
		   System.out.println("Got attachments " + ID);
		   DataSource ds = reqContainer.getAttachment(ID);
		   System.out.println(
		   "Attachment value is " + CfDataSourceReader.convertToString(ds));
	   }
	}

	// Create outgoing attachments
		
	if(context != null){
	   CfAttachmentsContainer repContainer = new CfAttachmentsContainer();
	   repContainer.addAttachment(ds1,"firstAttchment");
	   repContainer.addAttachment(ds2,"secondAttchment");
	   context.setProperty(
	   CfAttachmentHandler.HANDLER_RESPONSE_ATTACHMENT_PROPERTY,repContainer);
    }		
      // Return
      return request;
   }
}
		 


Listing 11. Complete implementation of client
package com.cna.service.tester;

import java.util.Iterator;
import java.util.Map;

import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.xml.rpc.Stub;
import javax.xml.soap.AttachmentPart;

import com.cna.service.attachments.CfAttachmentsContainer;
import com.cna.service.attachments.CfTwoWayAttchmentsContainer;
import com.cna.service.attachments.handler.CfAttachmentHandler;
import com.cna.services.EchoService;
import com.cna.services.EchoServiceProxy;
import com.cna.services.datasources.CfDataSourceReader;
import com.cna.services.datasources.CfOctetDataSource;
import com.cna.services.datasources.CfPlainTextDataSource;

public class serviceTester {
	
   private static String attachment1 = "Client weird attachment";
   private static String attachment2 = "Client other weird attachment";
   private DataSource ds1 = null;
   private DataSource ds2 = null;

   public serviceTester(){
	ds1 = new CfPlainTextDataSource("attachment1",attachment1);
	ds2 = new CfOctetDataSource("attachment2",attachment2.getBytes());
   }

   public void testServices(){
				
	CfTwoWayAttchmentsContainer tContainer = new CfTwoWayAttchmentsContainer();
	CfAttachmentsContainer reqContainer = new CfAttachmentsContainer();
	reqContainer.addAttachment(ds1,"firstAttchment");
	reqContainer.addAttachment(ds2,"secondAttchment");
	tContainer.setRequestContainer(reqContainer);
		
	EchoServiceProxy portProxy = new EchoServiceProxy();
	EchoService port = portProxy.getEchoService();
	Stub stub = (Stub)port;
	stub._setProperty(
	CfAttachmentHandler.HANDLER_REQUEST_RESPONSE_ATTACHMENT_PROPERTY,tContainer);
	String reply = null;
	try{
	   reply = port.echo("my echo");
	}
	catch(Exception e){
	   System.out.println("Error invoking service");
	   e.printStackTrace();
	}
	System.out.println("Done invoking service " + reply);
	CfAttachmentsContainer repContainer = tContainer.getResponseContainer();
	System.out.println("Got attachments container " + repContainer);
	if(repContainer != null){	
	   Map attachments = repContainer.getAttachments();
	   Iterator aIterator = attachments.keySet().iterator();
	   while(aIterator.hasNext()){
		String ID = (String)aIterator.next();
		System.out.println("Got attachments " + ID);
		DataSource ds = repContainer.getAttachment(ID);
		System.out.println(
		"Attachment value is " + CfDataSourceReader.convertToString(ds));
	   }
	}
   }
}
		 

Finally, both the client and server invocation pipelines have to be configured to include handlers, which are defined above (Listing 8 and Listing 9).

The following output is produced when a tester JSP (Listing 3) is invoked:


Listing 12. Results of a test service invocation
SystemOut     O Inside Client Handle Request
SystemOut     O Inside server Handle Request
SystemOut     O Storing Attachments to cont?iner
SystemOut     O Inside Service init method
SystemOut     O Inside echo service
SystemOut     O Got attachments container 
com.cna.service.attachments.CfAttachmentsContainer@71764583
SystemOut     O Got attachments secondAttchment
SystemOut     O Attachment value is Client other weird attachment
SystemOut     O Got attachments firstAttchment
SystemOut     O Attachment value is Client weird attachment
SystemOut     O Inside Service destroy method
SystemOut     O Inside Server Handle Response
SystemOut     O Inside Client Handle Response
SystemOut     O Storing Attachments to container
SystemOut     O Done invoking service my echo
SystemOut     O Got attachments container 
com.cna.service.attachments.CfAttachmentsContainer@22e60582
SystemOut     O Got attachments secondAttchment
SystemOut     O Attachment value is WAS Server other weird attachment
SystemOut     O Got attachments firstAttchment
SystemOut     O Attachment value is WAS Server weird attachment

		 


Extend handlers to support both MIME and DIME

Although it is not advertised or documented in the Application Server implementation, a SOAP message is based on the com.ibm.ws.webservices.engine.Message class which supports both MIME and DIME attachments. For the incoming messages, it examines HTTP content headers and builds a SOAP message correctly regardless of the attachment's type. Analogous to an AXIS implementation, this class allows querying of the attachment type of the incoming message (Listing 13).


Listing 13. Querying attachment type
import com.ibm.ws.webservices.engine.Message;
import com.ibm.ws.webservices.engine.attachments.Attachments;

....................................................................
   Message messageImpl = (Message)sMessage;
   if(messageImpl.getAttachmentsImpl().getSendType() == Attachments.SEND_TYPE_DIME)
	aContainer.setSendType(CfAttachmentsContainer.DIME_ATTACHMENTS);
   else
	aContainer.setSendType(CfAttachmentsContainer.MIME_ATTACHMENTS);
		 

When sending the attachment, Application Server, by default, assumes a MIME attachment, but allows it to overwrite the format in which the attachment will be sent, which is shown in Listing 14:


Listing 14. Overwriting attachment type
import com.ibm.ws.webservices.engine.Message;
import com.ibm.ws.webservices.engine.attachments.Attachments;

....................................................................
   if(aContainer.getSendType() == CfAttachmentsContainer.DIME_ATTACHMENTS){
	Message messageImpl = (Message)sMessage;
	messageImpl.getAttachmentsImpl().setSendType(Attachments.SEND_TYPE_DIME);
   }

		 

To incorporate the above capabilities, I modified the attachments container class (Listing 5) to add a variable, which contains the send or receive attachment's type and setter and getter methods for this variable. I also incorporated code to query the type of incoming message (Listing 13) and explicitly set the attachment type (Listing 14) into the implementation of a basic handler (Listing 7).


Implement a .Net service consumer

Implementation of attachments support in the .NET framework is provided by WSE, an add-on to .NET 1.1, which is freely available from the Microsoft Web site. I used the most current version of this package, WSE 2.0. WSE 2.0 provides a very simple model for processing attachments:

  • Individual attachment -- the DimeAttachment class can be created based on the attachment type and a stream, which represents the attachment content.
  • In order to create attachments for the request message, RequestSoapContext can be obtained from the service proxy. This context contains an attachment container, to which outgoing attachments can be added. A standard handler, provided by WSE, takes care of packaging these attachments with the outgoing message.
  • On the reply, the WSE-provided handler parses incoming messages and stores incoming attachments into the attachments container of the ResponseSoapContext. This context is then available as an attribute of the service proxy.

The overall sequence of steps, required for creation of a .Net client is as follows:

  • Download and install WSE 2.0.
  • Create a new C# console project in Visual Studio 2003.
  • Add the resource WSE 2.0.
  • Add Web resource, import WSDL for echo Service. When this is done Visual Studio generates two proxies: EchoServiceService (normal Web service proxy) and EchoServiceServiceWse (Web service proxy with WSE support). I used EchoServiceServiceWse class in this implementation.

The actual implementation is pretty straightforward, and is presented in Listing 15:


Listing 15. Implementation of a .Net client
using System;
using WasEchoServiceTester.WASEchoService;
using Microsoft.Web.Services2;
using Microsoft.Web.Services2.Dime;
using System.Web;
using System.Web.Services;
using System.IO;
using System.Text;
using System.Xml;
namespace WasEchoServiceTester
{
   class WASEchoClient
   {
	private static string request = "echo request";
	private static string aString = "Microsoft Client weird attachment";
	private static string aString1 = "Microsoft Client other weird attachment";
	[STAThread]
	static void Main(string[] args)
	{
	   // create service proxy

	   EchoServiceServiceWse eService = new EchoServiceServiceWse();

	   // Create attachments

	   SoapContext outSOAPContext = eService.RequestSoapContext;
	   UTF8Encoding encoder = new UTF8Encoding();
	   byte[] bytes = encoder.GetBytes(aString);
	   MemoryStream aStream = new MemoryStream(bytes);
	   DimeAttachment outAttachment = 
	   new DimeAttachment("text/plain",TypeFormat.None,aStream);
	   outAttachment.Id = "attachment1";
	   outSOAPContext.Attachments.Add(outAttachment);
	   bytes = encoder.GetBytes(aString1);
	   aStream = new MemoryStream(bytes);
	   outAttachment = new DimeAttachment("text/plain",TypeFormat.None,aStream);
	   outAttachment.Id = "attachment2";
	   outSOAPContext.Attachments.Add(outAttachment);

	   // Invoke service

	   string reply = eService.echo(request);
	   System.Console.Out.WriteLine("Returned from service 
 						with result " + reply);

	   // Process attachments

	   SoapContext inSOAPContext = eService.ResponseSoapContext;
	   int nAttachments = inSOAPContext.Attachments.Count;
	   if (nAttachments > 0){
		for(int i = 0; i < nAttachments;i ++){
		   String attID = inSOAPContext.Attachments[i].Id;
		   System.Console.Out.WriteLine("Processing attachment " + attID);
		   Stream inStream = inSOAPContext.Attachments[i].Stream;
		   int sLengt = (int)inStream.Length;
		   byte[] sBytes = new byte[sLengt];
		   int sByte = 0;
		   while(sLengt > 0){
		    int n = inStream.Read(sBytes, sByte, sLengt);
			if (n==0)
			   break;
			sByte += n;
			sLengt -= n;
		   }
		   inStream.Close();
		   Decoder decoder = encoder.GetDecoder();
		   int charCount = decoder.GetCharCount(sBytes, 0, sBytes.Length);
		   Char[] chars = new Char[charCount];
		   decoder.GetChars(sBytes, 0, sBytes.Length, chars, 0);
		   string attachmentValue = new string(chars);
		   System.Console.Write(attachmentValue + "\n");		
		}
	   }
	}
   }
}

		 

Testing of the .Net client (Listing 15) against the Application Server service (listing 10) produces the following results:


Listing 16. Echo Result of execution on Application Server
SystemOut     O Inside server Handle Request
SystemOut     O Storing Attachments to container
SystemOut     O Inside Service init method
SystemOut     O Inside echo service
SystemOut     O Got attachments container 
com.cna.service.attachments.CfAttachmentsContainer@4a5b5070
SystemOut     O Got attachments attachment2
SystemOut     O Attachment value is Microsoft Client other weird attachment
SystemOut     O Got attachments attachment1
SystemOut     O Attachment value is Microsoft Client weird attachment
SystemOut     O Inside Service destroy method
SystemOut     O Inside Server Handle Response
		 


Listing 17. Result of execution on .Net client
Returned from service with result echo request
Processing attachment secondAttchment
WAS Server other weird attachment
Processing attachment firstAttchment
WAS Server weird attachment
		 


Conclusion

This paper proposed a very flexible solution for attachment processing, for both incoming and outgoing messages. The solution allows for the processing of arbitrary arrays of attachments of an arbitrary type.


Acknowledgements

The author wishes to thank his CNA colleagues, especially James Mckune and Frank Marascom for discussion which helped to improve this paper and supporting code.



Download

DescriptionNameSizeDownload method
Source code for the articlews-jaxhandlecode.zip77 KBHTTP

Information about download methods


Resources

About the author

Boris Lublinsky is an enterprise architect at CNA Insurance where he is involved in design and implementation of CNA's integration strategy, building application frameworks, and implementing Service-Oriented Architecture (SOA). Boris has more than 20 years' experience in software engineering and technical architecture. You can reach Boris at boris.lublinsky@cna.com.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your developerWorks profile is displayed to the public, but you may edit the information at any time. Your first name, last name (unless you choose to hide them), and display name will accompany the content that you post.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services
ArticleID=56428
ArticleTitle=Process attachments using JAX-RPC handlers
publish-date=03182005
author1-email=boris.lublinsky@cna.com
author1-email-cc=

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

For articles in technology zones (such as Java technology, Linux, Open source, XML), Popular tags shows the top tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), Popular tags shows the top tags for just that product zone.

For articles in technology zones (such as Java technology, Linux, Open source, XML), My tags shows your tags for all technology zones. For articles in product zones (such as Info Mgmt, Rational, WebSphere), My tags shows your tags for just that product zone.

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Try IBM PureSystems. No charge.

Special offers