diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/handler/ArgumentArrayMessageMapper.java b/org.springframework.integration/src/main/java/org/springframework/integration/handler/ArgumentArrayMessageMapper.java index 0803fe383c..43cbb0afcb 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/handler/ArgumentArrayMessageMapper.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/handler/ArgumentArrayMessageMapper.java @@ -13,12 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package org.springframework.integration.handler; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.HashMap; +import java.util.Date; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; @@ -31,6 +30,7 @@ import org.springframework.core.MethodParameter; import org.springframework.core.ParameterNameDiscoverer; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.convert.TypeDescriptor; +import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.expression.Expression; @@ -108,16 +108,27 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper parameterList; - private GenericConversionService conversionService; + private static GenericConversionService conversionService; + static{ // see INT-829 + conversionService = new DefaultConversionService(); + conversionService.removeConvertible(Object.class, Map.class); + conversionService.removeConvertible(Map.class, Object.class); + conversionService.removeConvertible(Object.class, String.class); + conversionService.addConverter(new Converter() { + public String convert(Number source) {return null;} + }); + conversionService.addConverter(new Converter() { + public String convert(Date source) {return null;} + }); + } /** * * @param method */ public ArgumentArrayMessageMapper(Method method){ + Assert.notNull(method, "Can not create an instance of " + this.getClass().getName() + " with 'null' value"); this.method = method; parameterList = this.getMethodParameterList(method); - conversionService = new DefaultConversionService(); - conversionService.removeConvertible(Object.class, Map.class); } /** * @@ -125,8 +136,9 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper message){ - this.validateSignatureToMessage(message); + public Object[] fromMessage(Message message){ + Assert.notNull(message, "Can not map Message with 'null' value"); + this.validateMessageMapppings(message); Map messageArgumentsMap = this.mapMessageToArguments(parameterList, message); return messageArgumentsMap.values().toArray(); } @@ -140,34 +152,33 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper messageArgumentsMap = new java.util.LinkedHashMap(); for (MethodParameter methodParameter : parameterList) { String parameterName = methodParameter.getParameterName(); - String expression = parameterName; + String[] expressions = null; Annotation[] annotations = methodParameter.getParameterAnnotations(); Object value = null; if (annotations.length == 0){ - String[] expressions = null; if ("headers".equals(parameterName) || "payload".equals(parameterName)){ expressions = new String[]{parameterName}; } else if ("message".equals(parameterName)){ - expressions = new String[]{"#this", "payload"}; + expressions = new String[]{"#this", "payload"}; // just in case if 'parameterName' is 'message' but type is not Message } else { expressions = new String[]{"payload."+parameterName, "headers."+parameterName, "payload", "headers", "#this"}; } - value = this.processELExpressionAndGetValue(message, methodParameter.getParameterType(), expressions); + value = this.getValueFromMessageBasedOnEL(message, methodParameter.getParameterType(), false, expressions); } else { // for now support only single annotation per parameter if (annotations[0].annotationType().isAssignableFrom(Header.class)){ value = this.mapHeaderThruAnnotation(annotations[0], message, methodParameter, null)[1]; } else if (annotations[0].annotationType().isAssignableFrom(MessageMapping.class)){ - expression = (String) AnnotationUtils.getAnnotationAttributes(annotations[0]).get("expression"); - value = this.processELExpressionAndGetValue(message, methodParameter.getParameterType(), expression); + expressions = new String[]{(String) AnnotationUtils.getAnnotationAttributes(annotations[0]).get("expression")}; + value = this.getValueFromMessageBasedOnEL(message, methodParameter.getParameterType(), true, expressions); } else if (annotations[0].annotationType().isAssignableFrom(Headers.class)){ - expression = "headers"; - value = this.processELExpressionAndGetValue(message, methodParameter.getParameterType(), expression); + expressions = new String[]{"headers"}; + value = this.getValueFromMessageBasedOnEL(message, methodParameter.getParameterType(), false, expressions); } else { throw new IllegalArgumentException("unknown or unsupported annotation: " + annotations[0]); } } - messageArgumentsMap.put(methodParameter.getParameterIndex()+":"+expression, value); + messageArgumentsMap.put(methodParameter.getParameterIndex()+":"+parameterName, value); } return messageArgumentsMap; } @@ -176,7 +187,8 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper toMessage(Object[] arguments){ + public Message toMessage(Object[] arguments){ + Assert.notNull(arguments, "Can not map to 'null' arguments to Message"); if (arguments.length > parameterList.size()) { throw new IllegalArgumentException("Too many parameters provided for: " + method); } else if (arguments.length < parameterList.size()){ @@ -260,12 +272,12 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper message, MethodParameter methodParameter, Object headerValue){ - String value = (String) AnnotationUtils.getValue(header); - String headerName = StringUtils.hasText(value) ? value : methodParameter.getParameterName(); + String valueAttribute = (String) AnnotationUtils.getValue(header); + String headerName = StringUtils.hasText(valueAttribute) ? valueAttribute : methodParameter.getParameterName(); Assert.notNull(headerName, "Can not determine header name. Possible reasons: -debug is being " + - "disabled or header name is not explicitelt prvided in @Header annotation"); + "disabled or header name is not explicitely provided via @Header annotation"); if (message != null){ - headerValue = this.processELExpressionAndGetValue(message, methodParameter.getParameterType(), "headers." + headerName); + headerValue = this.getValueFromMessageBasedOnEL(message, methodParameter.getParameterType(), false, "headers." + headerName); } this.evauateHeader(header, headerName, headerValue, message); return new Object[]{headerName, headerValue}; @@ -293,31 +305,29 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapperProperties conversion break; + } else { + value = null; + } + } catch (Throwable e) { + if (rethrowException){ + throw new MessageHandlingException(message, e); } } } return value; } - /** * * @param method @@ -334,39 +344,36 @@ public class ArgumentArrayMessageMapper implements InboundMessageMapper message){ - if (parameterList.size() > 1){ - if (message != null){ - if (message.getPayload() instanceof Map && parameterList.size() > 1){ - boolean ambigutePayload = true; - for (MethodParameter parameter : parameterList) { - if (parameter.getParameterAnnotations().length > 0 || !parameter.getParameterType().isAssignableFrom(Map.class)){ - ambigutePayload = false; - break; - } - } - if (ambigutePayload){ - throw new MessageHandlingException(message, "Ambiguite signature. Many Payload candidates: " + method); - } - } - } - } - } /** * * @param message * @return */ @SuppressWarnings("unchecked") - private boolean payloadLooksLikeMapOrProperties(Message message){ - Object payload = message.getPayload(); - if (payload instanceof String && ((String)payload).indexOf("=") > -1){ - return true; + private boolean isConvertable(Object value, Class targetType){ + return conversionService.canConvert(value.getClass(), targetType) || + (value instanceof String && ((String)value).indexOf("=") > 1); // to address payload String "foo=bar" + // mapping to Properties/Map + } + /** + * + */ + @SuppressWarnings("unchecked") + public void validateMessageMapppings(Message message){ // validate against maps with no annotations + int counterOfUnnamedMapAttributes = 0; + if (message.getPayload() instanceof Map){ + for (MethodParameter parameter : parameterList) { + if ( parameter.getParameterAnnotations().length == 0 && + (parameter.getParameterType().isAssignableFrom(Properties.class) || parameter.getParameterType().isAssignableFrom(Map.class)) && + !(parameter.getParameterName().equals("payload") || parameter.getParameterName().equals("headers")) ) { + counterOfUnnamedMapAttributes++; + } + } + if (counterOfUnnamedMapAttributes > 1){ + throw new IllegalArgumentException("Ambiguate parameters. Can not determine parameter mappings between Method: " + method + + " and Message: " + message + + ". Try annotating individual parameters with @Headers, @Header or @MessageMapping"); + } } - return (message.getPayload() instanceof Map || message.getPayload() instanceof Properties); } } \ No newline at end of file