views:

189

answers:

3

I have a SOAP service, the request and responses work as expected with good input, if I specify bad input for an XML element

in request body:

...
<ns:myIntegerElement>asdf</ns:myIntegerElement>
...

my exception resolver is invoked, this resolver is just an implementation of the exception resolver, so it doesn't have exception mappings, just a few System.out's in the abstract method

<bean id="exceptionResolver" class="com.mycompany.ws.MyExceptionResolver">

however, if I send a request that looks more like this:

...
    <ns:myIntegSOMETHINGGOTTOTALLYMESSUP!!!ent>asdf</ns:myIntegerElement>
...

my resolver isn't executed at all

I setup log4j to have a root debug level and see this output:

2010-08-09 10:30:01,900 [Thread:http-8080-2] DEBUG [org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter] - Accepting incoming [org.springframework.ws.transport.http.HttpServletConnection@c46dcf] to [http://localhost:8080/myws/MyWebServices/] ERROR: 'The element type "ns:MESSEDUPELEMENT" must be terminated by the matching end-tag "".' 2010-08-09 10:30:01,920 [Thread:http-8080-2] DEBUG [org.springframework.ws.transport.http.MessageDispatcherServlet] - Could not complete request org.springframework.ws.soap.saaj.SaajSoapMessageException: 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: at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:162) at org.springframework.ws.soap.saaj.SaajSoapMessage.getImplementation(SaajSoapMessage.java:251) at org.springframework.ws.soap.saaj.SaajSoapMessage.(SaajSoapMessage.java:84) at org.springframework.ws.soap.saaj.SaajSoapMessage.(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.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:230) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:571) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:511) at javax.servlet.http.HttpServlet.service(HttpServlet.java:637) at javax.servlet.http.HttpServlet.service(HttpServlet.java:717) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298) at org.apache.coyote.http11.Http11AprProcessor.process(Http11AprProcessor.java:859) at org.apache.coyote.http11.Http11AprProtocol$Http11ConnectionHandler.process(Http11AprProtocol.java:579) at org.apache.tomcat.util.net.AprEndpoint$Worker.run(AprEndpoint.java:1555) at java.lang.Thread.run(Thread.java:619) Caused by: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source: at com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:114) 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) ... 24 more Caused by: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: The element type "smm:smm-aid" must be terminated by the matching end-tag "". at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:719) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:313) 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) ... 27 more Caused by: org.xml.sax.SAXParseException: The element type "smm:smm-aid" must be terminated by the matching end-tag "". 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 com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity(TransformerImpl.java:636) at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:707) ... 30 more

It appears that spring is missing a possible exception here, and not wrapping it, but such a basic error condition not being caught seems unlikely to me. Can anyone help me to find the root of this issue?

I'll include web.xml and servlet.xml too:

web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"&gt;
    <servlet>
        <servlet-name>ws</servlet-name>
        <servlet-class>org.springframework.ws.transport.http.MessageDispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <init-param>
            <param-name>transformWsdlLocations</param-name>
            <param-value>true</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>ws</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

servlet context:

<context:component-scan base-package="com.mycomp.proj.ws" />
    <bean id="smmService" class="com.mycomp.proj.ws.SMMRequestHandlingServiceStubImpl"/>

    <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping"/>
    <bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
        <constructor-arg ref="marshaller"/>
    </bean>
    <bean id="marshaller" class="org.springframework.oxm.castor.CastorMarshaller">
        <property name="mappingLocations">
            <list>
                <value>classpath:mapping.xml</value>
                <value>classpath:hoursOfOperationMapping.xml</value>
            </list>
        </property>
    </bean>

    <bean id="smmws" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="schema" />
        <property name="portTypeName" value="SMM" />
        <property name="locationUri" value="/SMMWebServices/"/>
        <property name="targetNamespace" value="http://mycomp.proj.com" />
    </bean>

    <bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver"/>

    <bean id="schema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="/WEB-INF/ws.xsd" />
    </bean>
A: 

You have to "wire" (or "inject") the exception handler in your spring beans. I'm not sure which of your Spring bean actually needs the exception handler.

Personally, I use default-autowire="byName", which cause my exceptionHandler to be wired in my Controller class automatically. Your approach actually use manual wiring. So you need to find out which bean should actually use the exception handler. Have you tried (just on top of my head):

<bean class="org.springframework.ws.server.endpoint.adapter.MarshallingMethodEndpointAdapter">
    <constructor-arg ref="marshaller"/>
    <property name="exceptionHandler" ref="exceptionHandler" />
</bean>

Or you could just add the autowired mechanism of Spring and let it wire the beans automatically. :)

Thierry-Dimitri Roy
the exception resolver I defined is automatically used during the lifecycle of the SOAP call, however the exception that I gave the trace of is for some reason not handled by it. Either it's because the resolver doesn't handle exceptions yet in the life-cycle, or this exception is somehow slipping through uncaught.
walnutmon
I say this because the MarshallingMethodEndpointAdapter doesn't have a property "exceptionHandler", so you could not inject one in this fashion, unless I'm missing something
walnutmon
+1  A: 

I remember running into this kind of problem when working with spring-security. I also had issues with my exception resolver not being invoked under some condititions.

The problem then was that there is a filter chain that handles each request. The code that invokes the exception resolvers is a filter in this chain, but it is close to the end of the chain. Hence, if an exception ocurred somewhere within the filters before the exception resolver invoking filter, my resolver would never be invoked.

I am guessing you suffer from something similar here, where envelope parse errors happen before the exception resolver invoking filter. If that is the case, you will have to resort to some other way of handling those exceptions, for example a vanilla servlet filter.

Kristoffer E
+1  A: 

I've looked more closely at your question and I think that I know what's happening. You exception handler is not called because it is in the higher level in the soap processing. You see, WebServiceMessageReceiverHandlerAdapter tries to decode the incoming string to an XML before sending it to the marshaller to be processed. Since the XML is invalid the call fails. And since WebServiceMessageReceiverHandlerAdapter does not support an exception handler, it just rethrows the exception "SaajSoapMessageException".

Now what you can do is create a new class that extends WebServiceMessageReceiverHandlerAdapter, but that also wraps handleConnection() in a try/catch that uses your exception handler when a exception is throw.


By the way, when debugging this kind of problem my approach is to output both method name and line number in log4j. As well as downloading the Spring sources.

Thierry-Dimitri Roy
I could subclass, but how would I tell spring to use that implementation? I don't see any place where that is configurable
walnutmon
I want to add you're completely correct, however I don't have any solution to actually fixing this problem, I'm not sure how to leverage my implementation of the WebServiceMessageReceiverHandlerAdapter in my web service project, the way I understand it only certain beans are automatically picked up within the servlet-context.xml. At some point this week I will try just definining this sub-classed bean in the context file and see if it is used automagically, if you think you know how to override let me know, and the bounty is yours
walnutmon