diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoots.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoots.java new file mode 100644 index 00000000..f21f321e --- /dev/null +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoots.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2007 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.ws.server.endpoint.annotation; + +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; + +/** + * Marks an endpoint method as containing multiple {@link PayloadRoot PayloadRoots}. + * + * @author Arjen Poutsma + * @see org.springframework.ws.server.endpoint.mapping.PayloadRootAnnotationMethodEndpointMapping + * @since 2.1.5 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface PayloadRoots { + + PayloadRoot[] value(); + +} diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java index ff3db85a..cf7ddab3 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/AbstractMethodEndpointMapping.java @@ -19,8 +19,10 @@ package org.springframework.ws.server.endpoint.mapping; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; @@ -119,10 +121,10 @@ public abstract class AbstractMethodEndpointMapping extends AbstractEndpointM ReflectionUtils.doWithMethods(endpointClass, new ReflectionUtils.MethodCallback() { public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { - T key = getLookupKeyForMethod(method); - if (key != null) { - registerEndpoint(key, new MethodEndpoint(endpoint, method)); - } + List keys = getLookupKeysForMethod(method); + for (T key : keys) { + registerEndpoint(key, new MethodEndpoint(endpoint, method)); + } } }); } @@ -133,6 +135,7 @@ public abstract class AbstractMethodEndpointMapping extends AbstractEndpointM * using {@link #registerEndpoint(Object, MethodEndpoint)}. * * @see #getLookupKeyForMethod(Method) + * @see #getLookupKeysForMethod(Method) */ protected void registerMethods(String beanName) { Assert.hasText(beanName, "'beanName' must not be empty"); @@ -141,13 +144,15 @@ public abstract class AbstractMethodEndpointMapping extends AbstractEndpointM Set methods = findEndpointMethods(endpointType, new ReflectionUtils.MethodFilter() { public boolean matches(Method method) { - return getLookupKeyForMethod(method) != null; + return !getLookupKeysForMethod(method).isEmpty(); } }); for (Method method : methods) { - T key = getLookupKeyForMethod(method); - registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), method)); + List keys = getLookupKeysForMethod(method); + for (T key : keys) { + registerEndpoint(key, new MethodEndpoint(beanName, getApplicationContext(), method)); + } } } @@ -179,16 +184,30 @@ public abstract class AbstractMethodEndpointMapping extends AbstractEndpointM } /** - * Returns the the endpoint keys for the given method. Returns null if the method is not to be + * Returns the the endpoint key for the given method. Returns null if the method is not to be * registered, which is the default. * * @param method the method * @return a registration key, or null if the method is not to be registered + * @see #getLookupKeysForMethod(Method) */ protected T getLookupKeyForMethod(Method method) { return null; } + /** + * Returns the the endpoint keys for the given method. Should return an empty array if the method is not to be + * registered. The default delegates to {@link #getLookupKeysForMethod(Method)}. + * + * @param method the method + * @return a list of registration keys + * @since 2.1.5 + */ + protected List getLookupKeysForMethod(Method method) { + T key = getLookupKeyForMethod(method); + return key != null ? Collections.singletonList(key) : Collections.emptyList(); + } + /** * Return the class or interface to use for method reflection. *

diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java index 1cd9cdfb..5a0c3781 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMapping.java @@ -17,6 +17,8 @@ package org.springframework.ws.server.endpoint.mapping; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import javax.xml.namespace.QName; import javax.xml.transform.TransformerFactory; @@ -25,6 +27,7 @@ import org.springframework.util.StringUtils; import org.springframework.ws.context.MessageContext; import org.springframework.ws.server.EndpointMapping; import org.springframework.ws.server.endpoint.annotation.PayloadRoot; +import org.springframework.ws.server.endpoint.annotation.PayloadRoots; import org.springframework.ws.server.endpoint.support.PayloadRootUtils; /** @@ -60,21 +63,33 @@ public class PayloadRootAnnotationMethodEndpointMapping extends AbstractAnnotati } @Override - protected QName getLookupKeyForMethod(Method method) { - PayloadRoot annotation = AnnotationUtils.findAnnotation(method, PayloadRoot.class); - if (annotation != null) { - QName qname; - if (StringUtils.hasLength(annotation.localPart()) && StringUtils.hasLength(annotation.namespace())) { - qname = new QName(annotation.namespace(), annotation.localPart()); - } - else { - qname = new QName(annotation.localPart()); - } - return qname; - } - else { - return null; + protected List getLookupKeysForMethod(Method method) { + List result = new ArrayList(); + + PayloadRoots payloadRoots = AnnotationUtils.findAnnotation(method, PayloadRoots.class); + if (payloadRoots != null) { + for (PayloadRoot payloadRoot : payloadRoots.value()) { + result.add(getQNameFromAnnotation(payloadRoot)); + } + } + else { + PayloadRoot payloadRoot = AnnotationUtils.findAnnotation(method, PayloadRoot.class); + if (payloadRoot != null) { + result.add(getQNameFromAnnotation(payloadRoot)); + } } + + return result; } + private QName getQNameFromAnnotation(PayloadRoot payloadRoot) { + if (StringUtils.hasLength(payloadRoot.localPart()) && StringUtils.hasLength( + payloadRoot.namespace())) { + return new QName(payloadRoot.namespace(), payloadRoot.localPart()); + } + else { + return new QName(payloadRoot.localPart()); + } + } + } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMappingTest.java b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMappingTest.java index 95ab54f9..e39b2ce3 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMappingTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/server/endpoint/mapping/PayloadRootAnnotationMethodEndpointMappingTest.java @@ -36,6 +36,7 @@ import org.springframework.ws.server.endpoint.MethodEndpoint; import org.springframework.ws.server.endpoint.adapter.DefaultMethodEndpointAdapter; import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.server.endpoint.annotation.PayloadRoot; +import org.springframework.ws.server.endpoint.annotation.PayloadRoots; import org.springframework.ws.server.endpoint.annotation.RequestPayload; import org.springframework.ws.soap.saaj.SaajSoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; @@ -58,15 +59,32 @@ public class PayloadRootAnnotationMethodEndpointMappingTest { private ApplicationContext applicationContext; @Test - public void registration() throws NoSuchMethodException { + public void registrationSingle() throws NoSuchMethodException { MethodEndpoint endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request")); assertNotNull("MethodEndpoint not registered", endpoint); Method doIt = MyEndpoint.class.getMethod("doIt", Source.class); MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doIt); assertEquals("Invalid endpoint registered", expected, endpoint); + } + @Test + public void registrationMultiple() throws NoSuchMethodException { + Method doItMultiple = MyEndpoint.class.getMethod("doItMultiple", Source.class); + MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doItMultiple); + + MethodEndpoint endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request1")); + assertNotNull("MethodEndpoint not registered", endpoint); + assertEquals("Invalid endpoint registered", expected, endpoint); + + endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request2")); + assertNotNull("MethodEndpoint not registered", endpoint); + assertEquals("Invalid endpoint registered", expected, endpoint); + } + + @Test + public void registrationInvalid() { assertNull("Invalid endpoint registered", - mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request2"))); + mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Invalid"))); } @Test @@ -112,11 +130,20 @@ public class PayloadRootAnnotationMethodEndpointMappingTest { logger.info("In doIt()"); } + @PayloadRoots({ + @PayloadRoot(localPart = "Request1", namespace = "http://springframework.org/spring-ws"), + @PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws") + }) + public void doItMultiple(@RequestPayload Source payload) { + doItInvoked = true; + logger.info("In doIt()"); + } + } static class OtherBean { - @PayloadRoot(localPart = "Request2", namespace = "http://springframework.org/spring-ws") + @PayloadRoot(localPart = "Invalid", namespace = "http://springframework.org/spring-ws") public void doIt() { } diff --git a/spring-ws-core/src/test/resources/org/springframework/ws/server/endpoint/mapping/payloadRootAnnotationMethodEndpointMapping.xml b/spring-ws-core/src/test/resources/org/springframework/ws/server/endpoint/mapping/payloadRootAnnotationMethodEndpointMapping.xml index 4a939e07..1adfd72c 100644 --- a/spring-ws-core/src/test/resources/org/springframework/ws/server/endpoint/mapping/payloadRootAnnotationMethodEndpointMapping.xml +++ b/spring-ws-core/src/test/resources/org/springframework/ws/server/endpoint/mapping/payloadRootAnnotationMethodEndpointMapping.xml @@ -15,6 +15,6 @@ - +