From fe4747d5fca5de63f8c8c6b7088563b1ca61bccd Mon Sep 17 00:00:00 2001 From: Arjen Poutsma Date: Fri, 18 Jul 2014 13:03:59 +0200 Subject: [PATCH] Support @Repeatable for @SoapAction @PayloadRoot This commit introduces support for the JDK 8 @Repeatable annotation in the @SoapAction and @PayloadRoot. Issue: SWS-877 --- .../endpoint/annotation/PayloadRoot.java | 4 +- .../endpoint/annotation/SoapAction.java | 4 +- .../endpoint/annotation/SoapActions.java | 39 ++++++++++ ...ActionAnnotationMethodEndpointMapping.java | 25 ++++++- ...otAnnotationMethodEndpointMappingTest.java | 53 ++++++++----- ...onAnnotationMethodEndpointMappingTest.java | 74 +++++++++++++++++-- 6 files changed, 171 insertions(+), 28 deletions(-) create mode 100644 spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapActions.java diff --git a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoot.java b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoot.java index 085f3830..82ff4701 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoot.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/server/endpoint/annotation/PayloadRoot.java @@ -1,5 +1,5 @@ /* - * Copyright 2007 the original author or authors. + * Copyright 2002-2014 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. @@ -18,6 +18,7 @@ package org.springframework.ws.server.endpoint.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -33,6 +34,7 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(PayloadRoots.class) public @interface PayloadRoot { /** diff --git a/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java b/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java index e7da24bd..9b5cbf44 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapAction.java @@ -1,5 +1,5 @@ /* - * Copyright 2007 the original author or authors. + * Copyright 2002-2014 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. @@ -18,6 +18,7 @@ package org.springframework.ws.soap.server.endpoint.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; +import java.lang.annotation.Repeatable; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @@ -33,6 +34,7 @@ import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented +@Repeatable(SoapActions.class) public @interface SoapAction { /** Signifies the value for the request {@code SOAPAction} header that is handled by the method. */ diff --git a/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapActions.java b/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapActions.java new file mode 100644 index 00000000..cdbf49fa --- /dev/null +++ b/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/annotation/SoapActions.java @@ -0,0 +1,39 @@ +/* + * Copyright 2002-2014 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.soap.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 SoapAction SoapActions}. + * + * @author Arjen Poutsma + * @see org.springframework.ws.soap.server.endpoint.mapping.SoapActionAnnotationMethodEndpointMapping + * @since 2.2.1 + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface SoapActions { + + SoapAction[] value(); + +} diff --git a/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java b/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java index ff181773..da87eeb2 100644 --- a/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java +++ b/spring-ws-core/src/main/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMapping.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2014 the original author or authors. + * Copyright 2002-2014 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. @@ -17,6 +17,8 @@ package org.springframework.ws.soap.server.endpoint.mapping; import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.util.Assert; @@ -29,6 +31,7 @@ import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.server.SoapEndpointInvocationChain; import org.springframework.ws.soap.server.SoapEndpointMapping; import org.springframework.ws.soap.server.endpoint.annotation.SoapAction; +import org.springframework.ws.soap.server.endpoint.annotation.SoapActions; /** * Implementation of the {@link org.springframework.ws.server.EndpointMapping} interface that uses the {@link @@ -112,4 +115,24 @@ public class SoapActionAnnotationMethodEndpointMapping extends AbstractAnnotatio SoapAction soapAction = AnnotationUtils.findAnnotation(method, SoapAction.class); return soapAction != null ? soapAction.value() : null; } + + @Override + protected List getLookupKeysForMethod(Method method) { + List result = new ArrayList(); + + SoapActions soapActions = AnnotationUtils.findAnnotation(method, + SoapActions.class); + if (soapActions != null) { + for (SoapAction soapAction : soapActions.value()) { + result.add(soapAction.value()); + } + } + else { + SoapAction soapAction = AnnotationUtils.findAnnotation(method, SoapAction.class); + if (soapAction != null) { + result.add(soapAction.value()); + } + } + return result; + } } 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 e39b2ce3..21e113fd 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 @@ -1,11 +1,11 @@ /* - * Copyright 2005-2011 the original author or authors. + * Copyright 2002-2014 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 + * 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, @@ -23,6 +23,11 @@ import javax.xml.soap.MessageFactory; import javax.xml.soap.SOAPMessage; import javax.xml.transform.Source; +import org.apache.commons.logging.LogFactory; +import static org.junit.Assert.*; +import org.junit.Test; +import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.test.context.ContextConfiguration; @@ -42,12 +47,6 @@ import org.springframework.ws.soap.saaj.SaajSoapMessage; import org.springframework.ws.soap.saaj.SaajSoapMessageFactory; import org.springframework.ws.soap.server.SoapMessageDispatcher; -import org.apache.commons.logging.LogFactory; -import org.junit.Test; -import org.junit.runner.RunWith; - -import static org.junit.Assert.*; - @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("payloadRootAnnotationMethodEndpointMapping.xml") public class PayloadRootAnnotationMethodEndpointMappingTest { @@ -69,7 +68,7 @@ public class PayloadRootAnnotationMethodEndpointMappingTest { @Test public void registrationMultiple() throws NoSuchMethodException { - Method doItMultiple = MyEndpoint.class.getMethod("doItMultiple", Source.class); + Method doItMultiple = MyEndpoint.class.getMethod("doItMultiple"); MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doItMultiple); MethodEndpoint endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request1")); @@ -81,6 +80,20 @@ public class PayloadRootAnnotationMethodEndpointMappingTest { assertEquals("Invalid endpoint registered", expected, endpoint); } + @Test + public void registrationRepeatable() throws NoSuchMethodException { + Method doItMultiple = MyEndpoint.class.getMethod("doItRepeatable"); + MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doItMultiple); + + MethodEndpoint endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request3")); + assertNotNull("MethodEndpoint not registered", endpoint); + assertEquals("Invalid endpoint registered", expected, endpoint); + + endpoint = mapping.lookupEndpoint(new QName("http://springframework.org/spring-ws", "Request4")); + assertNotNull("MethodEndpoint not registered", endpoint); + assertEquals("Invalid endpoint registered", expected, endpoint); + } + @Test public void registrationInvalid() { assertNull("Invalid endpoint registered", @@ -130,14 +143,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()"); - } + @PayloadRoots({@PayloadRoot(localPart = "Request1", + namespace = "http://springframework.org/spring-ws"), + @PayloadRoot(localPart = "Request2", + namespace = "http://springframework.org/spring-ws")}) + public void doItMultiple() { + } + + @PayloadRoot(localPart = "Request3", + namespace = "http://springframework.org/spring-ws") + @PayloadRoot(localPart = "Request4", + namespace = "http://springframework.org/spring-ws") + public void doItRepeatable() { + + } } diff --git a/spring-ws-core/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMappingTest.java b/spring-ws-core/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMappingTest.java index 4897da2c..292ec54f 100644 --- a/spring-ws-core/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMappingTest.java +++ b/spring-ws-core/src/test/java/org/springframework/ws/soap/server/endpoint/mapping/SoapActionAnnotationMethodEndpointMappingTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2005-2010 the original author or authors. + * Copyright 2002-2014 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. @@ -18,6 +18,11 @@ package org.springframework.ws.soap.server.endpoint.mapping; import java.lang.reflect.Method; +import static org.easymock.EasyMock.*; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + import org.springframework.context.support.StaticApplicationContext; import org.springframework.ws.WebServiceMessageFactory; import org.springframework.ws.context.DefaultMessageContext; @@ -27,12 +32,7 @@ import org.springframework.ws.server.endpoint.MethodEndpoint; import org.springframework.ws.server.endpoint.annotation.Endpoint; import org.springframework.ws.soap.SoapMessage; import org.springframework.ws.soap.server.endpoint.annotation.SoapAction; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import static org.easymock.EasyMock.*; +import org.springframework.ws.soap.server.endpoint.annotation.SoapActions; public class SoapActionAnnotationMethodEndpointMappingTest { @@ -50,7 +50,7 @@ public class SoapActionAnnotationMethodEndpointMappingTest { } @Test - public void testRegistration() throws Exception { + public void registrationSingle() throws Exception { SoapMessage requestMock = createMock(SoapMessage.class); expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction"); WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class); @@ -66,6 +66,52 @@ public class SoapActionAnnotationMethodEndpointMappingTest { verify(requestMock, factoryMock); } + @Test + public void registrationMultiple() throws Exception { + SoapMessage requestMock = createMock(SoapMessage.class); + expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction1"); + expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction2"); + WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class); + replay(requestMock, factoryMock); + + Method doItMultiple = MyEndpoint.class.getMethod("doItMultiple"); + MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doItMultiple); + + MessageContext context = new DefaultMessageContext(requestMock, factoryMock); + EndpointInvocationChain chain = mapping.getEndpoint(context); + Assert.assertNotNull("MethodEndpoint not registered", chain); + Assert.assertEquals("Invalid endpoint registered", expected, chain.getEndpoint()); + + chain = mapping.getEndpoint(context); + Assert.assertNotNull("MethodEndpoint not registered", chain); + Assert.assertEquals("Invalid endpoint registered", expected, chain.getEndpoint()); + + verify(requestMock, factoryMock); + } + + @Test + public void registrationRepeatable() throws Exception { + SoapMessage requestMock = createMock(SoapMessage.class); + expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction3"); + expect(requestMock.getSoapAction()).andReturn("http://springframework.org/spring-ws/SoapAction4"); + WebServiceMessageFactory factoryMock = createMock(WebServiceMessageFactory.class); + replay(requestMock, factoryMock); + + Method doItRepeatable = MyEndpoint.class.getMethod("doItRepeatable"); + MethodEndpoint expected = new MethodEndpoint("endpoint", applicationContext, doItRepeatable); + + MessageContext context = new DefaultMessageContext(requestMock, factoryMock); + EndpointInvocationChain chain = mapping.getEndpoint(context); + Assert.assertNotNull("MethodEndpoint not registered", chain); + Assert.assertEquals("Invalid endpoint registered", expected, chain.getEndpoint()); + + chain = mapping.getEndpoint(context); + Assert.assertNotNull("MethodEndpoint not registered", chain); + Assert.assertEquals("Invalid endpoint registered", expected, chain.getEndpoint()); + + verify(requestMock, factoryMock); + } + @Endpoint private static class MyEndpoint { @@ -74,5 +120,17 @@ public class SoapActionAnnotationMethodEndpointMappingTest { } + @SoapActions({@SoapAction("http://springframework.org/spring-ws/SoapAction1"), + @SoapAction("http://springframework.org/spring-ws/SoapAction2")}) + public void doItMultiple() { + } + + @SoapAction("http://springframework.org/spring-ws/SoapAction3") + @SoapAction("http://springframework.org/spring-ws/SoapAction4") + public void doItRepeatable() { + + } + + } } \ No newline at end of file