June 30, 2014

Spring-WS Default Endpoint Configuration

We've recently been trying to set up some web services that provide common security functionality such as signing and signature verification. We really wanted to keep them generic as we did not want to have to set up individual operations and services. We chose to use Spring-WS because it had the concept of a default endpoint that could be configured to consume any web service not otherwise handled. For the security element we chose to use WSS4J Interceptors.

When it came to setting up the default endpoint I could find nothing at all that clearly defined how to do it, Spring Documentation, Spring Forums and even blog posts and Stack Overflow did not seem to have the answer. In the end it took trial and error and a certain amount of reading the source code to work out the magic sauce.

To save others the pain I thought that I would record a how-to here.

<beans xmlns:context="http://www.springframework.org/schema/context" xmlns:sws="http://www.springframework.org/schema/web-services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/web-services http://www.springframework.org/schema/web-services/web-services-2.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

    <context:component-scan base-package="com.yourpackage">

    <sws:annotation-driven>

    <bean class="org.springframework.ws.server.endpoint.adapter.MessageEndpointAdapter" id="messageEndpointAdapter" lazy-init="false">

    <bean class="org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping" id="endpointMapping" lazy-init="false">
        <property name="defaultEndpoint">
            <ref bean="defaultEndpoint">
        </ref></property>
    </bean>

    <bean class="com.yourpackage.DefaultEndpoint" id="defaultEndpoint">
    
</bean></bean></sws:annotation-driven></context:component-scan></beans>

The spring beans configuration supports annotation based component scan which is how I would normally set up endpoints but in the case of a default endpoint there appears to be no annotation based approach. I've set up a MessageEndpointAdapter that will be used by the PayloadRootAnnotationEndpointMapping class to be able to communicate with the default endpoint. Finally we explicitly set up the Endpoint Mapping with our default endpoint set in as a property.

The default endpoint implements the MessageEndpoint interface which uses a generic mechanism for representing the incoming message called the MessageContext. Below you can see an example where the default endpoint echoes the request message to be the response.

package com.yourpackage;

import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.MessageEndpoint;


public class DefaultEndpoint implements MessageEndpoint{
   @Override
    public void invoke(MessageContext messageContext) throws Exception {
        messageContext.setResponse(messageContext.getRequest());
    }
}


To be honest setting up a default endpoint is not hard once you know how. Shame the documentation isn't really there.

No comments: