INT-991 Now using Spring 3.0.1 SpEL MethodFilters

This commit is contained in:
Mark Fisher
2010-02-19 23:52:28 +00:00
parent 7abbb92fc4
commit 9c61503032
5 changed files with 25 additions and 209 deletions

View File

@@ -1,158 +0,0 @@
/*
* Copyright 2002-2009 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;
import java.lang.reflect.Method;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.expression.AccessException;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.EvaluationException;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.TypeConverter;
import org.springframework.expression.TypedValue;
import org.springframework.expression.spel.SpelEvaluationException;
import org.springframework.expression.spel.SpelMessage;
import org.springframework.expression.spel.support.ReflectionHelper;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* An adaptation of the SpEL ReflectiveMethodResolver with the simple addition of a {@link MethodFilter}.
*
* @author Andy Clement
* @author Mark Fisher
* @since 2.0
*/
class FilteringReflectiveMethodResolver implements MethodResolver {
private final MethodFilter methodFilter;
private final Class<?>[] filteredTypes;
public FilteringReflectiveMethodResolver(MethodFilter methodFilter, Class<?> ... filteredTypes) {
Assert.notNull(methodFilter, "methodFilter must not be null");
this.methodFilter = methodFilter;
this.filteredTypes = filteredTypes;
}
public MethodExecutor resolve(EvaluationContext context, Object targetObject, String name, Class<?>[] argumentTypes) throws AccessException {
try {
TypeConverter typeConverter = context.getTypeConverter();
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
// this is the only addition for this implementation:
Method[] methods = ObjectUtils.containsElement(this.filteredTypes, type) ?
this.methodFilter.filter(type.getMethods()) : type.getMethods();
Method closeMatch = null;
int[] argsToConvert = null;
boolean multipleOptions = false;
Method matchRequiringConversion = null;
for (Method method : methods) {
if (method.isBridge()) {
continue;
}
if (method.getName().equals(name)) {
ReflectionHelper.ArgumentsMatchInfo matchInfo = null;
if (method.isVarArgs() && argumentTypes.length >= (method.getParameterTypes().length - 1)) {
// *sigh* complicated
matchInfo = ReflectionHelper.compareArgumentsVarargs(method.getParameterTypes(), argumentTypes, typeConverter);
} else if (method.getParameterTypes().length == argumentTypes.length) {
// name and parameter number match, check the arguments
matchInfo = ReflectionHelper.compareArguments(method.getParameterTypes(), argumentTypes, typeConverter);
}
if (matchInfo != null) {
if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.EXACT) {
return new ReflectiveMethodExecutor(method, null);
}
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.CLOSE) {
closeMatch = method;
}
else if (matchInfo.kind == ReflectionHelper.ArgsMatchKind.REQUIRES_CONVERSION) {
if (matchRequiringConversion != null) {
multipleOptions = true;
}
argsToConvert = matchInfo.argsRequiringConversion;
matchRequiringConversion = method;
}
}
}
}
if (closeMatch != null) {
return new ReflectiveMethodExecutor(closeMatch, null);
}
else if (matchRequiringConversion != null) {
if (multipleOptions) {
throw new SpelEvaluationException(SpelMessage.MULTIPLE_POSSIBLE_METHODS, name);
}
return new ReflectiveMethodExecutor(matchRequiringConversion, argsToConvert);
}
else {
return null;
}
}
catch (EvaluationException ex) {
throw new AccessException("Failed to resolve method", ex);
}
}
/**
* This class is copied also, since the original has package-only access.
*
* @author Andy Clement
*/
private static class ReflectiveMethodExecutor implements MethodExecutor {
private final Method method;
// When the method was found, we will have determined if arguments need to be converted for it
// to be invoked. Conversion won't be cheap so let's only do it if necessary.
private final int[] argsRequiringConversion;
public ReflectiveMethodExecutor(Method theMethod, int[] argumentsRequiringConversion) {
this.method = theMethod;
this.argsRequiringConversion = argumentsRequiringConversion;
}
public TypedValue execute(EvaluationContext context, Object target, Object... arguments) throws AccessException {
try {
if (this.argsRequiringConversion != null && arguments != null) {
ReflectionHelper.convertArguments(this.method.getParameterTypes(), this.method.isVarArgs(),
context.getTypeConverter(), this.argsRequiringConversion, arguments);
}
if (this.method.isVarArgs()) {
arguments = ReflectionHelper.setupArgumentsForVarargsInvocation(this.method.getParameterTypes(), arguments);
}
ReflectionUtils.makeAccessible(this.method);
return new TypedValue(this.method.invoke(target, arguments), new TypeDescriptor(new MethodParameter(method,-1)));
} catch (Exception ex) {
throw new AccessException("Problem invoking method: " + this.method, ex);
}
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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,11 +17,18 @@
package org.springframework.integration.handler;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.springframework.expression.MethodFilter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* A {@link MethodFilter} implementation that will always return
* the same Method instance within a single-element list if it is
* present in the candidate list. If the Method is not present
* in the candidate list, it will return an empty list.
*
* @author Mark Fisher
* @since 2.0
*/
@@ -36,11 +43,11 @@ class FixedHandlerMethodFilter implements MethodFilter {
}
public Method[] filter(Method[] methods) {
if (ObjectUtils.containsElement(methods, this.method)) {
return new Method[] { this.method };
public List<Method> filter(List<Method> methods) {
if (methods != null && methods.contains(this.method)) {
return Collections.singletonList(this.method);
}
return new Method[0];
return Collections.<Method>emptyList();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.List;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.MethodFilter;
import org.springframework.util.StringUtils;
/**
@@ -52,7 +53,7 @@ class HandlerMethodFilter implements MethodFilter {
}
public Method[] filter(Method[] methods) {
public List<Method> filter(List<Method> methods) {
List<Method> annotatedCandidates = new ArrayList<Method>();
List<Method> fallbackCandidates = new ArrayList<Method>();
for (Method method : methods) {
@@ -72,8 +73,7 @@ class HandlerMethodFilter implements MethodFilter {
fallbackCandidates.add(method);
}
}
return (!annotatedCandidates.isEmpty() ? annotatedCandidates.toArray(new Method[annotatedCandidates.size()])
: fallbackCandidates.toArray(new Method[fallbackCandidates.size()]));
return (!annotatedCandidates.isEmpty()) ? annotatedCandidates : fallbackCandidates;
}
}

View File

@@ -1,31 +0,0 @@
/*
* Copyright 2002-2009 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;
import java.lang.reflect.Method;
/**
* Strategy interface for filtering the available Methods prior to resolution.
*
* @author Mark Fisher
* @since 2.0
*/
interface MethodFilter {
Method[] filter(Method[] methods);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2009 the original author or authors.
* Copyright 2002-2010 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.
@@ -31,6 +31,7 @@ import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.expression.MapAccessor;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
@@ -106,7 +107,7 @@ public class MethodInvokingMessageProcessor implements MessageProcessor {
Assert.notNull(targetObject, "targetObject must not be null");
this.targetObject = targetObject;
this.handlerMethods = Collections.<Class<?>, HandlerMethod>singletonMap(handlerMethod.getTargetParameterType(), handlerMethod);
this.evaluationContext = this.createEvaluationContext(targetObject, method, annotationType);
this.evaluationContext = this.createEvaluationContext(method, annotationType);
this.setDisplayString(targetObject, method);
}
@@ -119,7 +120,7 @@ public class MethodInvokingMessageProcessor implements MessageProcessor {
this.targetObject = targetObject;
this.requiresReply = requiresReply;
this.handlerMethods = this.findHandlerMethodsForTarget(targetObject, annotationType, methodName, requiresReply);
this.evaluationContext = this.createEvaluationContext(targetObject, methodName, annotationType);
this.evaluationContext = this.createEvaluationContext(methodName, annotationType);
this.setDisplayString(targetObject, methodName);
}
@@ -135,18 +136,15 @@ public class MethodInvokingMessageProcessor implements MessageProcessor {
this.displayString = sb.toString() + "]";
}
private EvaluationContext createEvaluationContext(Object targetObject, Object method, Class<? extends Annotation> annotationType) {
private EvaluationContext createEvaluationContext(Object method, Class<? extends Annotation> annotationType) {
StandardEvaluationContext context = new StandardEvaluationContext();
// TODO: StandardEvaluationContext may soon provide a better way to *replace* the MethodResolver
context.getMethodResolvers().clear();
Class<?> targetType = AopUtils.getTargetClass(this.targetObject);
if (method instanceof Method) {
context.getMethodResolvers().add(new FilteringReflectiveMethodResolver(
new FixedHandlerMethodFilter((Method) method), targetType));
context.registerMethodFilter(targetType, new FixedHandlerMethodFilter((Method) method));
}
else if (method == null || method instanceof String) {
context.getMethodResolvers().add(new FilteringReflectiveMethodResolver(
new HandlerMethodFilter(annotationType, (String) method, this.requiresReply), targetType));
context.registerMethodFilter(targetType,
new HandlerMethodFilter(annotationType, (String) method, this.requiresReply));
}
context.addPropertyAccessor(new MapAccessor());
// TODO: Enable configuration of an integration ConversionService bean to be used here,