Applying different interceptors when using @Endpoint and PayloadRootAnnotationMethodEndpointMapping

Spring-WS

On the job we needed two webservice. Both in the same web context and servlet. We’re using the PayloadRootAnnotationMethodEndpointMapping which scans for  @Endpoint annotations to detect webservice endpoints.

We defined some interceptors for validation and security, the problem here was that only one of the two @Endpoint’s needed to be secured but with the annotation mapping it seems that all interceptors configured in the xml config file affect all @Endpoint.

After some googling and reading through the spring-ws reference I found that there currently is no solution or work around available. Which seems a bit dull. I did not want to create two servlet contexts I just wanted different interceptors for each @Endpoint annotated class.

Little thinking led me to the following solution: let’s extend PayloadRootAnnotationMethodEndpointMapping#createEndpointInvocationChain to add the needed interceptor to the invocation chain.

This resulted in an annotation to define interceptors defined in the application context

/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * balder at redlab dot be wrote this file. As long as you retain this notice you
 * can do whatever you want with this stuff and cannot hold me accountable for any kind of issue or financial loss cause by this code. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return @redlabbe
 * ----------------------------------------------------------------------------
 */
package be.redlab.spring-ws;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
 *
 * Annotation used by the MethodEndpointInterceptorAdder to add
 * interceptors to the EndpointInvocationChain.
 * @see http://www.redlab.be/blog/i-tee/apigeek/java/2010/spring-ws-interceptor/
 * @author Balder 
 *
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Interceptors {
    /**
     *
     * @return the qualified names of the interceptors to be added to the
invocation chain, default to an empty array
     */
    String[] qualifiers() default { };
}

and an override of PayloadRootAnnotationMethodEndpointMapping to use as mapper

/*
 * ----------------------------------------------------------------------------
 * "THE BEER-WARE LICENSE" (Revision 42):
 * balder at redlab dot be wrote this file. As long as you retain this notice you
 * can do whatever you want with this stuff and cannot hold me accountable for any kind of issue or financial loss cause by this code. If we meet some day, and you think
 * this stuff is worth it, you can buy me a beer in return @redlabbe
 * ----------------------------------------------------------------------------
 */
package be.redlab.spring-ws;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.EndpointInterceptor;
import org.springframework.ws.server.EndpointInvocationChain;
import org.springframework.ws.server.endpoint.MethodEndpoint;
import org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping;
/**
 * The MethodEndpointInterceptorAdder checks the
 * {@link MethodEndpoint#getMethod()} Method#getDeclaringClass() for the
 * {@link Interceptors} annotation. if the annotation is found and declares
 * interceptors, the beans with the given beanNames will be added to the
 * {@link EndpointInvocationChain} for this call.
 * @see http://www.redlab.be/blog/i-tee/apigeek/java/2010/spring-ws-interceptor/
 * @author Balder
 *
 */
public class MethodEndpointInterceptorAdder extends PayloadRootAnnotationMethodEndpointMapping {
	/*
	 * (non-Javadoc)
	 *
	 * @see
	 *
	 * org.springframework.ws.server.endpoint.mapping.AbstractEndpointMapping
	 * #createEndpointInvocationChain
	 * (org.springframework.ws.context.MessageContext, java.lang.Object,
	 * org.springframework.ws.server.EndpointInterceptor[])
	 */
	@Override
	protected EndpointInvocationChain createEndpointInvocationChain(final MessageContext messageContext, final Object endpoint, EndpointInterceptor[] interceptors) {
		if (endpoint.getClass().equals(MethodEndpoint.class)) {
			MethodEndpoint p = (MethodEndpoint) endpoint;
			Interceptors newInterceptor = AnnotationUtils.findAnnotation(p.getMethod().getDeclaringClass(), Interceptors.class);
			if (newInterceptor != null) {
				if (newInterceptor.qualifiers().length > 0) {
					ApplicationContext appContext = getApplicationContext();
					List list = new ArrayList();
					for (String interceptor : newInterceptor.qualifiers()) {
						if (appContext.containsBean(interceptor)) {
							Object bean = appContext.getBean(interceptor);
							list.add((EndpointInterceptor) bean);
						}
					}
					if (null != interceptors) {
						list.addAll(Arrays.asList(interceptors));
					}
					interceptors = list.toArray(new EndpointInterceptor[0]);
				}
			}
		}
		return super.createEndpointInvocationChain(messageContext, endpoint, interceptors);
	}
}

The @Interceptors can then be used like

@Endpoint
@Interceptors(qualifiers={"mySecurityInterceptor", "mySpecialLoggerInterceptor"})
public class MyWSEndpoint { 
 ......

the MethodEndpointInterceptorAdder will then look for interceptors defined in the application context and add them to the invocation chain.

With little modification this can easily be adapted to an annotation working on method level too.

3 Replies to “Applying different interceptors when using @Endpoint and PayloadRootAnnotationMethodEndpointMapping”

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.