Веб-службы Spring - исключение исключения ExceptionResolver - PullRequest
6 голосов
/ 09 августа 2010

У меня есть служба SOAP, запрос и ответы работают, как и ожидалось, с хорошим вводом, если я укажу неверный ввод для элемента XML

в теле запроса:

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

Мой распознаватель исключений вызывается, этот преобразователь является просто реализацией преобразователя исключений, поэтому он не имеет отображений исключений, только несколько System.out в абстрактном методе

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

однако, если я отправлю запрос, который выглядит примерно так:

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

мой распознаватель не выполняется вообще

Я настроил log4j, чтобы иметь уровень отладки root, и вижу вывод:

2010-08-09 10: 30: 01,900 [Тема: http-8080-2] DEBUG [Org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter] - Принятие входящих [org.springframework.ws.transport.http.HttpServletConnection@c46dcf] в [http://localhost:8080/myws/MyWebServices/] ОШИБКА: 'Тип элемента "ns: MESSEDUPELEMENT" должен быть прекращен соответствующий конечный тег "".» 2010-08-09 10: 30: 01,920 [Тема: http-8080-2] DEBUG [Org.springframework.ws.transport.http.MessageDispatcherServlet] - Не удалось выполнить запрос org.springframework.ws.soap.saaj.SaajSoapMessageException: Не удалось получить доступ к конверту: невозможно создать конверт из указанного источника:; вложенное исключение com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl: Невозможно создать конверт из заданного источник: в org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion (SaajUtils.java:162) в org.springframework.ws.soap.saaj.SaajSoapMessage.getImplementation (SaajSoapMessage.java:251) в org.springframework.ws.soap.saaj.SaajSoapMessage. (SaajSoapMessage.java:84) в org.springframework.ws.soap.saaj.SaajSoapMessage. (SaajSoapMessage.java:70) в org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage (SaajSoapMessageFactory.java:168) в org.springframework.ws.transport.AbstractWebServiceConnection.receive (AbstractWebServiceConnection.java:90) в org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection (WebServiceMessageReceiverObjectSupport.java:86) в org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle (WebServiceMessageReceiverHandlerAdapter.java:57) в org.springframework.ws.transport.http.MessageDispatcherServlet.doService (MessageDispatcherServlet.java:230) в org.springframework.web.servlet.FrameworkServlet.processRequest (FrameworkServlet.java:571) в org.springframework.web.servlet.FrameworkServlet.doPost (FrameworkServlet.java:511) на javax.servlet.http.HttpServlet.service (HttpServlet.java:637) на javax.servlet.http.HttpServlet.service (HttpServlet.java:717) в org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:290) в org.apache.catalina.core.ApplicationFilterChain.doFilter (ApplicationFilterChain.java:206) в org.apache.catalina.core.StandardWrapperValve.invoke (StandardWrapperValve.java:233) в org.apache.catalina.core.StandardContextValve.invoke (StandardContextValve.java:191) в org.apache.catalina.core.StandardHostValve.invoke (StandardHostValve.java:127) в org.apache.catalina.valves.ErrorReportValve.invoke (ErrorReportValve.java:102) в org.apache.catalina.core.StandardEngineValve.invoke (StandardEngineValve.java:109) в org.apache.catalina.connector.CoyoteAdapter.service (CoyoteAdapter.java:298) в org.apache.coyote.http11.Http11AprProcessor.process (Http11AprProcessor.java:859) в org.apache.coyote.http11.Http11AprProtocol $ Http11ConnectionHandler.process (Http11AprProtocol.java:579) в org.apache.tomcat.util.net.AprEndpoint $ Worker.run (AprEndpoint.java:1555) на java.lang.Thread.run (Thread.java:619) Вызванный: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl:Невозможно создать конверт из заданного источник: в com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope (EnvelopeFactory.java:114) в com.sun.xml.internal.messaging.saaj.soap.ver1_1.SOAPPart1_1Impl.createEnvelopeFromSource (SOAPPart1_1Impl.java:70) в com.sun.xml.internal.messaging.saaj.soap.SOAPPartImpl.getEnvelope (SOAPPartImpl.java:122) в org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion (SaajUtils.java:159) ... еще 24 Причина: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: тип элемента "smm: smm-aid" должен быть завершается соответствующим конечным тегом "". на com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform (TransformerImpl.java:719) на com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform (TransformerImpl.java:313) в com.sun.xml.internal.messaging.saaj.util.transform.EfficientStreamingTransformer.transform (EfficientStreamingTransformer.java:393) в com.sun.xml.internal.messaging.saaj.soap.EnvelopeFactory.createEnvelope (EnvelopeFactory.java:102) ... еще 27 Вызвано: org.xml.sax.SAXParseException: The тип элемента "smm: smm-aid" должен быть завершается соответствующим конечным тегом "". на com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse (AbstractSAXParser.java:1231) в com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl $ JAXPSAXParser.parse (SAXParserImpl.java:522) в org.xml.sax.helpers.XMLFilterImpl.parse (XMLFilterImpl.java:333) на com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transformIdentity (TransformerImpl.java:636) на com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform (TransformerImpl.java:707) ... еще 30

Похоже, что в Spring здесь отсутствует возможное исключение и оно не упаковано, но такое основное условие ошибки, которое не было обнаружено, кажется мне маловероятным. Может кто-нибудь помочь мне найти корень этой проблемы?

Я включу также web.xml и servlet.xml:

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">
    <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>

контекст сервлета:

<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>

Ответы [ 7 ]

6 голосов
/ 29 января 2013

MessageDispatcherServlet по умолчанию создает два своих собственных экземпляра EnpointExceptionResolver, а именно SoapFaultAnnotationExceptionResolver @ 1 и SimpleSoapExceptionResolver @ 2

И когда он разрешает исключение, SimpleSoapExceptionResolver @ 2 останавливает любую другую зарегистрированную исключающую точку EnEx.

Мне потребовалось смущающее время, чтобы понять, но ответ довольно прост, в контексте вашего сервлета вы должны сделать это:

<bean id="exceptionResolver" class="com.wdp.smm.ws.MyExceptionResolver">
    <property name="order" value="1"/>
</bean>
4 голосов
/ 20 августа 2010

Я внимательно посмотрел на ваш вопрос и думаю, что знаю, что происходит. Ваш обработчик исключений не вызывается, потому что он находится на более высоком уровне обработки мыла. Видите ли, WebServiceMessageReceiverHandlerAdapter пытается декодировать входящую строку в XML перед отправкой ее маршаллеру для обработки. Поскольку XML недействителен, вызов не выполняется. А поскольку WebServiceMessageReceiverHandlerAdapter не поддерживает обработчик исключений, он просто сбрасывает исключение «SaajSoapMessageException».

Теперь вы можете создать новый класс, который расширяет WebServiceMessageReceiverHandlerAdapter, но также оборачивает handleConnection () в try / catch, который использует ваш обработчик исключений, когда выбрасывается исключение.


Кстати, при отладке такого рода проблем мой подход заключается в выводе имени метода и номера строки в log4j. А также скачивание источников Spring.

3 голосов
/ 26 сентября 2013

Я думаю, что то, что объяснил @ thierry-dimitri-roy, правильно, однако у меня было много проблем с его реализацией.Например, недостаточно просто обернуть метод handleconnection из-за того, что noendpointfoundexception не генерируется.Поскольку это более общая проблема правильной обработки исключений, я привожу здесь свой код, чтобы облегчить боль для будущих поколений.Это проверено на spring-ws 2.1.3 и JBoss AS7.

Мой обработчик сообщений преобразует все проблемы в мыльные ошибки с кодом ответа 200.

package fi.eis.applications.spring.soap.server.transport.http;

import java.io.IOException;
import java.net.URISyntaxException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringEscapeUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.Ordered;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.ws.FaultAwareWebServiceMessage;
import org.springframework.ws.InvalidXmlException;
import org.springframework.ws.NoEndpointFoundException;
import org.springframework.ws.WebServiceMessage;
import org.springframework.ws.WebServiceMessageFactory;
import org.springframework.ws.context.DefaultMessageContext;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.support.DefaultStrategiesHelper;
import org.springframework.ws.transport.EndpointAwareWebServiceConnection;
import org.springframework.ws.transport.FaultAwareWebServiceConnection;
import org.springframework.ws.transport.WebServiceConnection;
import org.springframework.ws.transport.WebServiceMessageReceiver;
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.HttpTransportConstants;
import org.springframework.ws.transport.http.MessageDispatcherServlet;
import org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter;
import org.springframework.ws.transport.support.TransportUtils;

/**
 * Adapter to map XML parsing and other low-level errors to SOAP faults instead of
 * server standard error pages. Also, this class will always use return code HTTP_OK
 * (status 200) to requests, even if there are errors.
 *  
 */
public class MyWebServiceMessageReceiverHandlerAdapter
    extends WebServiceMessageReceiverHandlerAdapter
    implements HandlerAdapter, Ordered, InitializingBean, ApplicationContextAware {

    private ApplicationContext context;
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.context = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        DefaultStrategiesHelper defaultStrategiesHelper = new DefaultStrategiesHelper(MessageDispatcherServlet.class);
        WebServiceMessageFactory factory = defaultStrategiesHelper
                .getDefaultStrategy(WebServiceMessageFactory.class, context);
        setMessageFactory(factory);
    }

    public ModelAndView handle(HttpServletRequest httpServletRequest,
                               HttpServletResponse httpServletResponse,
                               Object handler) throws Exception {
        if (HttpTransportConstants.METHOD_POST.equals(httpServletRequest.getMethod())) {
            WebServiceConnection connection = new MyWebServiceConnection(httpServletRequest, httpServletResponse);
            try {
                overriddenHandleConnection(connection, (WebServiceMessageReceiver) handler);
            } catch (InvalidXmlException ex) {
                handleInvalidXmlException(httpServletRequest, httpServletResponse, handler, ex);
            } catch (Exception ex) {
                handleGeneralException(httpServletRequest, httpServletResponse, handler, ex);
            }
        }
        else {
            handleNonPostMethod(httpServletRequest, httpServletResponse, handler);
        }
        return null;
    }


    /**
     * Overridden version of handleConnection from WebServiceMessageReceiverObjectSupport to be able to handle
     * missing endpoint ourselves. That method is final, so we need to use another method here.
     * 
     * This has been reported as https://jira.springsource.org/browse/SWS-850
     * 
     * @param connection
     * @param receiver
     * @throws Exception
     */
    protected void overriddenHandleConnection(WebServiceConnection connection, WebServiceMessageReceiver receiver)
            throws Exception {
        logUri(connection);
        TransportContext previousTransportContext = TransportContextHolder.getTransportContext();
        TransportContextHolder.setTransportContext(new DefaultTransportContext(connection));

        try {
            WebServiceMessage request = connection.receive(getMessageFactory());
            MessageContext messageContext = new DefaultMessageContext(request, getMessageFactory());
            receiver.receive(messageContext);
            if (messageContext.hasResponse()) {
                WebServiceMessage response = messageContext.getResponse();
                if (response instanceof FaultAwareWebServiceMessage &&
                        connection instanceof FaultAwareWebServiceConnection) {
                    FaultAwareWebServiceMessage faultResponse = (FaultAwareWebServiceMessage) response;
                    FaultAwareWebServiceConnection faultConnection = (FaultAwareWebServiceConnection) connection;
                    faultConnection.setFault(faultResponse.hasFault());
                }
                connection.send(messageContext.getResponse());
            }
        }
        catch (NoEndpointFoundException ex) {
            if (connection instanceof EndpointAwareWebServiceConnection) {
                ((EndpointAwareWebServiceConnection) connection).endpointNotFound();
            }
            throw ex;
        }
        finally {
            TransportUtils.closeConnection(connection);
            TransportContextHolder.setTransportContext(previousTransportContext);
        }
    }

    private void logUri(WebServiceConnection connection) {
        if (logger.isDebugEnabled()) {
            try {
                logger.debug("Accepting incoming [" + connection + "] at [" + connection.getUri() + "]");
            }
            catch (URISyntaxException e) {
                // ignore
            }
        }
    }



    private void handleGeneralException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, Object handler,
            Exception ex) throws IOException {
        writeErrorResponseWithMessage(response, ex.getClass().getName() + ": " + ex.getMessage());
    }

    /**
     * By default, sets SC_BAD_REQUEST as response in Spring, so overwritten to
     * provide HTTP_OK and reasonable SOAP fault response.
     */
    protected void handleInvalidXmlException(
            HttpServletRequest httpServletRequest,
            HttpServletResponse response, Object handler, InvalidXmlException ex)
            throws IOException {
        writeErrorResponseWithMessage(response, ex.getClass().getName() + ": " + ex.getMessage());
    }

    /**
     * By default, sets SC_METHOD_NOT_ALLOWED as response in Spring, so overwritten to
     * provide HTTP_OK and reasonable SOAP fault response.
     */
    protected void handleNonPostMethod(HttpServletRequest httpServletRequest,
                                       HttpServletResponse response,
                                       Object handler) throws Exception {
        writeErrorResponseWithMessage(response, "HTTP Method not allowed");
    }

    private void writeErrorResponseWithMessage(HttpServletResponse response, String message)
            throws IOException {
        String errorXml = String.format(
                 "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">"
                +"    <SOAP-ENV:Header/>"
                +"    <SOAP-ENV:Body>"
                +"        <SOAP-ENV:Fault>"
                +"            <faultcode>SOAP-ENV:Client</faultcode>"
                +"            <faultstring xml:lang=\"en\">%s</faultstring>"
                +"        </SOAP-ENV:Fault>"
                +"    </SOAP-ENV:Body>"
                +"</SOAP-ENV:Envelope>",
                StringEscapeUtils.escapeXml(message)
                );

        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/xml");
        response.getWriter().write(errorXml);
        response.getWriter().flush();
    }
    @Override
    public int getOrder() {
        return 1;
    }

    /**
     * This class is needed as org.springframework.ws.transport.http.HttpServletConnection will throw an
     * exception if it is used outside Spring framework files. However, extending it and using the same
     * implementation seems to be fine.
     *
     */
    static class MyWebServiceConnection extends HttpServletConnection {
        protected MyWebServiceConnection(HttpServletRequest httpServletRequest,
                HttpServletResponse httpServletResponse) {
            super(httpServletRequest, httpServletResponse);
        }
    }    
}

Это также необходимо правильно настроить,Вот что нужно для весеннего контекста:

<!-- 'messageReceiverHandlerAdapter' is a magic name spring-ws
     org.springframework.ws.transport.http.MessageDispatcherServlet
     will bind to -->
<bean id="messageReceiverHandlerAdapter"
    class="fi.eis.applications.spring.soap.server.transport.http.MyWebServiceMessageReceiverHandlerAdapter">
</bean>
1 голос
/ 20 января 2012

Решение, которое я мог бы найти, состоит в том, чтобы переопределить метод doService в MessageDispatcherServlet и перехватить исключение, а затем обработать пользовательский ответ. Возможно, это не лучшее решение для вас, но оно работает для меня, надеюсь, это поможет!

public class CustomMessageDispatcherServlet extends MessageDispatcherServlet {

protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
    try {
        super.doService(request, response);
    } catch (Exception e) {
        String error = e.getMessage();
        String errorXml = "<SOAP-ENV:Envelope xmlns:SOAP-ENV=\"http://schemas.xmlsoap.org/soap/envelope/\">" +
                                "<SOAP-ENV:Header/>" +
                                    "<SOAP-ENV:Body>" +
                                        "<SOAP-ENV:Fault>" +
                                            "<faultcode>SOAP-ENV:Client</faultcode>" +
                                            "<faultstring xml:lang=\"en\"></faultstring>" + error +
                                        "</SOAP-ENV:Fault>" +
                                    "</SOAP-ENV:Body>" +
                                "</SOAP-ENV:Envelope>";
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/xml");
        response.getWriter().write(errorXml);
        response.getWriter().flush();
    }
}

}

Возможно, вы захотите перехватывать только те исключения, которые хотите обработать.

Замените org.springframework.ws.transport.http.MessageDispatcherServlet на CustomMessageDispatcherServlet в файле web.xml.

<servlet>
    <servlet-name>web-services</servlet-name>
    <servlet-class>CustomMessageDispatcherServlet</servlet-class>
</servlet>
1 голос
/ 20 августа 2010

Я помню, что сталкивался с такой проблемой при работе с Spring-Security.У меня также были проблемы с моим распознавателем исключений, который не вызывался в некоторых условиях.

Проблема тогда заключалась в том, что существует цепочка фильтров, которая обрабатывает каждый запрос.Код, который вызывает средства разрешения исключений, является фильтром в этой цепочке, но он находится близко к концу цепочки.Следовательно, если исключение возникло где-то в фильтрах до вызова фильтра распознавателя исключений, мой распознаватель никогда не будет вызван.

Я предполагаю, что вы страдаете от чего-то подобного, когда ошибки синтаксического анализа конверта происходят до вызова обработчика исключенияфильтр.В этом случае вам придется прибегнуть к другому способу обработки этих исключений, например, к фильтру ванильных сервлетов.

0 голосов
/ 22 февраля 2016

В последней версии Spring WS вы можете использовать пользовательский прослушиватель сообщений веб-службы

public class CustomWebServiceMessageListener extends WebServiceMessageListener {

private static final Logger LOG = LoggerFactory.getLogger(CustomWebServiceMessageListener.class);

@Override
public void onMessage(Message message, Session session) throws JMSException {
    try {
        this.handleMessage(message, session);
    } catch (JmsTransportException jmsTransportException) {
        LOG.error(jmsTransportException.getMessage(), jmsTransportException);
    } catch (Exception e) {
        JMSException jmsException = new JMSException(e.getMessage());
        jmsException.setLinkedException(e);
        LOG.error(e.getMessage(), e);
    }
  }
}

И введите это так:

  <bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
    <property name="concurrentConsumers" value="#{properties.concurrentListeners}"/>

    <property name="connectionFactory" ref="connectionFactory"/>
    <property name="destination" ref="queue"/>
    <property name="messageListener" ref="messageListener"/>
    <property name="sessionTransacted" value="true"/>

</bean>

<bean id="messageListener" class="com.yourproject.transport.CustomWebServiceMessageListener">
    <property name="messageFactory" ref="saajMessageFactory"/>
    <property name="messageReceiver" ref="messageDispatcher"/>
</bean>
0 голосов
/ 18 августа 2010

Вы должны "подключить" (или "внедрить") обработчик исключений в ваших бобах Spring.Я не уверен, какой из ваших бинов Spring действительно нуждается в обработчике исключений.

Лично я использую default-autowire="byName", что приводит к тому, что мой exceptionHandler автоматически подключается к моему классу контроллеров.Ваш подход на самом деле использует ручную проводку.Поэтому вам нужно выяснить, какой компонент должен использовать обработчик исключений.Вы пробовали (просто на моей голове):

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

Или вы могли бы просто добавить механизм Spring с автоматическим подключением и позволить ему автоматически связывать бобы.:)

...