J2EE Web Service Development with Attachments Using Axis

This article discusses the development of a java web service that takes DIME or MIME attachment as input and returns the output as an appropriate attachment. It also discusses the client implementation for these web services.

J2EE Web Service Development with Attachments Using Axis

J2EE Web Service Development with Attachments Using Axis

Summary

This article discusses the development of a java web service that takes DIME or MIME attachment as input and returns the output as an appropriate attachment. It also discusses the client implementation for these web services. It uses the open source Apache Axis implementation of the web services. The whole source code can be downloaded from source zip file. Author expects that the intended audience for this article have a basic understanding of J2EE web service development.

Introduction

Attachments are becoming increasingly popular in the web service development especially in the world of interoperable environment where the language is not at all a barrier and thanks to the Service Oriented Architecture (SOA). DIME attachments are used with a lot of web services developed in C# in the .NET world. Using DIME attachments we can send various binary files, XML fragments and other SOAP messages. These attachments are becoming increasingly popular often with the Health Insurance and Life Insurance sectors that follow the ACORD standards for transferring the prescription data in XML.

As a developer very often one faces the following challenges in the context of J2EE web service development with attachments:

  1. Developing a web service that handles the attachments.
  2. Developing a client for accessing such web service.
  3. Develop the code to deal with the attachments used in such web services.

In this article, I have shown the implementation for all the above three tasks by using Apache Axis and Tomcat. I have used XML string as an attachment.

During the later half of last year I worked on a project implementing a client to access a web service written in C# that takes a big chunk of XML string as a DIME attachment. Recently I implemented a web service with attachments using Axis. While going through the process I found that there is very little documentation using Apache Axis especially in implementing the code that handles the attachments and I have decided to share my experience with the rest of the community.

Web Service Implementation

I have used Tomcat 5.x as the web server to install the web application and uses axis 1.2.1. To implement a web service that takes either a MIME or DIME attachment, we have to define an operation that takes a parameter javax.activation.DataHandler as a parameter. I have defined two operations -- one that handles a DIME attachment and sends a DIME attachment and another one that handles a MIME attachment and sends a MIME attachment as shown in Listing 1.


Listing 1. Method signatures in the web service

public Object getDimeData(DataHandler dh) throws 
       InputValidationException,
       AttachmentServiceException

public Object getMimeData(DataHandler dh) throws 
      InputValidationException, 
  AttachmentServiceException

	

Whether it is a DIME attachment or MIME attachment the way we get the data from the attachment is similar. But it is very important to make an input data validation at every point. The very first step in the getDimeData() or getMimeData() is to check whether the attachment we received is of correct type or not. If the attachment is not of the expected type we can throw an InputValidationException. Listing 2 shows the code to handle the DIME attachment.


Listing 2. Checking the DIME attachment

MessageContext msgContext = MessageContext.getCurrentContext();
Message rspmsg = msgContext.getResponseMessage();
log.info("org.apache.axis.attachments.Attachments.SEND_TYPE_DIME : " 
		+ org.apache.axis.attachments.Attachments.SEND_TYPE_DIME);
								
int inputAttachmentType = rspmsg.getAttachmentsImpl().getSendType();
log.info("inputAttachmentType : " +  inputAttachmentType);
		
if (inputAttachmentType !=  Attachments.SEND_TYPE_DIME){
String failMsg = 
    "Attachment passed is not a DIME attachment, please check.";
throw new InputValidationException("Invalid input data error : " 
    + failMsg);
}
	

Listing 3 shows the code to handle the MIME attachment.

Listing 3. Checking the MIME attachment

MessageContext msgContext = MessageContext.getCurrentContext();
Message rspmsg = msgContext.getResponseMessage();
log.info("org.apache.axis.attachments.Attachments.SEND_TYPE_MIME : " 
		+ org.apache.axis.attachments.Attachments.SEND_TYPE_MIME);
								
int inputAttachmentType = rspmsg.getAttachmentsImpl().getSendType();
log.info("inputAttachmentType : " +  inputAttachmentType);

if(inputAttachmentType !=  Attachments.SEND_TYPE_MIME){
	String failMsg = "Attachment passed is not a MIME attachment, 
       please check.";
	throw new InputValidationException("Invalid input data error : " 
      + failMsg);
}
		

After checking for the correct type of attachment, we need to some how get the contents in the attachment. We need to get the byte array in the attachment. Although attachment can be used to send images and various other types of content, in our current problem we are just dealing with an XML string. Listing 4 shows the code to get an array of bytes from the DataHandler object. The procedure to get the byte array is similar what ever may be the attachment. Listing 4. Getting the byte array from any attachment


public static byte[] getBytesFromDataHandler(DataHandler data) 
       throws IOException {
	InputStream in = null;
	byte out[] = null;
	in = data.getInputStream();
	if(in != null) {
		out = new byte[in.available()];
		in.read(out);
	} else {
		out = new byte[0];
	}
	return out;
}

Once we have the byte array it is easy to get the actual content if we know the type of content. Since it is a String in the current scenario, we can use UTF-8 charset to convert it to the original String. Listing 5 shows the code to get the input String passed by the client from the attachment.

Listing 5. Getting the client's input String from attachment

private String getInputString(DataHandler dh) throws  InputValidationException {
    String failMsg = null;
	if (dh == null ) {
		failMsg = "Attachment is null -- missing attachment.";
	throw new InputValidationException("Invalid data error : " 
          + failMsg);
	} else {
		byte[] responseBytes = null;
		try {
		responseBytes = Utils.getBytesFromDataHandler(dh);
		log.info("responseBytes length : " + responseBytes.length);
		} catch(IOException e) {
failMsg = 
   "Error occured while parsing the input XML, please check the input.";
	throw new InputValidationException("Invalid input data error : " 
      + failMsg);
		}
		if(responseBytes == null) {
failMsg = "null data received while parsing the input XML, please check the input.";
	throw new InputValidationException("Invalid input data error : " 
     + failMsg);
		}
		String inputStr = null;
		try {
			inputStr = new String(responseBytes, "UTF-8");
			log.info("inputStr : " + inputStr);
		} catch(UnsupportedEncodingException e) {
			failMsg = 
  
"Please check the encoding attribute value of the input XML, it should be UTF-8.";
throw new InputValidationException("Invalid input data error : " 
    + failMsg);
		}
		return inputStr;
	}
}

After getting the input String, we can use this input String to do the necessary operations such as database retrieval or update. After performing the necessary operations according to business need, we now have an output file let's say output.xml. In this article I have used an output String created from an output.xml for an illustration and in a real application this output can be any String generated by any process. Now our goal is to return this result to the client as an appropriate attachment i.e., if the input is DIME attachment we have to return the output String as a DIME attachment and if the input is a MIME attachment we have to return output String as a MIME attachment. Listing 6 shows the procedure to send the output String as a DIME attachment.

Listing 6. Sending output as a DIME attachment.

log.info("setting the DIME type of attachment as the sender 
     sends it as DIME.");
rspmsg.getAttachmentsImpl().setSendType(
     org.apache.axis.attachments.Attachments.SEND_TYPE_DIME);
ByteArrayDataSource dataSource = 
     new ByteArrayDataSource(result, "UTF-8");
dh = new DataHandler(dataSource);
return dh;

Listing 7 shows the procedure to send the output String as a DIME attachment. Listing 7. Sending output as a MIME attachment.


log.info("setting the MIME type of attachment as the sender sends 
     it as MIME.");
rspmsg.getAttachmentsImpl().setSendType(inputAttachmentType);
//	Create temp file.
File temp = File.createTempFile("MimeOutput", null);
// Delete temp file when program exits.
temp.deleteOnExit();
// Write to temp file
BufferedWriter out = new BufferedWriter(new FileWriter(temp));
out.write(result);
out.close();
dh = new DataHandler(new FileDataSource(temp));

You might be wondering why a temporary file is used in case of a MIME attachment. Technically whether it is a DIME or MIME the same code should work in the context of creating an output DataHandler object. But, when using the Apache axis implementation it will not work for MIME attachment. If we use the same procedure we will get javax.mail.MessagingException. I observed this problem with Apache AXIS, you might not get the problem with other implementations and in that case you can use the same code for both DIME and as well as MIME attachments. This might be a bug in one of the open source implementation and the work around for this is to use a temporary file for constructing DataHandler and of course these temporary files will be deleted whenever the server restarts if you use the above code.

Deployment of web Service

After developing the web service it is time to build the war file and deploy the application. The src.zip file contains all the source code. The required jar files are activation.jar, axis.jar, commons-discovery.jar, commons-logging.jar, jaxrpc.jar, log4j.jar, saaj.jar, soap.jar and wsdl4j.jar. I used Maven to build the war file and you can find more info on Maven at maven site. You can find the actual version of the jar files used in project.xml in my source distribution. You can build the war file by using your favorite build procedure, but make sure that you include all the required jar files in the final war distribution. Once you have the war file, you can just deploy on any web server. I have used Tomcat as the web server. After you deploy the war file in Tomcat, you will see an error in the AttachmentService.log file saying 'unable to find the config file'. Do not worry about it, we are going to create server-config.wsdd by using the deploy.wsdd file. Listing 8 shows the contents of deploy.wsdd file.

Listing 8. Contents of deploy.wsdd file.

<deployment	xmlns="http://xml.apache.org/axis/wsdd/" 
xmlns:java="http://xml.apache.org/axis/wsdd/providers/java" 
			xmlns:ns1="AttachmentService">

	<service name="AttachmentService" provider="java:RPC">
   		<parameter name="className" 
                   value="webservices.attachments.AttachmentService"/>
	    
		<parameter name="allowedMethods" 
             value="getDimeData,getMimeData"/>
		
		<operation name="getDimeData" returnQName="returnqname" 
         returnType="ns1:DataHandler">
       		<parameter name="dh" type="ns1:DataHandler"/>
	    </operation>
   		
		<operation name="getMimeData" returnQName="returnqname" 
        returnType="ns1:DataHandler">
       		<parameter name="dh" type="ns1:DataHandler"/>
      	</operation>
		
		<typeMapping 
   deserializer="org.apache.axis.encoding.ser.JAFDataHandlerDeserializerFactory"
 	languageSpecificType="java:javax.activation.DataHandler" 
          qname="ns1:DataHandler"
  serializer="org.apache.axis.encoding.ser.JAFDataHandlerSerializerFactory" 
	   encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
  	</service>
	
</deployment>

After installing axis application on Tomcat web server, execute the following command in /src/conf/ directory.


On Windows 
java -cp %AXISCLASSPATH% org.apache.axis.client.AdminClient 
     -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd
On UNIX  
java -cp $AXISCLASSPATH org.apache.axis.client.AdminClient
     -lhttp://localhost:8080/axis/services/AdminService deploy.wsdd

Please follow the guidelines listed on the axis user guide at Installing new WebServices.

Running the above command creates the server-config.wsdd file in the webapps/AttachmentServices/WEB-INF directory. If you have trouble creating WSDD file, you can just use the server-config.wsdd file included in the source distribution as it is. If you reached this stage means our web service is ready to be accessed for outside world. You can check WSDL file for this service at http://localhost:8080/AttachmentServices/services/AttachmentService?wsdl. If you encounter an error, it may be caused by problem during deployment of the war file or running the AdminClient. Double check all the steps listed in axis installation user guide.

Client implementation

Once the web service is ready we can write a client application for accessing this. There are two approaches for building the client application. One is to create some wrapper classes using some utility classes provided by axis and using these wrapper classes you can create simple main method. Another approach is to write a client application on your own. I used the latter approach because it will be more useful in understanding what parameters were actually passed to the Call object before invoking the web service. Practically, I found it to be more useful to know what is happening rather than using the wrapper classes generated by some utility classes.

First, create the org.apache.axis.client.Call object as shown in Listing 9 :

Listing 9. Creating Call Object for the service.

Service service = new Service();
Call call = (Call) service.createCall();

After creating the call object, set various parameters that are needed before accessing the service as shown in Listing 10 :

Listing 10. Client program to access the web service.

private static void testDataRetrieval(Call call, String operation) 
throws IOException {
	//Set the target service host and service location.
	call.setTargetEndpointAddress(new URL(WEB_SERVICE_URL)); 

	//This is the target services method to invoke.		
	call.setOperationName(new QName(WEB_SERVICE_NAME, operation)); 
        
	QName qnameAttachment = new QName(WEB_SERVICE_NAME, "DataHandler");

    DataHandler dhSource = new DataHandler(new FileDataSource(INPUT_XML));
        
    call.registerTypeMapping(dhSource.getClass(), 
        //Add serializer for attachment. 
                             qnameAttachment,
                             JAFDataHandlerSerializerFactory.class,
                             JAFDataHandlerDeserializerFactory.class);

    call.addParameter("source", qnameAttachment, ParameterMode.IN);
    //Add the file.

    call.setReturnType(qnameAttachment);
        
    try {
		Object ret = call.invoke(new Object[]{dhSource}); 
//Add the attachment.
	    log.info("ret : " + ret);
	    if(ret == null) {
	    	log.info("null response received.");
	    } else if(ret instanceof String) { 
	       	String errorMsg = (String)ret;
	       	log.info("errorMsg : " + errorMsg);
	    } else  if(ret instanceof DataHandler) {
	       	DataHandler dh = (DataHandler)ret;
	       	log.info("dh.getName() : " + dh.getName());
	    	byte[] responseBytes = Utils.getBytesFromDataHandler(dh);
	    	log.info("responseBytes length : " + responseBytes.length);
	    	String responseStr = new String(responseBytes, "UTF-8");
	    	log.info("responseStr : " + responseStr);
	    }
    } catch(RemoteException  e) {
        	log.error("AttachmentServiceException :" , e);
    }
}

As seen in the client code in the source distribution, the only difference between sending a DIME attachment and a MIME attachment is setting Call.ATTACHMENT_ENCAPSULATION_FORMAT property.

Conclusion

As I have shown in this article, writing a web service that can handle attachments and the client to access the web service using Apache Axis implementation is not a very difficult task, but there is not enough documentation at one place to accomplish this complete task. Even though this article talks about deploying in Tomcat web server, it should work on any J2EE web server. All you need to do is to deploy the war file on the server and create server-config.wsdd.

Download

Download the source code of this article:  Source Code

Resources

About the author

Murthy Vaddiparthi is a Sun Certified Enterprise Architect and has nine years of experience with various J2EE related technologies. Currently he is working on a J2EE web service development project as a Technical Lead at Transamerica, Charlotte USA. He worked as an IT consultant for various industries such as banking, telecommunications, Insurance and Financial Industries in wide variety of J2EE technologies.

Tutorials

  1. Apache Axis2 - Apache Axis2 Tutorial
  2. Why Web Services?
  3. Java Building a Simple Web Service ? A Tutorial Tutorial
  4. Apache Geronimo Application server Tutorial
  5. Apache Axis2 Tutorial, Develop the Next Generation of Apache Web Services using Apache Axis2
  6. SOA and Web Services
  7. Web Services Examples in NetBeans
  8. SOA and Web Services
  9. J2EE Web Service Development with Attachments Using Axis
  10. J2EE Web Service Development with Attachments Using Axis
  11. WEBSERVICE USING APACHE AXIS TUTORIAL-2 UNDERSTANDING APACHE AXIS
  12. Web Services - Web Services Tutorials
  13. Developing Axis Web services with XML Schemas.
  14. What is Service-Oriented Architecture?
  15. WEBSERVICE USING APACHE AXIS -TUTORIAL-2 UNDERSTANDING APACHE AXIS
  16. WEBSERVICE USING APACHE AXIS TUTORIAL-2 UNDERSTANDING APACHE AXIS (part-2)
  17. WEBSERVICE USING APACHE AXIS TUTORIAL-1
  18. WEBSERVICE USING APACHE AXIS TUTORIAL-2 UNDERSTANDING APACHE AXIS
  19. WEBSERVICE USING APACHE AXIS - TUTORIAL-2 AXIS FOR EJB-WEBSERVICE (part-5)
  20. Web Services Tutorials and Links
  21. WEBSERVICE USING APACHE AXIS TUTORIAL-2
  22. WEBSERVICE USING APACHE AXIS- TUTORIAL-2 J2ME CLIENT FOR EJB & EJB-WEBSERVICE
  23. Web Service
  24. Java Client webservice
  25. Ejb Webservice
  26. SOAP with Attachments API for Java
  27. SOAP Header
  28. WSDL program
  29. Application Using JAX-RPC
  30. Security in Web Service
  31. JAX-RPC Advance Concepts
  32. Database driven webservices
  33. Apache Axis2 - Apache Axis2 Tutorial
  34. Apache Axis2 Introduction
  35. Downloading and Installing Apache Axis2
  36. Apache Axis2 Hello World Example
  37. Axis2 client - Axis2 Client example
  38. Axis2 ant wsdl2java - Learn WSDL2java utility of Axis2 with example
  39. Axis2 Eclipse plugin Tutorial
  40. Installing axis2 eclipse plugin