INT-1627, INT-1601 added ControlBusMethodFilter to restrict commands to Lifecycle, @ManagedOperation/Attribute, and configurable Executor/Scheduler instances
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user