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