diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java index 5eaf72d517..b1a0bfb917 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapper.java @@ -24,7 +24,6 @@ import org.springframework.core.GenericTypeResolver; import org.springframework.core.LocalVariableTableParameterNameDiscoverer; import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; -import org.springframework.integration.ConfigurationException; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageHandlingException; import org.springframework.integration.message.MessageHeaders; @@ -35,17 +34,16 @@ import org.springframework.util.StringUtils; /** * A {@link MessageMapper} implementation for annotated handler methods. * Method parameters are matched against the Message payload as well as its - * header attributes and properties. If a method parameter is annotated with - * {@link HeaderAttribute @HeaderAttribute} or {@link HeaderProperty @HeaderProperty}, - * the annotation's value will be used as an attribute/property key. If such an - * annotation contains no value, then the parameter name will be used as long as + * headers. If a method parameter is annotated with {@link Header @Header}, + * the annotation's value will be used as a header name. If such an annotation + * contains no value, then the parameter name will be used as long as * the information is available in the class file (requires compilation with * debug settings for parameter names). If neither annotation is present, then * the parameter will typically match the Message payload. However, if a Map or * Properties object is expected, and the paylaod is not itself assignable to - * that type, then the MessageHeader attributes will be passed in the case of - * a Map-typed parameter, or the MessageHeader properties will be passed in the - * case of a Properties-typed parameter. + * that type, then the MessageHeaders' values will be passed in the case of + * a Map-typed parameter, or the MessageHeaders' String-based values will be + * passed in the case of a Properties-typed parameter. * * @author Mark Fisher */ @@ -79,27 +77,16 @@ public class AnnotationMethodMessageMapper implements MessageMapper { MethodParameter methodParam = new MethodParameter(this.method, i); methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer); GenericTypeResolver.resolveParameterType(methodParam, this.method.getDeclaringClass()); - Object[] paramAnns = methodParam.getParameterAnnotations(); - String attributeName = null; - String propertyName = null; - for (int j = 0; j < paramAnns.length; j++) { - Object paramAnn = paramAnns[j]; - if (HeaderAttribute.class.isInstance(paramAnn)) { - HeaderAttribute headerAttribute = (HeaderAttribute) paramAnn; - attributeName = this.resolveParameterNameIfNecessary(headerAttribute.value(), methodParam); - parameterMetadata[i] = new MethodParameterMetadata(HeaderAttribute.class, attributeName, headerAttribute.required()); - } - else if (HeaderProperty.class.isInstance(paramAnn)) { - HeaderProperty headerProperty = (HeaderProperty) paramAnn; - propertyName = this.resolveParameterNameIfNecessary(headerProperty.value(), methodParam); - parameterMetadata[i] = new MethodParameterMetadata(HeaderProperty.class, propertyName, headerProperty.required()); + Object[] paramAnnotations = methodParam.getParameterAnnotations(); + String headerName = null; + for (int j = 0; j < paramAnnotations.length; j++) { + if (Header.class.isInstance(paramAnnotations[j])) { + Header headerAnnotation = (Header) paramAnnotations[j]; + headerName = this.resolveParameterNameIfNecessary(headerAnnotation.value(), methodParam); + parameterMetadata[i] = new MethodParameterMetadata(Header.class, headerName, headerAnnotation.required()); } } - if (attributeName != null && propertyName != null) { - throw new ConfigurationException("The @HeaderAttribute and @HeaderProperty annotations " + - "are mutually exclusive. They should not both be provided on the same parameter."); - } - if (attributeName == null && propertyName == null) { + if (headerName == null) { parameterMetadata[i] = new MethodParameterMetadata(methodParam.getParameterType(), null, false); } } @@ -121,19 +108,11 @@ public class AnnotationMethodMessageMapper implements MessageMapper { for (int i = 0; i < this.parameterMetadata.length; i++) { MethodParameterMetadata metadata = this.parameterMetadata[i]; Class expectedType = metadata.type; - if (expectedType.equals(HeaderAttribute.class)) { + if (expectedType.equals(Header.class)) { Object value = message.getHeaders().get(metadata.key); if (value == null && metadata.required) { throw new MessageHandlingException(message, - "required attribute '" + metadata.key + "' not available"); - } - args[i] = value; - } - else if (expectedType.equals(HeaderProperty.class)) { - Object value = message.getHeaders().get(metadata.key); - if (value == null && metadata.required) { - throw new MessageHandlingException(message, - "required property '" + metadata.key + "' not available"); + "required header '" + metadata.key + "' not available"); } args[i] = value; } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/HeaderAttribute.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/Header.java similarity index 79% rename from org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/HeaderAttribute.java rename to org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/Header.java index 31b8004036..88c7d89896 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/HeaderAttribute.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/Header.java @@ -24,17 +24,17 @@ import java.lang.annotation.Target; /** * Annotation indicating that a method parameter's value should be - * retrieved from an attribute in the message header. The value of - * the annotation provides the attribute key, and the optional - * 'required' property specifies whether the attribute value must - * be available within the header. + * retrieved from the message headers. The value of the annotation + * provides the header name, and the optional 'required' property + * specifies whether the attribute value must be available within + * the header. The default value for 'required' is true. * * @author Mark Fisher */ @Target(ElementType.PARAMETER) @Retention(RetentionPolicy.RUNTIME) @Documented -public @interface HeaderAttribute { +public @interface Header { String value() default ""; diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/HeaderProperty.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/HeaderProperty.java deleted file mode 100644 index d650eff6a6..0000000000 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/annotation/HeaderProperty.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2002-2008 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.integration.handler.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; - -/** - * Annotation indicating that a method parameter's value should be - * retrieved from a property in the message header. The value of - * the annotation provides the property key, and the optional - * 'required' property specifies whether the property value must - * be available within the header. - * - * @author Mark Fisher - */ -@Target(ElementType.PARAMETER) -@Retention(RetentionPolicy.RUNTIME) -@Documented -public @interface HeaderProperty { - - String value() default ""; - - boolean required() default true; - -} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/util/AbstractMethodInvokingAdapter.java b/org.springframework.integration/src/main/java/org/springframework/integration/util/AbstractMethodInvokingAdapter.java index 85fd4ad48d..55a034e777 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/util/AbstractMethodInvokingAdapter.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/util/AbstractMethodInvokingAdapter.java @@ -26,7 +26,7 @@ import org.springframework.core.Ordered; import org.springframework.integration.ConfigurationException; /** - * An base class for adapters that invoke a specified method and target object. + * A base class for adapters that invoke a specified method and target object. * Either a {@link Method} reference or a 'methodName' may be provided, but both * are not necessary. In fact, while preference is given to a {@link Method} * reference if available, an Exception will be thrown if a non-matching diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java index b9604146fb..8e8073c960 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/handler/annotation/AnnotationMethodMessageMapperTests.java @@ -41,8 +41,8 @@ import org.springframework.integration.message.StringMessage; public class AnnotationMethodMessageMapperTests { @Test - public void testOptionalAttribute() throws Exception { - Method method = TestHandler.class.getMethod("optionalAttribute", Integer.class); + public void testOptionalHeader() throws Exception { + Method method = TestHandler.class.getMethod("optionalHeader", Integer.class); AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); Object[] args = (Object[]) mapper.mapMessage(new StringMessage("foo")); assertEquals(1, args.length); @@ -50,15 +50,15 @@ public class AnnotationMethodMessageMapperTests { } @Test(expected=MessageHandlingException.class) - public void testRequiredAttributeNotProvided() throws Exception { - Method method = TestHandler.class.getMethod("requiredAttribute", Integer.class); + public void testRequiredHeaderNotProvided() throws Exception { + Method method = TestHandler.class.getMethod("requiredHeader", Integer.class); AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); mapper.mapMessage(new StringMessage("foo")); } @Test - public void testRequiredAttributeProvided() throws Exception { - Method method = TestHandler.class.getMethod("requiredAttribute", Integer.class); + public void testRequiredHeaderProvided() throws Exception { + Method method = TestHandler.class.getMethod("requiredHeader", Integer.class); AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); Message message = MessageBuilder.fromPayload("foo") .setHeader("num", new Integer(123)).build(); @@ -67,31 +67,39 @@ public class AnnotationMethodMessageMapperTests { assertEquals(new Integer(123), args[0]); } - @Test - public void testOptionalProperty() throws Exception { - Method method = TestHandler.class.getMethod("optionalProperty", String.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - Object[] args = (Object[]) mapper.mapMessage(new StringMessage("foo")); - assertEquals(1, args.length); - assertNull(args[0]); - } - @Test(expected=MessageHandlingException.class) - public void testRequiredPropertyNotProvided() throws Exception { - Method method = TestHandler.class.getMethod("requiredProperty", String.class); - AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); - mapper.mapMessage(new StringMessage("foo")); - } - - @Test - public void testRequiredPropertyProvided() throws Exception { - Method method = TestHandler.class.getMethod("requiredProperty", String.class); + public void testOptionalAndRequiredHeaderWithOnlyOptionalHeaderProvided() throws Exception { + Method method = TestHandler.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); Message message = MessageBuilder.fromPayload("foo") - .setHeader("prop", "bar").build(); + .setHeader("prop", "bar").build(); + mapper.mapMessage(message); + } + + @Test + public void testOptionalAndRequiredHeaderWithOnlyRequiredHeaderProvided() throws Exception { + Method method = TestHandler.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); + AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + Message message = MessageBuilder.fromPayload("foo") + .setHeader("num", new Integer(123)).build(); Object[] args = (Object[]) mapper.mapMessage(message); - assertEquals(1, args.length); + assertEquals(2, args.length); + assertNull(args[0]); + assertEquals(123, args[1]); + } + + @Test + public void testOptionalAndRequiredHeaderWithBothHeadersProvided() throws Exception { + Method method = TestHandler.class.getMethod("optionalAndRequiredHeader", String.class, Integer.class); + AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); + Message message = MessageBuilder.fromPayload("foo") + .setHeader("num", new Integer(123)) + .setHeader("prop", "bar") + .build(); + Object[] args = (Object[]) mapper.mapMessage(message); + assertEquals(2, args.length); assertEquals("bar", args[0]); + assertEquals(123, args[1]); } @Test @@ -210,7 +218,7 @@ public class AnnotationMethodMessageMapperTests { @Test public void testMessageAndHeaderWithAdapter() throws Exception { TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("messageAndAttribute", Message.class, Integer.class); + Method method = handler.getClass().getMethod("messageAndHeader", Message.class, Integer.class); AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); adapter.setObject(handler); @@ -223,9 +231,9 @@ public class AnnotationMethodMessageMapperTests { } @Test - public void testHeaderAndPropertyWithAdapter() throws Exception { + public void testMultipleHeadersWithAdapter() throws Exception { TestHandler handler = new TestHandler(); - Method method = handler.getClass().getMethod("propertyAndAttribute", String.class, Integer.class); + Method method = handler.getClass().getMethod("twoHeaders", String.class, Integer.class); AnnotationMethodMessageMapper mapper = new AnnotationMethodMessageMapper(method); DefaultMessageHandlerAdapter adapter = new DefaultMessageHandlerAdapter(); adapter.setObject(handler); @@ -247,33 +255,28 @@ public class AnnotationMethodMessageMapperTests { } @Handler - public String messageAndAttribute(Message message, @HeaderAttribute("number") Integer num) { + public String messageAndHeader(Message message, @Header("number") Integer num) { return (String) message.getPayload() + "-" + num.toString(); } @Handler - public String propertyAndAttribute(@HeaderProperty String prop, @HeaderAttribute("number") Integer num) { + public String twoHeaders(@Header String prop, @Header("number") Integer num) { return prop + "-" + num.toString(); } @Handler - public Integer optionalAttribute(@HeaderAttribute(required=false) Integer num) { + public Integer optionalHeader(@Header(required=false) Integer num) { return num; } @Handler - public Integer requiredAttribute(@HeaderAttribute(value="num", required=true) Integer num) { + public Integer requiredHeader(@Header(value="num", required=true) Integer num) { return num; } @Handler - public String optionalProperty(@HeaderProperty(required=false) String prop) { - return prop; - } - - @Handler - public String requiredProperty(@HeaderProperty(value="prop", required=true) String prop) { - return prop; + public String optionalAndRequiredHeader(@Header(required=false) String prop, @Header(value="num", required=true) Integer num) { + return prop + num; } @Handler diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/router/RouterMessageHandlerAdapterTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/router/RouterMessageHandlerAdapterTests.java index 7448c0ca8d..8d79dfc9d0 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/router/RouterMessageHandlerAdapterTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/router/RouterMessageHandlerAdapterTests.java @@ -26,17 +26,16 @@ import java.util.List; import org.junit.Test; -import org.springframework.integration.ConfigurationException; import org.springframework.integration.channel.ChannelRegistry; import org.springframework.integration.channel.ChannelRegistryAware; import org.springframework.integration.channel.DefaultChannelRegistry; import org.springframework.integration.channel.MessageChannel; import org.springframework.integration.channel.QueueChannel; -import org.springframework.integration.handler.annotation.HeaderAttribute; -import org.springframework.integration.handler.annotation.HeaderProperty; +import org.springframework.integration.handler.annotation.Header; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageBuilder; +import org.springframework.integration.message.MessageHandlingException; import org.springframework.integration.message.StringMessage; /** @@ -75,7 +74,7 @@ public class RouterMessageHandlerAdapterTests { @Test public void testChannelNameResolutionByHeader() throws Exception { SingleChannelNameRoutingTestBean testBean = new SingleChannelNameRoutingTestBean(); - Method routingMethod = testBean.getClass().getMethod("routeByProperty", String.class); + Method routingMethod = testBean.getClass().getMethod("routeByHeader", String.class); RouterMessageHandlerAdapter adapter = new RouterMessageHandlerAdapter(testBean, routingMethod); Message message = MessageBuilder.fromPayload("bar") .setHeader("returnAddress", "baz").build(); @@ -94,10 +93,10 @@ public class RouterMessageHandlerAdapterTests { assertEquals("bar", message2.getPayload()); } - @Test(expected=ConfigurationException.class) - public void testFailsWhenPropertyAndAttributeAreBothProvided() throws Exception { - InvalidRoutingTestBean testBean = new InvalidRoutingTestBean(); - Method routingMethod = testBean.getClass().getMethod("tooManyAnnotations", String.class); + @Test(expected=MessageHandlingException.class) + public void testFailsWhenRequireddHeaderIsNotProvided() throws Exception { + SingleChannelNameRoutingTestBean testBean = new SingleChannelNameRoutingTestBean(); + Method routingMethod = testBean.getClass().getMethod("routeByHeader", String.class); RouterMessageHandlerAdapter adapter = new RouterMessageHandlerAdapter(testBean, routingMethod); adapter.afterPropertiesSet(); adapter.handle(new GenericMessage("testing")); @@ -542,11 +541,7 @@ public class RouterMessageHandlerAdapterTests { return name + "-channel"; } - public String routeByProperty(@HeaderProperty("returnAddress") String name) { - return name + "-channel"; - } - - public String routeByAttribute(@HeaderAttribute("returnAddress") String name) { + public String routeByHeader(@Header("returnAddress") String name) { return name + "-channel"; } @@ -673,12 +668,4 @@ public class RouterMessageHandlerAdapterTests { } } - public static class InvalidRoutingTestBean { - - public String tooManyAnnotations(@HeaderProperty("foo") @HeaderAttribute("bar") String name) { - return name + "-channel"; - } - - } - } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java index 78fd163bbb..cc106ad838 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/transformer/AnnotationMethodTransformerAdapterTests.java @@ -25,8 +25,7 @@ import java.util.Properties; import org.junit.Test; import org.springframework.integration.annotation.Transformer; -import org.springframework.integration.handler.annotation.HeaderAttribute; -import org.springframework.integration.handler.annotation.HeaderProperty; +import org.springframework.integration.handler.annotation.Header; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageBuilder; @@ -72,11 +71,11 @@ public class AnnotationMethodTransformerAdapterTests { } @Test - public void testHeaderAttributeAnnotation() throws Exception { + public void testHeaderAnnotation() throws Exception { TestBean testBean = new TestBean(); AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("attributeTest", String.class, Integer.class)); + adapter.setMethod(testBean.getClass().getMethod("headerTest", String.class, Integer.class)); Message message = MessageBuilder.fromPayload("foo") .setHeader("number", 123).build(); Message result = adapter.handle(message); @@ -88,22 +87,21 @@ public class AnnotationMethodTransformerAdapterTests { TestBean testBean = new TestBean(); AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("attributeTest", String.class, Integer.class)); + adapter.setMethod(testBean.getClass().getMethod("headerTest", String.class, Integer.class)); Message message = MessageBuilder.fromPayload("foo") .setHeader("wrong", 123).build(); adapter.handle(message); } @Test - public void testHeaderAnnotation() throws Exception { + public void testOptionalHeaderAnnotation() throws Exception { TestBean testBean = new TestBean(); AnnotationMethodTransformerAdapter adapter = new AnnotationMethodTransformerAdapter(); adapter.setObject(testBean); - adapter.setMethod(testBean.getClass().getMethod("propertyTest", String.class, String.class)); - Message message = MessageBuilder.fromPayload("foo") - .setHeader("suffix", "bar").build(); + adapter.setMethod(testBean.getClass().getMethod("optionalHeaderTest", String.class, Integer.class)); + Message message = MessageBuilder.fromPayload("foo").build(); Message result = adapter.handle(message); - assertEquals("foobar", result.getPayload()); + assertEquals("foonull", result.getPayload()); } @Test @@ -153,13 +151,13 @@ public class AnnotationMethodTransformerAdapterTests { } @Transformer - public String attributeTest(String s, @HeaderAttribute("number") Integer num) { + public String headerTest(String s, @Header("number") Integer num) { return s + num; } @Transformer - public String propertyTest(String s, @HeaderProperty("suffix") String suffix) { - return s + suffix; + public String optionalHeaderTest(String s, @Header(value="number", required=false) Integer num) { + return s + num; } @Transformer