INT-1627, INT-1601 added ControlBusMethodFilter to restrict commands to Lifecycle, @ManagedOperation/Attribute, and configurable Executor/Scheduler instances

This commit is contained in:
Mark Fisher
2010-11-17 21:49:36 -05:00
parent b1c6c9834f
commit 6ca10847c9
5 changed files with 128 additions and 4 deletions

View File

@@ -13,15 +13,27 @@
package org.springframework.integration.config;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.springframework.context.Lifecycle;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.expression.BeanResolver;
import org.springframework.expression.MethodFilter;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.handler.ExpressionCommandMessageProcessor;
import org.springframework.integration.handler.ServiceActivatingHandler;
import org.springframework.jmx.export.annotation.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.util.CustomizableThreadCreator;
/**
* FactoryBean for creating {@link MessageHandler} instances to handle a message as a SpEL expression.
*
* @author Dave Syer
* @author Mark Fisher
* @since 2.0
*/
public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandlerFactoryBean {
@@ -30,7 +42,7 @@ public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandle
private volatile BeanResolver beanResolver;
private final ExpressionCommandMessageProcessor processor = new ExpressionCommandMessageProcessor();
private final MethodFilter methodFilter = new ControlBusMethodFilter();
public void setSendTimeout(Long sendTimeout) {
@@ -42,15 +54,50 @@ public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandle
}
protected MessageHandler createHandler() {
this.processor.setBeanFactory(this.getBeanFactory());
ExpressionCommandMessageProcessor processor = new ExpressionCommandMessageProcessor(this.methodFilter);
processor.setBeanFactory(this.getBeanFactory());
if (this.beanResolver != null) {
this.processor.setBeanResolver(this.beanResolver);
processor.setBeanResolver(this.beanResolver);
}
ServiceActivatingHandler handler = new ServiceActivatingHandler(this.processor);
ServiceActivatingHandler handler = new ServiceActivatingHandler(processor);
if (this.sendTimeout != null) {
handler.setSendTimeout(this.sendTimeout);
}
return handler;
}
private static class ControlBusMethodFilter implements MethodFilter {
public List<Method> filter(List<Method> methods) {
List<Method> supportedMethods = new ArrayList<Method>();
for (Method method : methods) {
if (this.accept(method)) {
supportedMethods.add(method);
}
}
return supportedMethods;
}
private boolean accept(Method method) {
if (method.getDeclaringClass().equals(Lifecycle.class)) {
return true;
}
if (CustomizableThreadCreator.class.isAssignableFrom(method.getDeclaringClass())
&& (method.getName().startsWith("get")
|| method.getName().startsWith("set")
|| method.getName().startsWith("shutdown"))) {
return true;
}
if (this.hasAnnotation(method, ManagedAttribute.class) || this.hasAnnotation(method, ManagedOperation.class)) {
return true;
}
return false;
}
private boolean hasAnnotation(Method method, Class<? extends Annotation> annotationType) {
return AnnotationUtils.findAnnotation(method, annotationType) != null;
}
}
}

View File

@@ -13,7 +13,20 @@
package org.springframework.integration.handler;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
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.Expression;
import org.springframework.expression.MethodExecutor;
import org.springframework.expression.MethodFilter;
import org.springframework.expression.MethodResolver;
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
import org.springframework.integration.Message;
/**
@@ -26,6 +39,17 @@ import org.springframework.integration.Message;
*/
public class ExpressionCommandMessageProcessor extends AbstractMessageProcessor<Object> {
public ExpressionCommandMessageProcessor() {
}
public ExpressionCommandMessageProcessor(MethodFilter methodFilter) {
if (methodFilter != null) {
MethodResolver methodResolver = new ExpressionCommandMethodResolver(methodFilter);
this.getEvaluationContext().setMethodResolvers(Collections.singletonList(methodResolver));
}
}
/**
* Evaluates the Message payload expression as a command.
* @throws IllegalArgumentException if the payload is not an Exception or String
@@ -41,4 +65,42 @@ public class ExpressionCommandMessageProcessor extends AbstractMessageProcessor<
throw new IllegalArgumentException("Message payload must be an Expression instance or an expression String.");
}
private static class ExpressionCommandMethodResolver extends ReflectiveMethodResolver {
private final MethodFilter methodFilter;
private ExpressionCommandMethodResolver(MethodFilter methodFilter) {
this.methodFilter = methodFilter;
}
public MethodExecutor resolve(EvaluationContext context,
Object targetObject, String name, List<TypeDescriptor> argumentTypes) throws AccessException {
this.validateMethod(targetObject, name, (argumentTypes != null ? argumentTypes.size() : 0));
return super.resolve(context, targetObject, name, argumentTypes);
}
private void validateMethod(Object targetObject, String name, int argumentCount) {
if (this.methodFilter == null) {
return;
}
Class<?> type = (targetObject instanceof Class ? (Class<?>) targetObject : targetObject.getClass());
Method[] methods = type.getMethods();
List<Method> candidates = new ArrayList<Method>();
for (Method method : methods) {
if (method.getName().equals(name) && method.getParameterTypes().length == argumentCount) {
candidates.add(method);
}
}
List<Method> supportedMethods = this.methodFilter.filter(candidates);
if (supportedMethods.size() == 0) {
String methodDescription = (candidates.size() > 0) ? candidates.get(0).toString() : name;
throw new EvaluationException("The method '" + methodDescription + "' is not supported by this command processor. " +
"If using the Control Bus, consider adding @ManagedOperation or @ManagedAttribute.");
}
}
}
}

View File

@@ -29,6 +29,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -60,9 +61,13 @@ public class ControlBusBeanResolverTests {
}
}
public static class Service {
@ManagedOperation
public String convert(String input) {
return "cat";
}
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -51,9 +52,13 @@ public class ControlBusExplicitPollerTests {
assertNull(output.receive(0));
}
public static class Service {
@ManagedOperation
public String convert(String input) {
return "cat";
}
}
}

View File

@@ -26,6 +26,7 @@ import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@@ -51,9 +52,13 @@ public class ControlBusTests {
assertNull(output.receive(0));
}
public static class Service {
@ManagedOperation
public String convert(String input) {
return "cat";
}
}
}