INT-3199: Fix Inconsistency for Candidate Methods
JIRA: https://jira.springsource.org/browse/INT-3199 * Set `TypeDescriptor` to `Void.class` as default `targetParameterType` for falling-back * Change `fall-thru` only to one method: * Extract generic type from `Massage` parameter, so now ``` m1(Message<String> message); m2(String payload); ``` are equal by `payload type` and similar POJO causes ambiguity configuration exception INT-3199: Polishing and further fixing * Address PR's comments * Provide more friendly exception message for `Void.class` method * Remove the logic around `@ServiceActivator`: - one part was fixed within INT-3114 - another for `RequestReplyExchanger#exchange` doesn't make sense as it is good candidate * Add fallback to `Iterator.class` method for `Iterable` payloads * Add tests Polishing INT-3199: Add `handlerMessageMethods` * Introduce separate properties: - `handlerMessageMethods` for methods with `Message` parameter - `handlerMethod`, if there is only one candidate after configuration * Polishing several tests according new state of `MessagingMethodInvokerHelper`'s properties Polishing Revert `RequestReplyExchanger` fallback Add `fallbackMessageMethods` processing INT-3199 Polishing Only revert to RequestReplyExchanger if there are ambiguous fallback methods - if the target object is an RRE, it will always have a candidate fallback (exchange). Add more RRE tests. Make ParametersWrapper a public inner class - it's only used within the context of a process() call. Making it public allows SpEL to access its properties without reflection.
This commit is contained in:
committed by
Gary Russell
parent
703b24541f
commit
945cb14407
@@ -28,6 +28,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
@@ -58,6 +59,7 @@ import org.springframework.integration.annotation.Payload;
|
||||
import org.springframework.integration.annotation.Payloads;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodCallback;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
@@ -76,21 +78,31 @@ import org.springframework.util.StringUtils;
|
||||
* @author Gunnar Hillert
|
||||
* @author Soby Chacko
|
||||
* @author Gary Russell
|
||||
* @author Artem Bilan
|
||||
*
|
||||
* @since 2.0
|
||||
*/
|
||||
public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator {
|
||||
|
||||
private static final String CANDIDATE_METHODS = "CANDIDATE_METHODS";
|
||||
|
||||
private static final String CANDIDATE_MESSAGE_METHODS = "CANDIDATE_MESSAGE_METHODS";
|
||||
|
||||
private final Log logger = LogFactory.getLog(this.getClass());
|
||||
|
||||
private final Object targetObject;
|
||||
|
||||
private volatile String displayString;
|
||||
|
||||
private volatile boolean requiresReply;
|
||||
|
||||
private final Map<Class<?>, HandlerMethod> handlerMethods;
|
||||
|
||||
private final Map<Class<?>, HandlerMethod> handlerMessageMethods;
|
||||
|
||||
private final LinkedList<Map<Class<?>, HandlerMethod>> handlerMethodsList;
|
||||
|
||||
private final HandlerMethod handlerMethod;
|
||||
|
||||
private final Class<?> expectedType;
|
||||
|
||||
private final boolean canProcessMessageList;
|
||||
@@ -154,11 +166,12 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
Assert.isTrue(method.getReturnType() != Void.class && method.getReturnType() != Void.TYPE,
|
||||
"method must have a return type");
|
||||
}
|
||||
HandlerMethod handlerMethod = new HandlerMethod(method, canProcessMessageList);
|
||||
Assert.notNull(targetObject, "targetObject must not be null");
|
||||
this.targetObject = targetObject;
|
||||
this.handlerMethods = Collections.<Class<?>, HandlerMethod> singletonMap(handlerMethod.getTargetParameterType()
|
||||
.getObjectType(), handlerMethod);
|
||||
this.handlerMethod = new HandlerMethod(method, canProcessMessageList);
|
||||
this.handlerMethods = null;
|
||||
this.handlerMessageMethods = null;
|
||||
this.handlerMethodsList = null;
|
||||
this.prepareEvaluationContext(this.getEvaluationContext(false), method, annotationType);
|
||||
this.setDisplayString(targetObject, method);
|
||||
}
|
||||
@@ -170,7 +183,32 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
this.expectedType = expectedType;
|
||||
this.targetObject = targetObject;
|
||||
this.requiresReply = expectedType != null;
|
||||
this.handlerMethods = this.findHandlerMethodsForTarget(targetObject, annotationType, methodName, requiresReply);
|
||||
Map<String, Map<Class<?>, HandlerMethod>> handlerMethodsForTarget =
|
||||
this.findHandlerMethodsForTarget(targetObject, annotationType, methodName, requiresReply);
|
||||
Map<Class<?>, HandlerMethod> handlerMethods = handlerMethodsForTarget.get(CANDIDATE_METHODS);
|
||||
Map<Class<?>, HandlerMethod> handlerMessageMethods = handlerMethodsForTarget.get(CANDIDATE_MESSAGE_METHODS);
|
||||
if ((handlerMethods.size() == 1 && handlerMessageMethods.isEmpty()) ||
|
||||
(handlerMessageMethods.size() == 1 && handlerMethods.isEmpty())) {
|
||||
if (handlerMethods.size() == 1) {
|
||||
this.handlerMethod = handlerMethods.values().iterator().next();
|
||||
}
|
||||
else {
|
||||
this.handlerMethod = handlerMessageMethods.values().iterator().next();
|
||||
}
|
||||
this.handlerMethods = null;
|
||||
this.handlerMessageMethods = null;
|
||||
this.handlerMethodsList = null;
|
||||
}
|
||||
else {
|
||||
this.handlerMethod = null;
|
||||
this.handlerMethods = handlerMethods;
|
||||
this.handlerMessageMethods = handlerMessageMethods;
|
||||
this.handlerMethodsList = new LinkedList<Map<Class<?>, HandlerMethod>>();
|
||||
|
||||
//TODO Consider to use global option to determine a precedence of methods
|
||||
this.handlerMethodsList.add(this.handlerMethods);
|
||||
this.handlerMethodsList.add(this.handlerMessageMethods);
|
||||
}
|
||||
this.prepareEvaluationContext(this.getEvaluationContext(false), methodName, annotationType);
|
||||
this.setDisplayString(targetObject, methodName);
|
||||
}
|
||||
@@ -181,7 +219,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
sb.append("." + ((Method) targetMethod).getName());
|
||||
}
|
||||
else if (targetMethod instanceof String) {
|
||||
sb.append("." + (String) targetMethod);
|
||||
sb.append("." + targetMethod);
|
||||
}
|
||||
this.displayString = sb.toString() + "]";
|
||||
}
|
||||
@@ -192,7 +230,8 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
if (method instanceof Method) {
|
||||
context.registerMethodFilter(targetType, new FixedMethodFilter((Method) method));
|
||||
if (expectedType != null) {
|
||||
Assert.state(context.getTypeConverter().canConvert(TypeDescriptor.valueOf(((Method) method).getReturnType()), TypeDescriptor.valueOf(expectedType)),
|
||||
Assert.state(context.getTypeConverter().canConvert(TypeDescriptor.valueOf(((Method) method).getReturnType()),
|
||||
TypeDescriptor.valueOf(expectedType)),
|
||||
"Cannot convert to expected type (" + expectedType + ") from " + method);
|
||||
}
|
||||
}
|
||||
@@ -220,64 +259,48 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
|
||||
private T processInternal(ParametersWrapper parameters) throws Exception {
|
||||
Throwable evaluationException = null;
|
||||
List<HandlerMethod> candidates = this.findHandlerMethodsForParameters(parameters);
|
||||
Assert.state(!candidates.isEmpty(), "No candidate methods found for messages.");
|
||||
for (HandlerMethod candidate : candidates) {
|
||||
try {
|
||||
Expression expression = candidate.getExpression();
|
||||
Class<?> expectedType = this.expectedType != null ? this.expectedType : candidate.method.getReturnType();
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) this.evaluateExpression(expression, parameters, expectedType);
|
||||
if (this.requiresReply) {
|
||||
Assert.notNull(result,
|
||||
"Expression evaluation result was null, but this processor requires a reply.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// keep the first exception
|
||||
catch (EvaluationException e) {
|
||||
if (evaluationException == null) {
|
||||
evaluationException = e.getCause();
|
||||
}
|
||||
if (evaluationException == null) {
|
||||
evaluationException = e;
|
||||
}
|
||||
}
|
||||
catch (MessageHandlingException e) {
|
||||
if (evaluationException == null) {
|
||||
evaluationException = e.getCause();
|
||||
}
|
||||
if (evaluationException == null) {
|
||||
evaluationException = e;
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
if (evaluationException == null) {
|
||||
evaluationException = e;
|
||||
}
|
||||
HandlerMethod candidate = this.findHandlerMethodForParameters(parameters);
|
||||
Assert.notNull(candidate, "No candidate methods found for messages.");
|
||||
Expression expression = candidate.getExpression();
|
||||
Class<?> expectedType = this.expectedType != null ? this.expectedType : candidate.method.getReturnType();
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) this.evaluateExpression(expression, parameters, expectedType);
|
||||
if (this.requiresReply) {
|
||||
Assert.notNull(result,
|
||||
"Expression evaluation result was null, but this processor requires a reply.");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (evaluationException instanceof Exception) {
|
||||
throw (Exception) evaluationException;
|
||||
}
|
||||
else if (evaluationException instanceof Error) {
|
||||
throw (Error) evaluationException;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Cannot process message", evaluationException);
|
||||
catch (Exception e) {
|
||||
Throwable evaluationException = e;
|
||||
if ((e instanceof EvaluationException || e instanceof MessageHandlingException) && e.getCause() != null) {
|
||||
evaluationException = e.getCause();
|
||||
}
|
||||
if (evaluationException instanceof Exception) {
|
||||
throw (Exception) evaluationException;
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Cannot process message", evaluationException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Class<?>, HandlerMethod> findHandlerMethodsForTarget(final Object targetObject,
|
||||
private Map<String, Map<Class<?>, HandlerMethod>> findHandlerMethodsForTarget(final Object targetObject,
|
||||
final Class<? extends Annotation> annotationType, final String methodName, final boolean requiresReply) {
|
||||
|
||||
Map<String, Map<Class<?>, HandlerMethod>> handlerMethods = new HashMap<String, Map<Class<?>, HandlerMethod>>();
|
||||
|
||||
final Map<Class<?>, HandlerMethod> candidateMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final Map<Class<?>, HandlerMethod> candidateMessageMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final Map<Class<?>, HandlerMethod> fallbackMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final Map<Class<?>, HandlerMethod> fallbackMessageMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final AtomicReference<Class<?>> ambiguousFallbackType = new AtomicReference<Class<?>>();
|
||||
final AtomicReference<Class<?>> ambiguousFallbackMessageGenericType = new AtomicReference<Class<?>>();
|
||||
final Class<?> targetClass = this.getTargetClass(targetObject);
|
||||
MethodFilter methodFilter = new UniqueMethodFilter(targetClass);
|
||||
ReflectionUtils.doWithMethods(targetClass, new MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
boolean matchesAnnotation = false;
|
||||
if (method.isBridge()) {
|
||||
@@ -311,37 +334,75 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
return;
|
||||
}
|
||||
Class<?> targetParameterType = handlerMethod.getTargetParameterType().getObjectType();
|
||||
Class<?> targetParameterType = handlerMethod.getTargetParameterType();
|
||||
if (matchesAnnotation || annotationType == null) {
|
||||
Assert.isTrue(!candidateMethods.containsKey(targetParameterType),
|
||||
"Found more than one method match for type [" + targetParameterType + "]");
|
||||
candidateMethods.put(targetParameterType, handlerMethod);
|
||||
if (handlerMethod.isMessageMethod()) {
|
||||
if (candidateMessageMethods.containsKey(targetParameterType)) {
|
||||
throw new IllegalArgumentException("Found more than one method match for type " +
|
||||
"[Message<" + targetParameterType + ">]");
|
||||
}
|
||||
candidateMessageMethods.put(targetParameterType, handlerMethod);
|
||||
}
|
||||
else {
|
||||
if (candidateMethods.containsKey(targetParameterType)) {
|
||||
String exceptionMessage = "Found more than one method match for ";
|
||||
if (Void.class.equals(targetParameterType)) {
|
||||
exceptionMessage += "empty parameter for 'payload'";
|
||||
}
|
||||
else {
|
||||
exceptionMessage += "type [" + targetParameterType + "]";
|
||||
}
|
||||
throw new IllegalArgumentException(exceptionMessage);
|
||||
}
|
||||
candidateMethods.put(targetParameterType, handlerMethod);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (fallbackMethods.containsKey(targetParameterType)) {
|
||||
// we need to check for duplicate type matches,
|
||||
// but only if we end up falling back
|
||||
// and we'll only keep track of the first one
|
||||
ambiguousFallbackType.compareAndSet(null, targetParameterType);
|
||||
if (handlerMethod.isMessageMethod()) {
|
||||
if (fallbackMessageMethods.containsKey(targetParameterType)) {
|
||||
// we need to check for duplicate type matches,
|
||||
// but only if we end up falling back
|
||||
// and we'll only keep track of the first one
|
||||
ambiguousFallbackMessageGenericType.compareAndSet(null, targetParameterType);
|
||||
}
|
||||
fallbackMessageMethods.put(targetParameterType, handlerMethod);
|
||||
}
|
||||
else {
|
||||
if (fallbackMethods.containsKey(targetParameterType)) {
|
||||
// we need to check for duplicate type matches,
|
||||
// but only if we end up falling back
|
||||
// and we'll only keep track of the first one
|
||||
ambiguousFallbackType.compareAndSet(null, targetParameterType);
|
||||
}
|
||||
fallbackMethods.put(targetParameterType, handlerMethod);
|
||||
}
|
||||
fallbackMethods.put(targetParameterType, handlerMethod);
|
||||
}
|
||||
}
|
||||
}, methodFilter);
|
||||
if (!candidateMethods.isEmpty()) {
|
||||
return candidateMethods;
|
||||
|
||||
if (!candidateMethods.isEmpty() || !candidateMessageMethods.isEmpty()) {
|
||||
handlerMethods.put(CANDIDATE_METHODS, candidateMethods);
|
||||
handlerMethods.put(CANDIDATE_MESSAGE_METHODS, candidateMessageMethods);
|
||||
return handlerMethods;
|
||||
}
|
||||
if ((fallbackMethods.isEmpty() || ambiguousFallbackType.get() != null) && ServiceActivator.class.equals(annotationType)) {
|
||||
// a Service Activator can fallback to either MessageHandler.handleMessage(m) or RequestReplyExchanger.exchange(m)
|
||||
if ((ambiguousFallbackType.get() != null
|
||||
|| ambiguousFallbackMessageGenericType.get() != null)
|
||||
&& ServiceActivator.class.equals(annotationType)) {
|
||||
/*
|
||||
* When there are ambiguous fallback methods,
|
||||
* a Service Activator can finally fallback to RequestReplyExchanger.exchange(m).
|
||||
* Ambiguous means > 1 method that takes the same payload type, or > 1 method
|
||||
* that takes a Message with the same generic type.
|
||||
*/
|
||||
List<Method> frameworkMethods = new ArrayList<Method>();
|
||||
Class<?>[] allInterfaces = org.springframework.util.ClassUtils.getAllInterfacesForClass(targetClass);
|
||||
for (Class<?> iface : allInterfaces) {
|
||||
try {
|
||||
if ("org.springframework.integration.gateway.RequestReplyExchanger".equals(iface.getName())) {
|
||||
frameworkMethods.add(targetClass.getMethod("exchange", Message.class));
|
||||
}
|
||||
else if ("org.springframework.integration.core.MessageHandler".equals(iface.getName()) && !requiresReply) {
|
||||
frameworkMethods.add(targetClass.getMethod("handleMessage", Message.class));
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(targetObject.getClass() + ": Ambiguous fallback methods; using RequestReplyExchanger.exchange()");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e) {
|
||||
@@ -350,14 +411,30 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
if (frameworkMethods.size() == 1) {
|
||||
HandlerMethod handlerMethod = new HandlerMethod(frameworkMethods.get(0), canProcessMessageList);
|
||||
return Collections.<Class<?>, HandlerMethod>singletonMap(Object.class, handlerMethod);
|
||||
handlerMethods.put(CANDIDATE_METHODS, Collections.<Class<?>, HandlerMethod>singletonMap(Object.class, handlerMethod));
|
||||
handlerMethods.put(CANDIDATE_MESSAGE_METHODS, candidateMessageMethods);
|
||||
return handlerMethods;
|
||||
}
|
||||
}
|
||||
Assert.notEmpty(fallbackMethods, "Target object of type [" + this.targetObject.getClass()
|
||||
+ "] has no eligible methods for handling Messages.");
|
||||
|
||||
try {
|
||||
Assert.state(!fallbackMethods.isEmpty() || !fallbackMessageMethods.isEmpty(),
|
||||
"Target object of type [" + this.targetObject.getClass() + "] has no eligible methods for handling Messages.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
//TODO backward compatibility
|
||||
throw new IllegalArgumentException(e.getMessage());
|
||||
}
|
||||
|
||||
Assert.isNull(ambiguousFallbackType.get(), "Found ambiguous parameter type [" + ambiguousFallbackType
|
||||
+ "] for method match: " + fallbackMethods.values());
|
||||
return fallbackMethods;
|
||||
Assert.isNull(ambiguousFallbackMessageGenericType.get(),
|
||||
"Found ambiguous parameter type [" + ambiguousFallbackMessageGenericType + "] for method match: "
|
||||
+ fallbackMethods.values());
|
||||
|
||||
handlerMethods.put(CANDIDATE_METHODS, fallbackMethods);
|
||||
handlerMethods.put(CANDIDATE_MESSAGE_METHODS, fallbackMessageMethods);
|
||||
return handlerMethods;
|
||||
}
|
||||
|
||||
private Class<?> getTargetClass(Object targetObject) {
|
||||
@@ -388,22 +465,40 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
return targetClass;
|
||||
}
|
||||
|
||||
private List<HandlerMethod> findHandlerMethodsForParameters(ParametersWrapper parameters) {
|
||||
private HandlerMethod findHandlerMethodForParameters(ParametersWrapper parameters) {
|
||||
if (this.handlerMethod != null) {
|
||||
return this.handlerMethod;
|
||||
}
|
||||
|
||||
final Class<?> payloadType = parameters.getFirstParameterType();
|
||||
|
||||
HandlerMethod closestMatch = this.findClosestMatch(payloadType);
|
||||
if (closestMatch != null) {
|
||||
return Collections.singletonList(closestMatch);
|
||||
return closestMatch;
|
||||
|
||||
}
|
||||
return new ArrayList<HandlerMethod>(this.handlerMethods.values());
|
||||
|
||||
if (Iterable.class.isAssignableFrom(payloadType) && this.handlerMethods.containsKey(Iterator.class)) {
|
||||
return this.handlerMethods.get(Iterator.class);
|
||||
}
|
||||
else {
|
||||
return this.handlerMethods.get(Void.class);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private HandlerMethod findClosestMatch(Class<?> payloadType) {
|
||||
Set<Class<?>> candidates = this.handlerMethods.keySet();
|
||||
Class<?> match = null;
|
||||
if (candidates != null && !candidates.isEmpty()) {
|
||||
match = ClassUtils.findClosestMatch(payloadType, candidates, true);
|
||||
for (Map<Class<?>, HandlerMethod> handlerMethods : handlerMethodsList) {
|
||||
Set<Class<?>> candidates = handlerMethods.keySet();
|
||||
Class<?> match = null;
|
||||
if (!CollectionUtils.isEmpty(candidates)) {
|
||||
match = ClassUtils.findClosestMatch(payloadType, candidates, true);
|
||||
}
|
||||
if (match != null) {
|
||||
return handlerMethods.get(match);
|
||||
}
|
||||
}
|
||||
return (match != null) ? this.handlerMethods.get(match) : null;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean isMethodDefinedOnObjectClass(Method method) {
|
||||
@@ -446,10 +541,13 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
|
||||
private final Expression expression;
|
||||
|
||||
private volatile TypeDescriptor targetParameterType;
|
||||
|
||||
private final boolean canProcessMessageList;
|
||||
|
||||
private volatile TypeDescriptor targetParameterTypeDescriptor;
|
||||
|
||||
private volatile Class<?> targetParameterType = Void.class;
|
||||
|
||||
private volatile boolean messageMethod;
|
||||
|
||||
HandlerMethod(Method method, boolean canProcessMessageList) {
|
||||
this.method = method;
|
||||
@@ -462,10 +560,14 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
return this.expression;
|
||||
}
|
||||
|
||||
TypeDescriptor getTargetParameterType() {
|
||||
Class<?> getTargetParameterType() {
|
||||
return this.targetParameterType;
|
||||
}
|
||||
|
||||
private boolean isMessageMethod() {
|
||||
return messageMethod;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.method.toString();
|
||||
@@ -476,13 +578,12 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
|
||||
boolean hasUnqualifiedMapParameter = false;
|
||||
TypeDescriptor defaultParameterTypeDescriptor = TypeDescriptor.valueOf(List.class);
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
TypeDescriptor parameterTypeDescriptor = new TypeDescriptor(new MethodParameter(method, i));
|
||||
defaultParameterTypeDescriptor = parameterTypeDescriptor;
|
||||
MethodParameter methodParameter = new MethodParameter(method, i);
|
||||
TypeDescriptor parameterTypeDescriptor = new TypeDescriptor(methodParameter);
|
||||
Class<?> parameterType = parameterTypeDescriptor.getObjectType();
|
||||
Annotation mappingAnnotation = findMappingAnnotation(parameterAnnotations[i]);
|
||||
if (mappingAnnotation != null) {
|
||||
@@ -494,7 +595,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
sb.append("." + qualifierExpression);
|
||||
}
|
||||
if (!StringUtils.hasText(qualifierExpression)) {
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
}
|
||||
if (annotationType.equals(Payloads.class)) {
|
||||
@@ -505,7 +606,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
sb.append("]");
|
||||
if (!StringUtils.hasText(qualifierExpression)) {
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
}
|
||||
else if (annotationType.equals(Headers.class)) {
|
||||
@@ -515,17 +616,18 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
else if (annotationType.equals(Header.class)) {
|
||||
Header headerAnnotation = (Header) mappingAnnotation;
|
||||
sb.append(this.determineHeaderExpression(headerAnnotation, new MethodParameter(method, i)));
|
||||
sb.append(this.determineHeaderExpression(headerAnnotation, methodParameter));
|
||||
}
|
||||
}
|
||||
else if (parameterTypeDescriptor.isAssignableTo(messageTypeDescriptor)) {
|
||||
this.messageMethod = true;
|
||||
sb.append("message");
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
else if ((parameterTypeDescriptor.isAssignableTo(messageListTypeDescriptor) || parameterTypeDescriptor
|
||||
.isAssignableTo(messageArrayTypeDescriptor))) {
|
||||
sb.append("messages");
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
else if (Collection.class.isAssignableFrom(parameterType) || parameterType.isArray()) {
|
||||
if (canProcessMessageList) {
|
||||
@@ -534,11 +636,11 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
else {
|
||||
sb.append("payload");
|
||||
}
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
else if (Iterator.class.isAssignableFrom(parameterType)) {
|
||||
if (canProcessMessageList) {
|
||||
Type type = method.getGenericParameterTypes()[0];
|
||||
Type type = method.getGenericParameterTypes()[i];
|
||||
Type parameterizedType = null;
|
||||
if (type instanceof ParameterizedType){
|
||||
parameterizedType = ((ParameterizedType)type).getActualTypeArguments()[0];
|
||||
@@ -546,7 +648,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
parameterizedType = ((ParameterizedType) parameterizedType).getRawType();
|
||||
}
|
||||
}
|
||||
if (parameterizedType != null && Message.class.isAssignableFrom((Class<?>)parameterizedType)){
|
||||
if (parameterizedType != null && Message.class.isAssignableFrom((Class<?>) parameterizedType)){
|
||||
sb.append("messages.iterator()");
|
||||
}
|
||||
else {
|
||||
@@ -556,7 +658,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
else {
|
||||
sb.append("payload.iterator()");
|
||||
}
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
else if (Map.class.isAssignableFrom(parameterType)) {
|
||||
if (Properties.class.isAssignableFrom(parameterType)) {
|
||||
@@ -573,19 +675,19 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
else {
|
||||
sb.append("payload");
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor);
|
||||
this.setExclusiveTargetParameterType(parameterTypeDescriptor, methodParameter);
|
||||
}
|
||||
}
|
||||
if (hasUnqualifiedMapParameter) {
|
||||
if (targetParameterType != null && Map.class.isAssignableFrom(this.targetParameterType.getObjectType())) {
|
||||
if (targetParameterType != null && Map.class.isAssignableFrom(this.targetParameterType)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to determine payload matching parameter due to ambiguous Map typed parameters. "
|
||||
+ "Consider adding the @Payload and or @Headers annotations as appropriate.");
|
||||
}
|
||||
}
|
||||
sb.append(")");
|
||||
if (this.targetParameterType == null) {
|
||||
this.targetParameterType = defaultParameterTypeDescriptor;
|
||||
if (this.targetParameterTypeDescriptor == null) {
|
||||
this.targetParameterTypeDescriptor = TypeDescriptor.valueOf(Void.class);
|
||||
}
|
||||
return EXPRESSION_PARSER.parseExpression(sb.toString());
|
||||
}
|
||||
@@ -638,15 +740,22 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
return headerRetrievalExpression + " != null ? " + fullHeaderExpression + " : " + fallbackExpression;
|
||||
}
|
||||
|
||||
private synchronized void setExclusiveTargetParameterType(TypeDescriptor targetParameterType) {
|
||||
Assert.isNull(this.targetParameterType, "Found more than one parameter type candidate: ["
|
||||
+ this.targetParameterType + "] and [" + targetParameterType + "]");
|
||||
this.targetParameterType = targetParameterType;
|
||||
private synchronized void setExclusiveTargetParameterType(TypeDescriptor targetParameterType, MethodParameter methodParameter) {
|
||||
Assert.isNull(this.targetParameterTypeDescriptor, "Found more than one parameter type candidate: ["
|
||||
+ this.targetParameterTypeDescriptor + "] and [" + targetParameterType + "]");
|
||||
this.targetParameterTypeDescriptor = targetParameterType;
|
||||
if (Message.class.isAssignableFrom(targetParameterType.getObjectType())) {
|
||||
methodParameter.increaseNestingLevel();
|
||||
this.targetParameterType = methodParameter.getNestedParameterType();
|
||||
methodParameter.decreaseNestingLevel();
|
||||
}
|
||||
else {
|
||||
this.targetParameterType = targetParameterType.getObjectType();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class ParametersWrapper {
|
||||
public class ParametersWrapper {
|
||||
|
||||
private final Object payload;
|
||||
|
||||
@@ -692,7 +801,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
if (payload != null) {
|
||||
return payload.getClass();
|
||||
}
|
||||
return Collection.class;
|
||||
return this.messages.getClass();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -308,10 +308,9 @@ public class MethodInvokingMessageGroupProcessorTests {
|
||||
assertTrue(((Message<?>)result).getPayload() instanceof Iterator<?>);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
@Test
|
||||
public void testTwoMethodsWithSameParameterTypesAmbiguous() {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
class AnnotatedParametersAggregator {
|
||||
public Integer and(List<Integer> flags) {
|
||||
int result = 0;
|
||||
@@ -327,7 +326,12 @@ public class MethodInvokingMessageGroupProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
new MethodInvokingMessageGroupProcessor(new AnnotatedParametersAggregator());
|
||||
MessageGroupProcessor processor = new MethodInvokingMessageGroupProcessor(new AnnotatedParametersAggregator());
|
||||
when(messageGroupMock.getMessages()).thenReturn(messagesUpForProcessing);
|
||||
Object result = processor.processMessageGroup(messageGroupMock);
|
||||
Object payload = ((Message<?>) result).getPayload();
|
||||
assertTrue(payload instanceof Integer);
|
||||
assertEquals(7, payload);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -16,9 +16,16 @@
|
||||
|
||||
package org.springframework.integration.config;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.junit.Assert;
|
||||
@@ -49,14 +56,6 @@ import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
* @author Mark Fisher
|
||||
@@ -123,14 +122,14 @@ public class AggregatorParserTests {
|
||||
Object consumer = new DirectFieldAccessor(endpoint).getPropertyValue("handler");
|
||||
assertThat(consumer, is(instanceOf(AggregatingMessageHandler.class)));
|
||||
DirectFieldAccessor accessor = new DirectFieldAccessor(consumer);
|
||||
Map<?, ?> map = (Map<?, ?>) new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(accessor
|
||||
Object handlerMethods = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(accessor
|
||||
.getPropertyValue("outputProcessor")).getPropertyValue("processor")).getPropertyValue("delegate"))
|
||||
.getPropertyValue("handlerMethods");
|
||||
assertEquals("The MethodInvokingAggregator is not injected with the appropriate aggregation method", 1, map
|
||||
.size());
|
||||
assertEquals("The release strategy is not injected with the appropriate method", 1, map.size());
|
||||
assertTrue("Handler methods do not contain correct method: " + map, map.toString().contains(
|
||||
"createSingleMessageFromGroup"));
|
||||
assertNull(handlerMethods);
|
||||
Object handlerMethod = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(accessor
|
||||
.getPropertyValue("outputProcessor")).getPropertyValue("processor")).getPropertyValue("delegate"))
|
||||
.getPropertyValue("handlerMethod");
|
||||
assertTrue(handlerMethod.toString().contains("createSingleMessageFromGroup"));
|
||||
assertEquals("The AggregatorEndpoint is not injected with the appropriate ReleaseStrategy instance",
|
||||
releaseStrategy, accessor.getPropertyValue("releaseStrategy"));
|
||||
assertEquals("The AggregatorEndpoint is not injected with the appropriate CorrelationStrategy instance",
|
||||
@@ -180,10 +179,10 @@ public class AggregatorParserTests {
|
||||
Assert.assertTrue(releaseStrategy instanceof MethodInvokingReleaseStrategy);
|
||||
DirectFieldAccessor releaseStrategyAccessor = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(releaseStrategy)
|
||||
.getPropertyValue("adapter")).getPropertyValue("delegate"));
|
||||
Map<?, ?> map = (Map<?, ?>) releaseStrategyAccessor.getPropertyValue("handlerMethods");
|
||||
assertEquals("The release strategy is not injected with the appropriate method", 1, map.size());
|
||||
assertTrue("Handler methods do not contain correct method: " + map, map.toString()
|
||||
.contains("checkCompleteness"));
|
||||
Object handlerMethods = releaseStrategyAccessor.getPropertyValue("handlerMethods");
|
||||
assertNull(handlerMethods);
|
||||
Object handlerMethod = releaseStrategyAccessor.getPropertyValue("handlerMethod");
|
||||
assertTrue(handlerMethod.toString().contains("checkCompleteness"));
|
||||
input.send(createMessage(1l, "correllationId", 4, 0, null));
|
||||
input.send(createMessage(2l, "correllationId", 4, 1, null));
|
||||
input.send(createMessage(3l, "correllationId", 4, 2, null));
|
||||
@@ -205,10 +204,10 @@ public class AggregatorParserTests {
|
||||
Assert.assertTrue(releaseStrategy instanceof MethodInvokingReleaseStrategy);
|
||||
DirectFieldAccessor releaseStrategyAccessor = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(releaseStrategy)
|
||||
.getPropertyValue("adapter")).getPropertyValue("delegate"));
|
||||
Map<?, ?> map = (Map<?, ?>) releaseStrategyAccessor.getPropertyValue("handlerMethods");
|
||||
assertEquals("The release strategy is not injected with the appropriate method", 1, map.size());
|
||||
assertTrue("Handler methods do not contain correct method: " + map, map.toString()
|
||||
.contains("checkCompleteness"));
|
||||
Object handlerMethods = releaseStrategyAccessor.getPropertyValue("handlerMethods");
|
||||
assertNull(handlerMethods);
|
||||
Object handlerMethod = releaseStrategyAccessor.getPropertyValue("handlerMethod");
|
||||
assertTrue(handlerMethod.toString().contains("checkCompleteness"));
|
||||
input.send(createMessage(1l, "correllationId", 4, 0, null));
|
||||
input.send(createMessage(2l, "correllationId", 4, 1, null));
|
||||
input.send(createMessage(3l, "correllationId", 4, 2, null));
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -16,11 +16,18 @@
|
||||
|
||||
package org.springframework.integration.config.annotation;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.DirectFieldAccessor;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
@@ -35,16 +42,10 @@ import org.springframework.integration.support.channel.BeanFactoryChannelResolve
|
||||
import org.springframework.integration.support.channel.ChannelResolver;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import static org.springframework.integration.test.util.TestUtils.getPropertyValue;
|
||||
|
||||
/**
|
||||
* @author Marius Bogoevici
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class AggregatorAnnotationTests {
|
||||
|
||||
@@ -86,13 +87,12 @@ public class AggregatorAnnotationTests {
|
||||
Object releaseStrategy = getPropertyValue(aggregator, "releaseStrategy");
|
||||
Assert.assertTrue(releaseStrategy instanceof MethodInvokingReleaseStrategy);
|
||||
MethodInvokingReleaseStrategy releaseStrategyAdapter = (MethodInvokingReleaseStrategy) releaseStrategy;
|
||||
Map<?, ?> map = (Map<?, ?>) new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(releaseStrategyAdapter)
|
||||
Object handlerMethods = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(releaseStrategyAdapter)
|
||||
.getPropertyValue("adapter")).getPropertyValue("delegate")).getPropertyValue("handlerMethods");
|
||||
assertEquals("The MethodInvokingAggregator is not injected with the appropriate aggregation method", 1, map
|
||||
.size());
|
||||
assertEquals("The release strategy is not injected with the appropriate method", 1, map.size());
|
||||
assertTrue("Handler methods do not contain correct method: " + map, map.toString()
|
||||
.contains("completionChecker"));
|
||||
assertNull(handlerMethods);
|
||||
Object handlerMethod = new DirectFieldAccessor(new DirectFieldAccessor(new DirectFieldAccessor(releaseStrategyAdapter)
|
||||
.getPropertyValue("adapter")).getPropertyValue("delegate")).getPropertyValue("handlerMethod");
|
||||
assertTrue(handlerMethod.toString().contains("completionChecker"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -108,9 +108,10 @@ public class AggregatorAnnotationTests {
|
||||
.getPropertyValue("processor")).getPropertyValue("delegate"));
|
||||
Object targetObject = processorAccessor.getPropertyValue("targetObject");
|
||||
assertSame(context.getBean(endpointName), targetObject);
|
||||
Map<?, ?> handlerMethods = (Map<?, ?>) processorAccessor.getPropertyValue("handlerMethods");
|
||||
assertEquals(1, handlerMethods.size());
|
||||
DirectFieldAccessor handlerMethodAccessor = new DirectFieldAccessor(handlerMethods.values().iterator().next());
|
||||
assertNull(processorAccessor.getPropertyValue("handlerMethods"));
|
||||
Object handlerMethod = processorAccessor.getPropertyValue("handlerMethod");
|
||||
assertNotNull(handlerMethod);
|
||||
DirectFieldAccessor handlerMethodAccessor = new DirectFieldAccessor(handlerMethod);
|
||||
Method completionCheckerMethod = (Method) handlerMethodAccessor.getPropertyValue("method");
|
||||
assertEquals("correlate", completionCheckerMethod.getName());
|
||||
}
|
||||
|
||||
@@ -17,12 +17,18 @@
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.gateway.RequestReplyExchanger;
|
||||
import org.springframework.integration.handler.ServiceActivatingHandler;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
|
||||
@@ -66,6 +72,166 @@ public class ServiceActivatorMethodResolutionTests {
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testRequestReplyExchanger() {
|
||||
RequestReplyExchanger testBean = new RequestReplyExchanger() {
|
||||
|
||||
@Override
|
||||
public Message<?> exchange(Message<?> request) {
|
||||
return request;
|
||||
}
|
||||
};
|
||||
|
||||
final Message<?> test = new GenericMessage<Object>("foo");
|
||||
|
||||
ServiceActivatingHandler serviceActivator = new ServiceActivatingHandler(testBean) {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> message) {
|
||||
Object o = super.handleRequestMessage(message);
|
||||
assertSame(test, o);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
serviceActivator.handleMessage(test);
|
||||
}
|
||||
|
||||
@Test
|
||||
/*
|
||||
* A handler and message handler fallback (RRE); don't force RRE
|
||||
*/
|
||||
public void testRequestReplyExchangerSeveralMethods() {
|
||||
RequestReplyExchanger testBean = new RequestReplyExchanger() {
|
||||
|
||||
@Override
|
||||
public Message<?> exchange(Message<?> request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String foo(String request) {
|
||||
return request.toUpperCase();
|
||||
}
|
||||
|
||||
};
|
||||
ServiceActivatingHandler serviceActivator = new ServiceActivatingHandler(testBean);
|
||||
PollableChannel outputChannel = new QueueChannel();
|
||||
serviceActivator.setOutputChannel(outputChannel);
|
||||
|
||||
Message<?> test = new GenericMessage<Object>(new Date());
|
||||
serviceActivator.handleMessage(test);
|
||||
assertEquals(test, outputChannel.receive(10));
|
||||
|
||||
test = new GenericMessage<Object>("foo");
|
||||
serviceActivator.handleMessage(test);
|
||||
assertEquals("FOO", outputChannel.receive(10).getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
/*
|
||||
* No handler fallback methods; don't force RRE
|
||||
*/
|
||||
public void testRequestReplyExchangerWithGenericMessageMethod() {
|
||||
RequestReplyExchanger testBean = new RequestReplyExchanger() {
|
||||
|
||||
@Override
|
||||
public Message<?> exchange(Message<?> request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String foo(Message<String> request) {
|
||||
return request.getPayload().toUpperCase();
|
||||
}
|
||||
|
||||
};
|
||||
ServiceActivatingHandler serviceActivator = new ServiceActivatingHandler(testBean);
|
||||
PollableChannel outputChannel = new QueueChannel();
|
||||
serviceActivator.setOutputChannel(outputChannel);
|
||||
|
||||
Message<?> test = new GenericMessage<Object>(new Date());
|
||||
serviceActivator.handleMessage(test);
|
||||
assertEquals(test, outputChannel.receive(10));
|
||||
|
||||
test = new GenericMessage<Object>("foo");
|
||||
serviceActivator.handleMessage(test);
|
||||
assertEquals("FOO", outputChannel.receive(10).getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
/*
|
||||
* No handler fallback methods; ambiguous message handler fallbacks; force RRE
|
||||
*/
|
||||
public void testRequestReplyExchangerWithAmbiguousGenericMessageMethod() {
|
||||
RequestReplyExchanger testBean = new RequestReplyExchanger() {
|
||||
|
||||
@Override
|
||||
public Message<?> exchange(Message<?> request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String foo(Message<String> request) {
|
||||
return request.getPayload().toUpperCase();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String bar(Message<String> request) {
|
||||
return request.getPayload().toUpperCase();
|
||||
}
|
||||
|
||||
};
|
||||
ServiceActivatingHandler serviceActivator = new ServiceActivatingHandler(testBean);
|
||||
PollableChannel outputChannel = new QueueChannel();
|
||||
serviceActivator.setOutputChannel(outputChannel);
|
||||
|
||||
Message<?> test = new GenericMessage<Object>(new Date());
|
||||
serviceActivator.handleMessage(test);
|
||||
assertEquals(test, outputChannel.receive(10));
|
||||
|
||||
test = new GenericMessage<Object>("foo");
|
||||
serviceActivator.handleMessage(test);
|
||||
assertNotEquals("FOO", outputChannel.receive(10).getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
/*
|
||||
* One message handler fallback method (RRE); ambiguous handler fallbacks; force RRE
|
||||
*/
|
||||
public void testRequestReplyExchangerWithAmbiguousMethod() {
|
||||
RequestReplyExchanger testBean = new RequestReplyExchanger() {
|
||||
|
||||
@Override
|
||||
public Message<?> exchange(Message<?> request) {
|
||||
return request;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String foo(String request) {
|
||||
return request.toUpperCase();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public String bar(String request) {
|
||||
return request.toUpperCase();
|
||||
}
|
||||
|
||||
};
|
||||
ServiceActivatingHandler serviceActivator = new ServiceActivatingHandler(testBean);
|
||||
PollableChannel outputChannel = new QueueChannel();
|
||||
serviceActivator.setOutputChannel(outputChannel);
|
||||
|
||||
Message<?> test = new GenericMessage<Object>(new Date());
|
||||
serviceActivator.handleMessage(test);
|
||||
assertEquals(test, outputChannel.receive(10));
|
||||
|
||||
test = new GenericMessage<Object>("foo");
|
||||
serviceActivator.handleMessage(test);
|
||||
assertNotEquals("FOO", outputChannel.receive(10).getPayload());
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private static class SingleAnnotationTestBean {
|
||||
|
||||
|
||||
@@ -20,18 +20,23 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.hamcrest.TypeSafeMatcher;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import org.springframework.expression.spel.SpelEvaluationException;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
@@ -355,6 +360,135 @@ public class MethodInvokingMessageProcessorTests {
|
||||
assertSame(RequestReplyExchanger.class, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3199GenericTypeResolvingAndObjectMethod() throws Exception {
|
||||
|
||||
class Foo {
|
||||
|
||||
public String handleMessage(Message<Number> message) {
|
||||
return "" + (message.getPayload().intValue() * 2);
|
||||
}
|
||||
|
||||
public String objectMethod(Integer foo) {
|
||||
return foo.toString();
|
||||
}
|
||||
|
||||
public String voidMethod() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MessagingMethodInvokerHelper helper = new MessagingMethodInvokerHelper(new Foo(), (String) null, false);
|
||||
assertEquals("4", helper.process(new GenericMessage<Object>(2L)));
|
||||
assertEquals("1", helper.process(new GenericMessage<Object>(1)));
|
||||
assertEquals("foo", helper.process(new GenericMessage<Object>(new Date())));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3199GettersAmbiguity() throws Exception {
|
||||
|
||||
class Foo {
|
||||
|
||||
public String getFoo() {
|
||||
return "foo";
|
||||
}
|
||||
|
||||
public String getBar() {
|
||||
return "foo";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
new MessagingMethodInvokerHelper(new Foo(), (String) null, false);
|
||||
fail("IllegalArgumentException expected");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, Matchers.instanceOf(IllegalArgumentException.class));
|
||||
assertEquals("Found more than one method match for empty parameter for 'payload'", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3199MessageMethods() throws Exception {
|
||||
|
||||
class Foo {
|
||||
|
||||
public String m1(Message<String> message) {
|
||||
return message.getPayload();
|
||||
}
|
||||
|
||||
public Integer m2(Message<Integer> message) {
|
||||
return message.getPayload();
|
||||
}
|
||||
|
||||
public Object m3(Message<?> message) {
|
||||
return message.getPayload();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Foo targetObject = new Foo();
|
||||
|
||||
MessagingMethodInvokerHelper helper = new MessagingMethodInvokerHelper(targetObject, (String) null, false);
|
||||
assertEquals("foo", helper.process(new GenericMessage<Object>("foo")));
|
||||
assertEquals(1, helper.process(new GenericMessage<Object>(1)));
|
||||
assertEquals(targetObject, helper.process(new GenericMessage<Object>(targetObject)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3199TypedMethods() throws Exception {
|
||||
|
||||
class Foo {
|
||||
|
||||
public String m1(String payload) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public Integer m2(Integer payload) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public Object m3(Object payload) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Foo targetObject = new Foo();
|
||||
|
||||
MessagingMethodInvokerHelper helper = new MessagingMethodInvokerHelper(targetObject, (String) null, false);
|
||||
assertEquals("foo", helper.process(new GenericMessage<Object>("foo")));
|
||||
assertEquals(1, helper.process(new GenericMessage<Object>(1)));
|
||||
assertEquals(targetObject, helper.process(new GenericMessage<Object>(targetObject)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInt3199PrecedenceOfCandidates() throws Exception {
|
||||
|
||||
class Foo {
|
||||
|
||||
public Object m1(Message<String> message) {
|
||||
fail("This method must not be invoked");
|
||||
return message;
|
||||
}
|
||||
|
||||
public Object m2(String payload) {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public Object m3() {
|
||||
return "FOO";
|
||||
}
|
||||
}
|
||||
|
||||
Foo targetObject = new Foo();
|
||||
|
||||
MessagingMethodInvokerHelper helper = new MessagingMethodInvokerHelper(targetObject, (String) null, false);
|
||||
assertEquals("foo", helper.process(new GenericMessage<Object>("foo")));
|
||||
assertEquals("FOO", helper.process(new GenericMessage<Object>(targetObject)));
|
||||
}
|
||||
|
||||
private static class ExceptionCauseMatcher extends TypeSafeMatcher<Exception> {
|
||||
private Throwable cause;
|
||||
|
||||
@@ -528,5 +662,7 @@ public class MethodInvokingMessageProcessorTests {
|
||||
this.lastArg = s;
|
||||
return s;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -749,14 +749,14 @@ public class PayloadAndHeaderMappingTests {
|
||||
this.lastHeaders.put("foo", header);
|
||||
this.lastPayload = payload;
|
||||
}
|
||||
|
||||
|
||||
public void payloadMapAndHeaderStrings(Map payload, @Header("foo") String header1, @Header("bar") String header2) {
|
||||
this.lastHeaders = new HashMap<String, String>();
|
||||
this.lastHeaders.put("foo", header1);
|
||||
this.lastHeaders.put("bar", header2);
|
||||
this.lastPayload = payload;
|
||||
this.lastPayload = payload;
|
||||
}
|
||||
|
||||
|
||||
public void payloadMapAndHeaderMap(Map payload, @Headers Map headers) {
|
||||
this.lastHeaders = headers;
|
||||
this.lastPayload = payload;
|
||||
@@ -771,7 +771,7 @@ public class PayloadAndHeaderMappingTests {
|
||||
this.lastHeaders = headers;
|
||||
this.lastPayload = payload;
|
||||
}
|
||||
|
||||
|
||||
public void headerPropertiesPayloadMapAndStringHeader(@Headers Properties headers, Map payload, @Header("foo") String header) {
|
||||
this.lastHeaders = headers;
|
||||
this.lastHeaders.put("foo2", header);
|
||||
|
||||
Reference in New Issue
Block a user