diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/ExpressionControlBusFactoryBean.java b/spring-integration-core/src/main/java/org/springframework/integration/config/ExpressionControlBusFactoryBean.java index aa3cf9f368..f6b571e175 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/ExpressionControlBusFactoryBean.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/ExpressionControlBusFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2011 the original author or authors. + * Copyright 2002-2014 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 @@ -13,21 +13,11 @@ 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.MethodFilter; -import org.springframework.messaging.MessageHandler; +import org.springframework.integration.expression.ControlBusMethodFilter; 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; -import org.springframework.util.ReflectionUtils; +import org.springframework.messaging.MessageHandler; /** * FactoryBean for creating {@link MessageHandler} instances to handle a message as a SpEL expression. @@ -39,9 +29,9 @@ import org.springframework.util.ReflectionUtils; */ public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandlerFactoryBean { - private volatile Long sendTimeout; + private static final MethodFilter methodFilter = new ControlBusMethodFilter(); - private final MethodFilter methodFilter = new ControlBusMethodFilter(); + private volatile Long sendTimeout; public void setSendTimeout(Long sendTimeout) { @@ -51,7 +41,7 @@ public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandle @Override protected MessageHandler createHandler() { ExpressionCommandMessageProcessor processor = - new ExpressionCommandMessageProcessor(this.methodFilter, this.getBeanFactory()); + new ExpressionCommandMessageProcessor(methodFilter, this.getBeanFactory()); ServiceActivatingHandler handler = new ServiceActivatingHandler(processor); if (this.sendTimeout != null) { handler.setSendTimeout(this.sendTimeout); @@ -60,39 +50,4 @@ public class ExpressionControlBusFactoryBean extends AbstractSimpleMessageHandle } - private static class ControlBusMethodFilter implements MethodFilter { - - public List filter(List methods) { - List supportedMethods = new ArrayList(); - for (Method method : methods) { - if (this.accept(method)) { - supportedMethods.add(method); - } - } - return supportedMethods; - } - - private boolean accept(Method method) { - Class declaringClass = method.getDeclaringClass(); - if (Lifecycle.class.isAssignableFrom(declaringClass) - && ReflectionUtils.findMethod(Lifecycle.class, method.getName(), method.getParameterTypes()) != null) { - return true; - } - if (CustomizableThreadCreator.class.isAssignableFrom(declaringClass) - && (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 annotationType) { - return AnnotationUtils.findAnnotation(method, annotationType) != null; - } - } - } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/expression/ControlBusMethodFilter.java b/spring-integration-core/src/main/java/org/springframework/integration/expression/ControlBusMethodFilter.java new file mode 100644 index 0000000000..9872611cc1 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/expression/ControlBusMethodFilter.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014 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.expression; + +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.MethodFilter; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedOperation; +import org.springframework.util.CustomizableThreadCreator; +import org.springframework.util.ReflectionUtils; + +/** + * SpEL {@link MethodFilter} to restrict method invocations to: + *
    + *
  • {@link Lifecycle} components + *
  • {@code get}, {@code set} and {@code shutdown} methods of {@link CustomizableThreadCreator} + *
  • methods with {@link ManagedAttribute} and {@link ManagedOperation} annotations + *
+ * This class isn't designed for target applications and typically is used from {@code ExpressionControlBusFactoryBean}. + * + * @author Mark Fisher + * @author Artem Bilan +* @since 4.0 +*/ +public class ControlBusMethodFilter implements MethodFilter { + + public List filter(List methods) { + List supportedMethods = new ArrayList(); + for (Method method : methods) { + if (this.accept(method)) { + supportedMethods.add(method); + } + } + return supportedMethods; + } + + private boolean accept(Method method) { + Class declaringClass = method.getDeclaringClass(); + if (Lifecycle.class.isAssignableFrom(declaringClass) + && ReflectionUtils.findMethod(Lifecycle.class, method.getName(), method.getParameterTypes()) != null) { + return true; + } + if (CustomizableThreadCreator.class.isAssignableFrom(declaringClass) + && (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 annotationType) { + return AnnotationUtils.findAnnotation(method, annotationType) != null; + } +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java index c13e0d673a..88775122db 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/AbstractMessageRouter.java @@ -18,6 +18,7 @@ package org.springframework.integration.router; import java.util.Collection; +import org.springframework.beans.BeansException; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.integration.channel.NullChannel; @@ -28,6 +29,9 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageDeliveryException; import org.springframework.messaging.MessagingException; +import org.springframework.messaging.core.DestinationResolutionException; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; /** * Base class for all Message Routers. @@ -44,6 +48,8 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { private volatile MessageChannel defaultOutputChannel; + private volatile String defaultOutputChannelName; + private volatile boolean ignoreSendFailures; private volatile boolean applySequence; @@ -65,6 +71,10 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { this.defaultOutputChannel = defaultOutputChannel; } + public void setDefaultOutputChannelName(String defaultOutputChannelName) { + this.defaultOutputChannelName = defaultOutputChannelName; + } + /** * Set the timeout for sending a message to the resolved channel. By default, there is no timeout, meaning the send * will block indefinitely. @@ -128,6 +138,16 @@ public abstract class AbstractMessageRouter extends AbstractMessageHandler { super.onInit(); if (this.getBeanFactory() != null) { this.messagingTemplate.setBeanFactory(this.getBeanFactory()); + if (StringUtils.hasText(this.defaultOutputChannelName)) { + Assert.isNull(this.defaultOutputChannel, "'defaultOutputChannelName' and 'defaultOutputChannel' are mutually exclusive."); + try { + this.defaultOutputChannel = this.getBeanFactory().getBean(this.defaultOutputChannelName, MessageChannel.class); + } + catch (BeansException e) { + throw new DestinationResolutionException("Failed to look up MessageChannel with name '" + + this.defaultOutputChannelName + "' in the BeanFactory."); + } + } } } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java b/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java index 0e3d3a0bd3..f014dfb604 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/router/RecipientListRouter.java @@ -92,7 +92,7 @@ public class RecipientListRouter extends AbstractMessageRouter implements Initia } @Override - public final void onInit() { + public void onInit() { Assert.notEmpty(this.recipients, "recipient list must not be empty"); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/test/util/TestUtils.java b/spring-integration-core/src/test/java/org/springframework/integration/test/util/TestUtils.java index 91b73c8c82..00fb5d894e 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/test/util/TestUtils.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/test/util/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -33,7 +33,6 @@ import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.messaging.MessageHandlingException; import org.springframework.integration.MessageRejectedException; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.context.IntegrationContextUtils; @@ -45,6 +44,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageDeliveryException; import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessageHandlingException; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.Assert; import org.springframework.util.ErrorHandler; @@ -80,7 +80,9 @@ public abstract class TestUtils { @SuppressWarnings("unchecked") public static T getPropertyValue(Object root, String propertyPath, Class type) { Object value = getPropertyValue(root, propertyPath); - Assert.isAssignable(type, value.getClass()); + if (value != null) { + Assert.isAssignable(type, value.getClass()); + } return (T) value; } @@ -158,6 +160,7 @@ public abstract class TestUtils { @SuppressWarnings("rawtypes") public static MessageHandler handlerExpecting(final Matcher messageMatcher) { return new MessageHandler() { + @Override public void handleMessage(Message message) throws MessageRejectedException, MessageHandlingException, MessageDeliveryException { assertThat(message, is(messageMatcher)); } diff --git a/spring-integration-test/src/main/java/org/springframework/integration/test/util/TestUtils.java b/spring-integration-test/src/main/java/org/springframework/integration/test/util/TestUtils.java index 2a6c665b54..3286c87910 100644 --- a/spring-integration-test/src/main/java/org/springframework/integration/test/util/TestUtils.java +++ b/spring-integration-test/src/main/java/org/springframework/integration/test/util/TestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -33,7 +33,6 @@ import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.context.support.GenericApplicationContext; -import org.springframework.messaging.MessageHandlingException; import org.springframework.integration.MessageRejectedException; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.context.IntegrationContextUtils; @@ -45,6 +44,7 @@ import org.springframework.messaging.Message; import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageDeliveryException; import org.springframework.messaging.MessageHandler; +import org.springframework.messaging.MessageHandlingException; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.util.Assert; import org.springframework.util.ErrorHandler; @@ -79,7 +79,9 @@ public abstract class TestUtils { @SuppressWarnings("unchecked") public static T getPropertyValue(Object root, String propertyPath, Class type) { Object value = getPropertyValue(root, propertyPath); - Assert.isAssignable(type, value.getClass()); + if (value != null) { + Assert.isAssignable(type, value.getClass()); + } return (T) value; } @@ -153,6 +155,7 @@ public abstract class TestUtils { @SuppressWarnings("rawtypes") public static MessageHandler handlerExpecting(final Matcher messageMatcher) { return new MessageHandler() { + @Override public void handleMessage(Message message) throws MessageRejectedException, MessageHandlingException, MessageDeliveryException { assertThat(message, is(messageMatcher)); }