Разработка web-сервисов настройка Spring Web Services

Для обеспечения обработки больших SOAP сообщений и повышения скорости необходима дополнительная настройка spring-ws. В этой статья я покажу как заставить spring-ws обрабатывать большие сообщения, как обеспечить поддержку SOAP 1.2 , как включить валидацию и журналирование входящих и исходящих сообщений и как обрабатывать внештатные ситуации.

Опубликовано 13-01-2013
Эксперементы
Теги java, spring-ws, soap

Для обеспечения обработки больших SOAP сообщений и повышения скорости необходима дополнительная настройка spring-ws. В этой статья я покажу как заставить spring-ws обрабатывать большие сообщения, как обеспечить поддержку SOAP 1.2 , как включить валидацию и журналирование входящих и исходящих сообщений и как обрабатывать внештатные ситуации.

Исходный код.

За основу мы возьмём уже разработанный web-сервис описанный в статье Разработка web-сервисов .

Обработка больших сообщений.

По умолчанию в spring-ws используется SAAJ для обработки SOAP сообщений. Если послать достаточно большое сообщение ( ~30MB ) нашему web-сервису, spring-ws не дойдя до обработчика вывалится с ошибкой OutOfMemory порождённой объектами SAAJ. Проблема в использовании SAAJ DOM парсера, что значительно замедляет процесс обработки сообщений и увеличивает размер потребляемой памяти. Для решения данной проблемы целесообразно использовать Apache Axiom в место SAAJ. Axiom использует так называемый pull parser позволяющий обрабатывать xml частично, по ходу его чтения, тем самым увеличивая скорость обработки и значительно снижая потребление памяти.

Добавим к нашему проекту Apache Axiom в файле pom.xml :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>ru.sarjsheff</groupId>
    <artifactId>sample-ws</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>sample-ws Maven Webapp</name>
    <url>http://maven.apache.org</url>
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.ws</groupId>
            <artifactId>spring-ws-core</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <type>jar</type>
        </dependency>
        <dependency>
            <groupId>org.apache.ws.commons.axiom</groupId>
            <artifactId>axiom-impl</artifactId>
            <version>1.2.13</version>
            <type>jar</type>
        </dependency>
    </dependencies>
    <build>
        <finalName>sample-ws</finalName>
    </build>
</project>

И переопределим messageFactory в WEB-INF/sample-ws-servlet.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
    
    <bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
        <property name="payloadCaching" value="true"/>
    </bean>
    
    <bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
        <property name="usePath" value="true"/>
        <property name="mappings">
            <props>
                <prop key="/sample-ws/sample">sampleEndpoint</prop>
            </props>
        </property>
    </bean>
    
    <bean id="sampleEndpoint" class="ru.sarjsheff.samplews.SampleEndpoint">
    </bean>
    
    <bean id="sample" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="sampleSchema"/>
        <property name="portTypeName" value="sample"/>
        <property name="locationUri" value="http://localhost:8080/sample-ws/sample"/>
        <property name="createSoap12Binding" value="true"/>
    </bean>

    <bean id="sampleSchema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="classpath:sample.xsd"/>
    </bean>

</beans>

Все. Теперь spring-ws способен обрабатывать сообщения большого размера и работает намного быстрей.

Поддержка SOAP 1.2 .

Для переключения версии SOAP достаточно определить пропорцию soapVersion объекта messageFactory в файле WEB-INF/sample-ws.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
    
    <bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
        <property name="payloadCaching" value="true"/>
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
        </property>
    </bean>
    
    <bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
        <property name="usePath" value="true"/>
        <property name="mappings">
            <props>
                <prop key="/sample-ws/sample">sampleEndpoint</prop>
            </props>
        </property>
    </bean>
    
    <bean id="sampleEndpoint" class="ru.sarjsheff.samplews.SampleEndpoint">
    </bean>
    
    <bean id="sample" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="sampleSchema"/>
        <property name="portTypeName" value="sample"/>
        <property name="locationUri" value="http://localhost:8080/sample-ws/sample"/>
        <property name="createSoap12Binding" value="true"/>
    </bean>

    <bean id="sampleSchema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="classpath:sample.xsd"/>
    </bean>

</beans>

Валидация и журналирование SOAP сообщений.

Объект org.springframework.ws.server.endpoint.mapping.UriEndpointMapping занимается распределением входящих вызовов по конечным обработчикам. Для данного объекта можно задать перехватчики входящих и исходящих сообщений ( установив пропорцию interceptors ). Для валидации сообщений по схеме используется перехватчик класса org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor . Для журналирования перехватчик класса org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor . Добавим определение перехватчиков и привяжем их к UriEndpointMapping в файле sample-ws-servlet.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
    
    <bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
        <property name="payloadCaching" value="true"/>
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
        </property>
    </bean>
    
    <bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
        <property name="usePath" value="true"/>
        <property name="mappings">
            <props>
                <prop key="/sample-ws/sample">sampleEndpoint</prop>
            </props>
        </property>
        <property name="interceptors">
            <list>
                <ref bean="validatingInterceptor" />
                <ref bean="loggingInterceptor" />
            </list>
        </property>
    </bean>

    <bean id="validatingInterceptor" class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
        <property name="schema" value="classpath:sample.xsd"/>
        <property name="validateRequest" value="true"/>
        <property name="validateResponse" value="true"/>
    </bean>
    
    <bean id="loggingInterceptor" class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
    
    <bean id="sampleEndpoint" class="ru.sarjsheff.samplews.SampleEndpoint">
    </bean>
    
    <bean id="sample" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="sampleSchema"/>
        <property name="portTypeName" value="sample"/>
        <property name="locationUri" value="http://localhost:8080/sample-ws/sample"/>
        <property name="createSoap12Binding" value="true"/>
    </bean>

    <bean id="sampleSchema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="classpath:sample.xsd"/>
    </bean>

</beans>

Теперь все сообщения видны в журналах, а при несоответствии входящего запроса формату описанному в схеме мы получим в ответ сообщение с описанием ошибки.

Обработка внештатных ситуаций.

Во время получения и обработки SOAP сообщений могут возникать непредвиденный ошибки. В spring-ws существует специальный объект обработчик exceptionResolver давайте переопределим его в файле sample-ws-servlet.xml :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd">
    
    <bean id="messageFactory" class="org.springframework.ws.soap.axiom.AxiomSoapMessageFactory">
        <property name="payloadCaching" value="true"/>
        <property name="soapVersion">
            <util:constant static-field="org.springframework.ws.soap.SoapVersion.SOAP_12"/>
        </property>
    </bean>
    
    <bean class="org.springframework.ws.server.endpoint.mapping.UriEndpointMapping">
        <property name="usePath" value="true"/>
        <property name="mappings">
            <props>
                <prop key="/sample-ws/sample">sampleEndpoint</prop>
            </props>
        </property>
        <property name="interceptors">
            <list>
                <ref bean="validatingInterceptor" />
                <ref bean="loggingInterceptor" />
            </list>
        </property>
    </bean>

    <bean id="validatingInterceptor" class="org.springframework.ws.soap.server.endpoint.interceptor.PayloadValidatingInterceptor">
        <property name="schema" value="classpath:sample.xsd"/>
        <property name="validateRequest" value="true"/>
        <property name="validateResponse" value="true"/>
    </bean>
    
    <bean id="loggingInterceptor" class="org.springframework.ws.server.endpoint.interceptor.PayloadLoggingInterceptor"/>
    
    <bean id="sampleEndpoint" class="ru.sarjsheff.samplews.SampleEndpoint">
    </bean>
    
    <bean id="sample" class="org.springframework.ws.wsdl.wsdl11.DefaultWsdl11Definition">
        <property name="schema" ref="sampleSchema"/>
        <property name="portTypeName" value="sample"/>
        <property name="locationUri" value="http://localhost:8080/sample-ws/sample"/>
        <property name="createSoap12Binding" value="true"/>
    </bean>

    <bean id="sampleSchema" class="org.springframework.xml.xsd.SimpleXsdSchema">
        <property name="xsd" value="classpath:sample.xsd"/>
    </bean>
    
    <bean id="exceptionResolver" class="org.springframework.ws.soap.server.endpoint.SoapFaultMappingExceptionResolver">
        <property name="defaultFault" value="SERVER"/>
        <property name="exceptionMappings">
            <value>
                org.springframework.oxm.ValidationFailureException=CLIENT,Invalid request
            </value>
        </property>
    </bean>

</beans>

Теперь если мы сбросим Exception в нашем обработчике SampleEndpoint мы увидим корректный ответ с блоком soapenv:Fault в котором будет отображено сообщение из Exception .