Wednesday, March 31, 2010

Handling malformed SOAP messages gracefully in Spring WS

I'm going to discuss how to modify Spring WS 1.5.9 to gracefully
handle malformed SOAP message like this one :

<ArrayObject>

Notice that the ArrayObject tag is not closed. Out of the box if you
send Spring WS a malformed message you will get an HTTP 500 Error
with an stack trace that looks like this:

at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522) at org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:484) at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:393) at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:102) at com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:70) at com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:122) at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:159) at org.springframework.ws.soap.saaj.SaajSoapMessage.getImplementation(SaajSoapMessage.java:251) at org.springframework.ws.soap.saaj.SaajSoapMessage.<init>(SaajSoapMessage.java:84) at org.springframework.ws.soap.saaj.SaajSoapMessage.<init>(SaajSoapMessage.java:70) at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:168) at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:90) at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:86) at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57) at org.springframework.ws.transport.http.MessageDispatcherServlet2.doService(MessageDispatcherServlet2.java:148) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511)

In this post I'm going to show you how to have Spring-WS respond to
such messages with a correctly formed SOAP fault that looks like this
:

<SOAP-ENV:Envelope > <SOAP-ENV:Header/> <SOAP-ENV:Body> <SOAP-ENV:Fault> <faultcode>SOAP-ENV:Server</faultcode> <faultstring xml:lang="en">Could not access envelope: Unable to create envelope from given source: ; nested exception is com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source:</faultstring> <detail/> </SOAP-ENV:Fault> </SOAP-ENV:Body> </SOAP-ENV:Envelope>

Spring WS returns a HTTP 500 error when dealing with a malformed
message because it does not catch XML parsing errors. So we need to
find a point in the code where we can easily catch these exceptions,
create a SOAP fault message and return that SOAP fault message. Which
naturally, begs the question of what's the correct point in the stack
to intercept these exceptions?

We want to catch the XML parsing exception as high up in the stack as
possible, this will ensure that the solution works for as many
configurations of Spring WS as possible. This would suggest that we
catch the message in MessageDispatcherServlet2.doService. Which is
the earliest point the code that Spring WS enters into the stack.

However, intercepting the exception in
MessageDispatcherServlet2.doService has a major draw back, we don't
have access to a SOAP message factory at that point in stack. We
could inject a SOAP message factory into the MessageDispatcherServlet2
however this isn't ideal because we'd simply be duplicating
functionality found in the WebServiceMessageReceiverHandlerAdapter.
Given that WebServiceMessageReceiverHandlerAdapter.handle(...) is
called by MessageDispatcherServlet2.doService(...),
WebServiceMessageReceiverHandlerAdapter.handle(...) seems to be as a
good a place as any to catch the exception.

So now we have to write a class that extends
WebServiceMessageRecivierHandlerAdapter and override the handle method
to catch and process XML parsing exceptions:

package org.springframework.ws.transport.http; import java.io.IOException; import java.util.Locale; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView; import org.springframework.ws.context.DefaultMessageContext; import org.springframework.ws.context.MessageContext; import org.springframework.ws.soap.SoapBody; import org.springframework.ws.soap.SoapBodyException; import org.springframework.ws.soap.SoapFault; import org.springframework.ws.soap.SoapFaultDetail; import org.springframework.ws.soap.SoapFaultException; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.transport.WebServiceConnection; import org.springframework.ws.transport.context.DefaultTransportContext; import org.springframework.ws.transport.context.TransportContext; import org.springframework.ws.transport.context.TransportContextHolder; import org.springframework.ws.transport.http.HttpServletConnection; import org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter; import org.springframework.ws.transport.support.TransportUtils; public class ErrorAwareWebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverHandlerAdapter { @Override public void afterPropertiesSet() throws Exception { //do nothing } @Override public ModelAndView handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { try { super.handle(httpServletRequest, httpServletResponse, handler); } catch (Exception ex) { createSoapFaultFromException(httpServletRequest, httpServletResponse, ex); } return null; } protected void createSoapFaultFromException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Exception ex) throws SoapBodyException, IOException, SoapFaultException { WebServiceConnection connection = new HttpServletConnection(httpServletRequest, httpServletResponse); TransportContext previousTransportContext = TransportContextHolder.getTransportContext(); TransportContextHolder.setTransportContext(new DefaultTransportContext(connection)); try { MessageContext messageContext = new DefaultMessageContext(getMessageFactory().createWebServiceMessage(), getMessageFactory()); SoapBody soapBody = ((SoapMessage) messageContext.getResponse()).getSoapBody(); SoapFault fault = soapBody.addServerOrReceiverFault(ex.getLocalizedMessage(), Locale.ENGLISH); SoapFaultDetail faultDetail = fault.addFaultDetail(); connection.send(messageContext.getResponse()); } finally { TransportUtils.closeConnection(connection); TransportContextHolder.setTransportContext(previousTransportContext); } } }

As you can see I overrode the handle function to call the super
implementation and to catch and convert any exceptions that are thrown
into SOAP messages. The only tricky thing about this class is that it
is a member of the org.springframework.ws.transport.http package.
This is because it uses HTTPServletConnection which is package
protected. This also, unfortunately, means that if the the
HTTPServletConnection class is modified in the future this class will
cease to work.

Finally, you have to modify your application context to use the
ErrorAwareWebServiceMessageReceiverHandlerAdapter rather than the
default WebServiceMessageRecieverHandlerAdapter. You can do this by
adding this line to you ApplicationContext.xml:

<bean id="messageReceiverHandlerAdapter" class="org.springframework.ws.transport.http.ErrorAwareWebServiceMessageReceiverHandlerAdapter" />

Posted via email from Relatively Prime

Handling malformed SOAP messages gracefully in Spring WS.

 


I'm going to discuss how to modify Spring WS 1.5.9 to gracefully handle malformed SOAP message like this one :


<p style="MARGIN-BOTTOM: 0in"><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:web="http://namespace.du.jour"> <p style="MARGIN-BOTTOM: 0in"><soapenv:Header> <p style="MARGIN-BOTTOM: 0in"></soapenv:Header> <p style="MARGIN-BOTTOM: 0in"><soapenv:Body> <p style="MARGIN-BOTTOM: 0in"><web:publish> <p style="MARGIN-BOTTOM: 0in"><ArrayObject> <p style="MARGIN-BOTTOM: 0in"></web:publishIirRecord> <p style="MARGIN-BOTTOM: 0in"></soapenv:Body> <p style="MARGIN-BOTTOM: 0in"></soapenv:Envelope> <p style="MARGIN-BOTTOM: 0in">


Notice that the ArrayObject tag is not closed. Out of the box if you send Spring WS a malformed message you will get an HTTP 500 Error with an stack trace that looks like this:


<p style="MARGIN-BOTTOM: 0in">at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231) <p style="MARGIN-BOTTOM: 0in">at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522) <p style="MARGIN-BOTTOM: 0in">at org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333) <p style="MARGIN-BOTTOM: 0in">at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:484) <p style="MARGIN-BOTTOM: 0in">at com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:393) <p style="MARGIN-BOTTOM: 0in">at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:102) <p style="MARGIN-BOTTOM: 0in">at com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource(SOAPPart1_1Impl.java:70) <p style="MARGIN-BOTTOM: 0in">at com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:122) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:159) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.soap.saaj.SaajSoapMessage.getImplementation(SaajSoapMessage.java:251) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.soap.saaj.SaajSoapMessage.<init>(SaajSoapMessage.java:84) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.soap.saaj.SaajSoapMessage.<init>(SaajSoapMessage.java:70) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:168) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:90) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:86) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57) <p style="MARGIN-BOTTOM: 0in">at org.springframework.ws.transport.http.MessageDispatcherServlet2.doService(MessageDispatcherServlet2.java:148) <p style="MARGIN-BOTTOM: 0in">at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571) <p style="MARGIN-BOTTOM: 0in">at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511) <p style="MARGIN-BOTTOM: 0in">


In this post I'm going to show you how to have Spring-WS respond to such messages with a correctly formed SOAP fault that looks like this :


<p style="MARGIN-BOTTOM: 0in"><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"> <p style="MARGIN-BOTTOM: 0in"><SOAP-ENV:Header/> <p style="MARGIN-BOTTOM: 0in"><SOAP-ENV:Body> <p style="MARGIN-BOTTOM: 0in"><SOAP-ENV:Fault> <p style="MARGIN-BOTTOM: 0in"><faultcode>SOAP-ENV:Server</faultcode> <p style="MARGIN-BOTTOM: 0in"><faultstring xml:lang="en">Could not access envelope: Unable to create envelope from given source: ; nested exception is com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source:</faultstring> <p style="MARGIN-BOTTOM: 0in"><detail/> <p style="MARGIN-BOTTOM: 0in"></SOAP-ENV:Fault> <p style="MARGIN-BOTTOM: 0in"></SOAP-ENV:Body> <p style="MARGIN-BOTTOM: 0in"></SOAP-ENV:Envelope> <p style="MARGIN-BOTTOM: 0in">



Spring WS returns a HTTP 500 error when dealing with a malformed message because it does not catch XML parsing errors. So we need to find a point in the code where we can easily catch these exceptions, create a SOAP fault message and return that SOAP fault message. Which naturally, begs the question of what's the correct point in the stack to intercept these exceptions?


We want to catch the XML parsing exception as high up in the stack as possible, this will ensure that the solution works for as many configurations of Spring WS as possible. This would suggest that we catch the message in MessageDispatcherServlet2.doService. Which is the earliest point the code that Spring WS enters into the stack.


However, intercepting the exception in MessageDispatcherServlet2.doService has a major draw back, we don't have access to a SOAP message factory at that point in stack. We could inject a SOAP message factory into the MessageDispatcherServlet2 however this isn't ideal because we'd simply be duplicating functionality found in the WebServiceMessageReceiverHandlerAdapter. Given that WebServiceMessageReceiverHandlerAdapter.handle(...) is called by MessageDispatcherServlet2.doService(...), WebServiceMessageReceiverHandlerAdapter.handle(...) seems to be as a good a place as any to catch the exception.


So now we have to write a class that extends WebServiceMessageRecivierHandlerAdapter and override the handle method to catch and process XML parsing exceptions:


<p style="MARGIN-BOTTOM: 0in">package org.springframework.ws.transport.http; <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">import java.io.IOException; <p style="MARGIN-BOTTOM: 0in">import java.util.Locale; <p style="MARGIN-BOTTOM: 0in">import javax.servlet.http.HttpServletRequest; <p style="MARGIN-BOTTOM: 0in">import javax.servlet.http.HttpServletResponse; <p style="MARGIN-BOTTOM: 0in">import org.springframework.web.servlet.ModelAndView; <p style="MARGIN-BOTTOM: 0in">import org.springframework.web.servlet.ModelAndView; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.context.DefaultMessageContext; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.context.MessageContext; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.soap.SoapBody; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.soap.SoapBodyException; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.soap.SoapFault; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.soap.SoapFaultDetail; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.soap.SoapFaultException; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.soap.SoapMessage; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.WebServiceConnection; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.context.DefaultTransportContext; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.context.TransportContext; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.context.TransportContextHolder; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.http.HttpServletConnection; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter; <p style="MARGIN-BOTTOM: 0in">import org.springframework.ws.transport.support.TransportUtils; <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">public class ErrorAwareWebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverHandlerAdapter { <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">@Override <p style="MARGIN-BOTTOM: 0in">public void afterPropertiesSet() throws Exception { <p style="MARGIN-BOTTOM: 0in">//do nothing <p style="MARGIN-BOTTOM: 0in">} <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">@Override <p style="MARGIN-BOTTOM: 0in">public ModelAndView handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler) throws Exception { <p style="MARGIN-BOTTOM: 0in">try { <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">super.handle(httpServletRequest, httpServletResponse, handler); <p style="MARGIN-BOTTOM: 0in">} catch (Exception ex) { <p style="MARGIN-BOTTOM: 0in">createSoapFaultFromException(httpServletRequest, httpServletResponse, ex); <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">} <p style="MARGIN-BOTTOM: 0in">return null; <p style="MARGIN-BOTTOM: 0in">} <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">protected void createSoapFaultFromException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Exception ex) throws SoapBodyException, IOException, SoapFaultException { <p style="MARGIN-BOTTOM: 0in">WebServiceConnection connection = new HttpServletConnection(httpServletRequest, httpServletResponse); <p style="MARGIN-BOTTOM: 0in">TransportContext previousTransportContext = TransportContextHolder.getTransportContext(); <p style="MARGIN-BOTTOM: 0in">TransportContextHolder.setTransportContext(new DefaultTransportContext(connection)); <p style="MARGIN-BOTTOM: 0in">try { <p style="MARGIN-BOTTOM: 0in">MessageContext messageContext = new DefaultMessageContext(getMessageFactory().createWebServiceMessage(), getMessageFactory()); <p style="MARGIN-BOTTOM: 0in">SoapBody soapBody = ((SoapMessage) messageContext.getResponse()).getSoapBody(); <p style="MARGIN-BOTTOM: 0in">SoapFault fault = soapBody.addServerOrReceiverFault(ex.getLocalizedMessage(), Locale.ENGLISH); <p style="MARGIN-BOTTOM: 0in">SoapFaultDetail faultDetail = fault.addFaultDetail(); <p style="MARGIN-BOTTOM: 0in">connection.send(messageContext.getResponse()); <p style="MARGIN-BOTTOM: 0in">} finally { <p style="MARGIN-BOTTOM: 0in">TransportUtils.closeConnection(connection); <p style="MARGIN-BOTTOM: 0in">TransportContextHolder.setTransportContext(previousTransportContext); <p style="MARGIN-BOTTOM: 0in">} <p style="MARGIN-BOTTOM: 0in">} <p style="MARGIN-BOTTOM: 0in">} <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">


As you can see I overrode the handle function to call the super implementation and to catch and convert any exceptions that are thrown into SOAP messages. The only tricky thing about this class is that it is a member of the org.springframework.ws.transport.http package. This is because it uses HTTPServletConnection which is package protected. This also, unfortunately, means that if the the HTTPServletConnection class is modified in the future this class will cease to work.


Finally, you have to modify your application context to use the ErrorAwareWebServiceMessageReceiverHandlerAdapter rather than the default WebServiceMessageRecieverHandlerAdapter. You can do this by adding this line to you ApplicationContext.xml:


<p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in"><bean id="messageReceiverHandlerAdapter" class="org.springframework.ws.transport.http.ErrorAwareWebServiceMessageReceiverHandlerAdapter" /> <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in"> <p style="MARGIN-BOTTOM: 0in">

Posted via email from Karthik's posterous

Monday, March 8, 2010

Annotated-Based Transaction Managment with Spring 2.5.x and AspectJ Compile Time Weaving

In this post I'll describe how to use annotated transaction management with Spring 2.5.x and AspectJ compile time weaving. This is a short post, so I'll assume you have a working knowledge of Spring, it's transaction management capabilities, and Aspect J.

There are a number of ways to configure transaction managment in Spring for a complete description of the vast number of options available to you, I suggest you persue the Spring documenation. In this post, I'm dealing with the scenario in which you want declare your transaction semantics directly in your code using the @Transactional annotation, and in which you want to utilize AspectJ's compile time weaving rather than interface proxies or load time weaving.

<target name="-post-compile"> <taskdef resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">             <classpath>                 <pathelement location="${lib.dir}/AspectJ_1.6.8/aspectjtools.jar"/>             </classpath> </taskdef>  <iajc aspectPath="${file.reference.spring-aspects.jar}; ${build.classes.dir}/gov/ic/dia/wiseism/webservices/aspects"             classpath="${lib.dir}/AspectJ_1.6.8/aspectjrt.jar; ${javac.classpath}"             inpath="${build.classes.dir}"             destDir="${build.classes.dir}"             showWeaveInfo="true" /> <targe>

Posted via web from Relatively Prime