diff --git a/spring-integration-core/src/main/java/org/springframework/integration/bus/MessageBus.java b/spring-integration-core/src/main/java/org/springframework/integration/bus/MessageBus.java index 22c69adcfc..4c8c96f0d0 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/bus/MessageBus.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/bus/MessageBus.java @@ -29,7 +29,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.Lifecycle; import org.springframework.integration.MessagingConfigurationException; -import org.springframework.integration.MessagingException; import org.springframework.integration.adapter.SourceAdapter; import org.springframework.integration.channel.ChannelRegistry; import org.springframework.integration.channel.ChannelRegistryAware; @@ -95,7 +94,7 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif public void setMessagingTaskScheduler(MessagingTaskScheduler taskScheduler) { Assert.notNull(taskScheduler, "task scheduler must not be null"); if (taskScheduler instanceof SimpleMessagingTaskScheduler) { - ((SimpleMessagingTaskScheduler) this.taskScheduler).setCorePoolSize(dispatcherPoolSize); + ((SimpleMessagingTaskScheduler) taskScheduler).setCorePoolSize(dispatcherPoolSize); } this.taskScheduler = taskScheduler; } @@ -150,11 +149,13 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif if (this.getInvalidMessageChannel() == null) { this.setInvalidMessageChannel(new SimpleChannel(Integer.MAX_VALUE)); } - initScheduler(); + if (this.taskScheduler == null) { + this.setMessagingTaskScheduler(createDefaultScheduler()); + } this.initialized = true; } - private void initScheduler() { + private MessagingTaskScheduler createDefaultScheduler() { CustomizableThreadFactory threadFactory = new CustomizableThreadFactory(); threadFactory.setThreadNamePrefix("dispatcher-executor-"); threadFactory.setThreadGroup(new ThreadGroup("dispatcher-executors")); @@ -163,7 +164,7 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif scheduler.setThreadFactory(threadFactory); scheduler.setErrorHandler(new MessagePublishingErrorHandler(this.getInvalidMessageChannel())); scheduler.afterPropertiesSet(); - this.taskScheduler = scheduler; + return scheduler; } public MessageChannel getInvalidMessageChannel() { @@ -248,7 +249,7 @@ public class MessageBus implements ChannelRegistry, ApplicationContextAware, Lif channel = this.lookupChannel(channelName); if (channel == null) { if (this.autoCreateChannels == false) { - throw new MessagingException("Cannot activate subscription, unknown channel '" + channelName + + throw new MessagingConfigurationException("Cannot activate subscription, unknown channel '" + channelName + "'. Consider enabling the 'autoCreateChannels' option for the message bus."); } if (this.logger.isInfoEnabled()) { diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/MessageBusParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/MessageBusParser.java index dddad74466..c6643a8cb2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/MessageBusParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/MessageBusParser.java @@ -20,9 +20,12 @@ import org.w3c.dom.Element; import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.core.Conventions; import org.springframework.integration.bus.MessageBus; +import org.springframework.util.StringUtils; /** * Parser for the message-bus element of the integration namespace. @@ -33,6 +36,9 @@ public class MessageBusParser extends AbstractSimpleBeanDefinitionParser { public static final String MESSAGE_BUS_BEAN_NAME = "org.springframework.integration.bus.internalMessageBus"; + private static final String INVALID_MESSAGE_CHANNEL_ATTRIBUTE = "invalid-message-channel"; + + @Override protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) throws BeanDefinitionStoreException { @@ -44,4 +50,18 @@ public class MessageBusParser extends AbstractSimpleBeanDefinitionParser { return MessageBus.class; } + @Override + protected boolean isEligibleAttribute(String attributeName) { + return !INVALID_MESSAGE_CHANNEL_ATTRIBUTE.equals(attributeName) && super.isEligibleAttribute(attributeName); + } + + @Override + protected void postProcess(BeanDefinitionBuilder beanDefinition, Element element) { + String invalidMessageChannelRef = element.getAttribute(INVALID_MESSAGE_CHANNEL_ATTRIBUTE); + if (StringUtils.hasText(invalidMessageChannelRef)) { + beanDefinition.addPropertyReference(Conventions.attributeNameToPropertyName( + INVALID_MESSAGE_CHANNEL_ATTRIBUTE), invalidMessageChannelRef); + } + } + } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/MessageBusParserTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/MessageBusParserTests.java new file mode 100644 index 0000000000..4bc0423359 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/MessageBusParserTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2002-2007 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.config; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; + +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; +import org.springframework.integration.MessagingConfigurationException; +import org.springframework.integration.bus.MessageBus; +import org.springframework.integration.bus.Subscription; +import org.springframework.integration.handler.TestHandlers; + +/** + * @author Mark Fisher + */ +public class MessageBusParserTests { + + @Test + public void testErrorChannelReference() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "messageBusWithErrorChannelReference.xml", this.getClass()); + MessageBus bus = (MessageBus) context.getBean(MessageBusParser.MESSAGE_BUS_BEAN_NAME); + bus.initialize(); + assertEquals(context.getBean("errorMessages"), bus.getInvalidMessageChannel()); + } + + @Test + public void testDefaultErrorChannel() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "messageBusWithDefaults.xml", this.getClass()); + MessageBus bus = (MessageBus) context.getBean(MessageBusParser.MESSAGE_BUS_BEAN_NAME); + bus.initialize(); + assertNotNull("bus should have created a default error channel", bus.getInvalidMessageChannel()); + } + + @Test(expected=MessagingConfigurationException.class) + public void testAutoCreateChannelsDisabledByDefault() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "messageBusWithDefaults.xml", this.getClass()); + MessageBus bus = (MessageBus) context.getBean(MessageBusParser.MESSAGE_BUS_BEAN_NAME); + Subscription subscription = new Subscription("unknownChannel"); + bus.registerHandler("handler", TestHandlers.nullHandler(), subscription); + bus.start(); + } + + @Test + public void testAutoCreateChannelsEnabled() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "messageBusWithAutoCreateChannels.xml", this.getClass()); + MessageBus bus = (MessageBus) context.getBean(MessageBusParser.MESSAGE_BUS_BEAN_NAME); + Subscription subscription = new Subscription("channelToCreate"); + bus.registerHandler("handler", TestHandlers.nullHandler(), subscription); + bus.start(); + assertNotNull(bus.lookupChannel("channelToCreate")); + bus.stop(); + } + +} diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAutoCreateChannels.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAutoCreateChannels.xml new file mode 100644 index 0000000000..156e0432cc --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithAutoCreateChannels.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml new file mode 100644 index 0000000000..7078e8bb35 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithDefaults.xml @@ -0,0 +1,12 @@ + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannelReference.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannelReference.xml new file mode 100644 index 0000000000..139800609b --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/messageBusWithErrorChannelReference.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/TestHandlers.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/TestHandlers.java new file mode 100644 index 0000000000..b564ca224e --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/TestHandlers.java @@ -0,0 +1,53 @@ +/* + * Copyright 2002-2007 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.util.concurrent.CountDownLatch; + +import org.springframework.integration.message.Message; + +/** + * Factory for {@link MessageHandler} implementations that are useful for testing. + * + * @author Mark Fisher + */ +public class TestHandlers { + + /** + * Create a {@link MessageHandler} that always returns null. + */ + public static MessageHandler nullHandler() { + return new MessageHandler() { + public Message handle(Message message) { + return null; + } + }; + } + + /** + * Create a {@link MessageHandler} that counts down on the provided latch. + */ + public static MessageHandler countDownHandler(final CountDownLatch latch) { + return new MessageHandler() { + public Message handle(Message message) { + latch.countDown(); + return null; + } + }; + } + +}