Home > Code, General, Java, Web/HTML > CXF Raw SOAP Envelope via Interceptor

CXF Raw SOAP Envelope via Interceptor

CXF SOAPRecently I started using the CXF SOAP library from Apache and was stumped on a simple problem, that being, how to view/modify a raw SOAP envelope. This sounds like a relatively simple task (not too bad when you know how) however it does in fact require some know-how.

The CXF SOAP library gives you access to all parts of the SOAP request and response life-cycle (via HTTP) using CXF Interceptors. The different points in time are referred to as phases and an interceptor can be placed at any one of the defined phases and automatically gain access to the SOAP message object and it’s current state.

For this example I have chosen Phase.RECEIVE which is one of the first phases which is essentially unprocessed by the CXF SOAP stack, which means I have free rain to pre-process the SOAP envelope (if I want to). Particularly handy if you need to maintain backwards compatibility with old SOAP API’s, poorly defined complex SOAP objects, dodgy WSDL definitions or maybe you simply want to view raw SOAP envelope (handy for debugging).

The crux to this problem is manipulating InputStream’s, once an InputStream has been read it is not available, which obviously causes a problem as the rest of the CXF SOAP stack expects it to be available. The solution is to use the CXF provided CachedOutputStream and IOUtils.copy which takes a cache copy of the InputStream making it available for future use :)

Once the Interceptor is implemented all that remains is to add it to the CXF SOAP client pror to usage.

This is an example of gaining access to an incoming client SOAP envelope, the interceptor could quite easily be in the sent phase or even part of the SOAP server implementation…it’s up to you.

package CXFSoap;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import org.apache.cxf.helpers.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;

public class PacketInterceptor extends AbstractPhaseInterceptor<Message>{

	public PacketInterceptor(){
		super(Phase.RECEIVE);
	}

	public void handleMessage(Message message) throws Fault{
		// Get the supplied SOAP envelope in the form of an InputStream
		InputStream inputStream = message.getContent(InputStream.class);

		if (inputStream != null){
			String processedSoapEnv = "";
			// Cache InputStream so it can be read independently
			CachedOutputStream cachedInputStream = new CachedOutputStream();  
			try {
				IOUtils.copy(inputStream,cachedInputStream);
				inputStream.close();
				cachedInputStream.close();
				
				InputStream tmpInputStream = cachedInputStream.getInputStream();
				try{
					String inputBuffer = "";
					int data;
					while((data = tmpInputStream.read()) != -1){
						byte x = (byte)data;
						inputBuffer += (char)x;
					}
					/**
					  * At this point you can choose to reformat the SOAP
					  * envelope or simply view it just make sure you put
 					  * an InputStream back when you done (see below)
 					  * otherwise CXF will complain.
					  */
					processedSoapEnv = fixSoapEnvelope(inputBuffer);
				}
				catch(IOException e){
					
				}
			} 
			catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			// Re-set the SOAP InputStream with the new envelope
			message.setContent(InputStream.class,new ByteArrayInputStream(processSoapEnv.getBytes()));
	
			/**		
			 * If you just want to read the InputStream and not 
			 * modify it then you just need to put it back where
			 * it was using the CXF cached inputstream
			 *
			 * message.setContent(InputStream.class,cachedInputStream.getInputStream());
			*/
		}
	}

	private String fixSoapEnvelope(String xml){
		...
	}
}

import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;

import Stuff.StuffObj;
import CXFSoap.PacketInterceptor;
import CXFSoap.VIPCoreAPI;

public class CXFTest{

	    public static void main(String args[]) throws Exception{
	    		JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
		    	factory.setServiceClass(VIPCoreAPI.class);
		    	factory.setAddress("http://x.x.x.x/SOAP/API");
		    	factory.setWsdlLocation("http://x.x.x.x/SOAP/API?wsdl");
		    	factory.setUsername("xxx");
		    	factory.setPassword("xxx");


		    	/**
		    	 * This is the important part, the interceptor needs
		    	 * to be added to the CXF SOAP client prior to
		    	 * the SOAP call being made.
		    	 */
		    	factory.getInInterceptors().add(new PacketInterceptor());

		    	API client = (API)factory.create();
		    	StuffObj stuff = client.getStuff();
		    	System.out.print(stuff.toString());
	    }
}
  1. learner
    March 26th, 2010 at 21:40 | #1

    Hi,

    this was really helpful. But I tried implementing similar logic for outgoing interceptor which just wouldnt work. The output or the response sent to the client has the old+processed string i.e. double soap envelopes failing the system.
    Would you mind sharing the outgoing interceptor example too if you have tried any.

  2. January 10th, 2011 at 10:25 | #2

    Most likely you are using the incorrect phase, try: Phase.UNMARSHAL

  3. November 22nd, 2011 at 23:11 | #3

    With little modifications this helped to solve issue when remote webservice was returning invalid SOAP message.
    Thank you dude for this example.

  1. No trackbacks yet.
*