diff --git a/spring-integration-core/.springBeans b/spring-integration-core/.springBeans
deleted file mode 100644
index 6a45d2853e..0000000000
--- a/spring-integration-core/.springBeans
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- 1
-
-
-
-
-
-
- src/test/java/org/springframework/integration/gateway/GatewayInterfaceTest-context.xml
- src/test/java/org/springframework/integration/gateway/InnerGatewayWithChainTests-context.xml
- src/test/java/org/springframework/integration/history/annotated-config.xml
- src/test/java/org/springframework/integration/aggregator/config.xml
-
-
-
-
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java
index 71498c7464..769477ca89 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractConsumerEndpointParser.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -16,19 +16,22 @@
package org.springframework.integration.config.xml;
+import java.util.Collection;
import java.util.List;
import org.w3c.dom.Element;
-import org.springframework.beans.factory.BeanFactory;
-import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.ConstructorArgumentValues;
+import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
-import org.springframework.integration.context.IntegrationContextUtils;
+import org.springframework.integration.config.ConsumerEndpointFactoryBean;
import org.springframework.util.CollectionUtils;
import org.springframework.util.xml.DomUtils;
@@ -46,7 +49,6 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
protected static final String EXPRESSION_ATTRIBUTE = "expression";
-
@Override
protected boolean shouldGenerateId() {
return false;
@@ -83,28 +85,34 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
return handlerBeanDefinition;
}
- BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
- IntegrationNamespaceUtils.BASE_PACKAGE + ".config.ConsumerEndpointFactoryBean");
+ BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class);
String handlerBeanName = BeanDefinitionReaderUtils.generateBeanName(handlerBeanDefinition, parserContext.getRegistry());
parserContext.registerBeanComponent(new BeanComponentDefinition(handlerBeanDefinition, handlerBeanName));
-
+
builder.addPropertyReference("handler", handlerBeanName);
String inputChannelName = element.getAttribute(inputChannelAttributeName);
- boolean channelExists = false;
- if (parserContext.getRegistry() instanceof BeanFactory) {
- // BeanFactory also checks ancestor contexts in a hierarchy
- channelExists = ((BeanFactory) parserContext.getRegistry()).containsBean(inputChannelName);
- }
- else {
- channelExists = parserContext.getRegistry().containsBeanDefinition(inputChannelName);
- }
- if (!channelExists && this.shouldAutoCreateChannel(inputChannelName)) {
- BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(
- IntegrationNamespaceUtils.BASE_PACKAGE + ".channel.DirectChannel");
- BeanDefinitionHolder holder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), inputChannelName);
- BeanDefinitionReaderUtils.registerBeanDefinition(holder, parserContext.getRegistry());
+
+ if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)){
+ if (parserContext.getRegistry().containsBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)){
+ BeanDefinition channelRegistry = parserContext.getRegistry().
+ getBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
+ ConstructorArgumentValues caValues = channelRegistry.getConstructorArgumentValues();
+ ValueHolder vh = caValues.getArgumentValue(0, Collection.class);
+ if (vh == null){ //although it should never happen if it does we can fix it
+ caValues.addIndexedArgumentValue(0, new ManagedSet());
+ }
+
+ @SuppressWarnings("unchecked")
+ Collection channelCandidateNames = (Collection) caValues.getArgumentValue(0, Collection.class).getValue();
+ channelCandidateNames.add(inputChannelName);
+ }
+ else {
+ parserContext.getReaderContext().error("Failed to locate '" +
+ ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'", parserContext.getRegistry());
+ }
}
+
builder.addPropertyValue("inputChannelName", inputChannelName);
List pollerElementList = DomUtils.getChildElementsByTagName(element, "poller");
if (!CollectionUtils.isEmpty(pollerElementList)) {
@@ -120,10 +128,4 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit
parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinition, beanName));
return null;
}
-
- private boolean shouldAutoCreateChannel(String channelName) {
- return !IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME.equals(channelName)
- && !IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME.equals(channelName);
- }
-
-}
+}
\ No newline at end of file
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java
index 23be8df71a..a1b6e98de5 100644
--- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AbstractIntegrationNamespaceHandler.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2011 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -24,11 +24,13 @@ import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.ManagedSet;
import org.springframework.beans.factory.xml.BeanDefinitionDecorator;
import org.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandler;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.integration.config.xml.ChannelInitializer.AutoCreateCandidatesCollector;
import org.springframework.util.StringUtils;
/**
@@ -36,11 +38,14 @@ import org.springframework.util.StringUtils;
* for configuring default bean definitions.
*
* @author Mark Fisher
+ * @author Oleg Zhurakousky
*/
public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHandler {
private static final String VERSION = "2.1";
+ public static final String CHANNEL_INITIALIZER_BEAN_NAME = ChannelInitializer.class.getSimpleName();
+
private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME =
"DefaultConfiguringBeanFactoryPostProcessor";
@@ -53,6 +58,7 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
public final BeanDefinition parse(Element element, ParserContext parserContext) {
this.verifySchemaVersion(element, parserContext);
+ this.registerImplicitChannelCreator(parserContext);
this.registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(parserContext);
return this.delegate.parse(element, parserContext);
}
@@ -61,6 +67,46 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
return this.delegate.decorate(source, definition, parserContext);
}
+ /*
+ * This method will auto-register a ChannelInitializer which could also be overridden by the user
+ * by simply registering a ChannelInitializer with its 'autoCreate' property set to false to suppress channel creation.
+ * It will also register a ChannelInitializer$AutoCreateCandidatesCollector which simply collects candidate channel names.
+ */
+ private void registerImplicitChannelCreator(ParserContext parserContext) {
+ // ChannelInitializer
+ boolean alreadyRegistered = false;
+ if (parserContext.getRegistry() instanceof ListableBeanFactory) {
+ // unlike DefaultConfiguringBeanFactoryPostProcessor we need one of these per registry
+ // therefore we need to call containsBeanDefinition(..) which does not consider parent registry
+ alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBeanDefinition(CHANNEL_INITIALIZER_BEAN_NAME);
+ }
+ else {
+ alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(CHANNEL_INITIALIZER_BEAN_NAME);
+ }
+ if (!alreadyRegistered) {
+ BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class);
+ BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), CHANNEL_INITIALIZER_BEAN_NAME);
+ BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, parserContext.getRegistry());
+ }
+ // ChannelInitializer$AutoCreateCandidatesCollector
+ if (parserContext.getRegistry() instanceof ListableBeanFactory) {
+ // unlike DefaultConfiguringBeanFactoryPostProcessor we need one of these per registry
+ // therefore we need to call containsBeanDefinition(..) which does not consider parent registry
+ alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).
+ containsBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
+ }
+ else {
+ alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
+ }
+ if (!alreadyRegistered) {
+ BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder.genericBeanDefinition(AutoCreateCandidatesCollector.class);
+ channelRegistryBuilder.addConstructorArgValue(new ManagedSet());
+ BeanDefinitionHolder channelRegistryHolder =
+ new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(), ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
+ BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, parserContext.getRegistry());
+ }
+ }
+
private void registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(ParserContext parserContext) {
boolean alreadyRegistered = false;
if (parserContext.getRegistry() instanceof ListableBeanFactory) {
@@ -125,5 +171,4 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa
}
}
-
}
diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ChannelInitializer.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ChannelInitializer.java
new file mode 100644
index 0000000000..eab471262a
--- /dev/null
+++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/ChannelInitializer.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2002-2012 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.xml;
+
+import java.util.Collection;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
+import org.springframework.beans.factory.InitializingBean;
+import org.springframework.beans.factory.config.BeanDefinitionHolder;
+import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
+import org.springframework.beans.factory.support.BeanDefinitionRegistry;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.integration.channel.DirectChannel;
+import org.springframework.util.Assert;
+
+/**
+ * A {@link InitializingBean} implementation that is responsible for creating
+ * channels that are not explicitly defined but identified via the 'input-channel'
+ * attribute of the corresponding endpoints.
+ *
+ * This bean plays a role of pre-instantiator since it is instantiated and
+ * initialized as the very first bean of all SI beans using
+ * {@link AbstractIntegrationNamespaceHandler}.
+ *
+ * @author Oleg Zhurakousky
+ * @since 2.1.1
+ */
+final class ChannelInitializer implements BeanFactoryAware, InitializingBean {
+
+ public static String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates";
+ public static String CHANNEL_NAMES_ATTR = "channelNames";
+
+ private Log logger = LogFactory.getLog(this.getClass());
+
+ private volatile BeanFactory beanFactory;
+
+ private volatile boolean autoCreate = true;
+
+ public void setAutoCreate(boolean autoCreate) {
+ this.autoCreate = autoCreate;
+ }
+
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
+ public void afterPropertiesSet() throws Exception {
+ Assert.notNull(this.beanFactory, "'beanFactory' must not be null");
+ if (!autoCreate){
+ return;
+ }
+ else {
+ AutoCreateCandidatesCollector channelCandidatesCollector =
+ (AutoCreateCandidatesCollector) beanFactory.getBean(AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class);
+ Assert.notNull(channelCandidatesCollector, "Failed to locate '" +
+ ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME);
+ // at this point channelNames are all resolved with placeholders and SpEL
+ Collection channelNames = channelCandidatesCollector.getChannelNames();
+ if (channelNames != null){
+ for (String channelName : channelNames) {
+ if (!beanFactory.containsBean(channelName)){
+ if (this.logger.isDebugEnabled()){
+ this.logger.debug("Auto-creating channel '" + channelName + "' as DirectChannel");
+ }
+ RootBeanDefinition messageChannel = new RootBeanDefinition();
+ messageChannel.setBeanClass(DirectChannel.class);
+ BeanDefinitionHolder messageChannelHolder = new BeanDefinitionHolder(messageChannel, channelName);
+ BeanDefinitionReaderUtils.registerBeanDefinition(messageChannelHolder, (BeanDefinitionRegistry) this.beanFactory);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * Collects candidate channel names to be auto-created by ChannelInitializer
+ */
+ static class AutoCreateCandidatesCollector {
+
+ private final Collection channelNames;
+
+ public AutoCreateCandidatesCollector(Collection channelNames){
+ this.channelNames = channelNames;
+ }
+
+ public Collection getChannelNames() {
+ return channelNames;
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/DirectChannelTests.java b/spring-integration-core/src/test/java/org/springframework/integration/channel/DirectChannelTests.java
index 65f46609a4..3455abee1d 100644
--- a/spring-integration-core/src/test/java/org/springframework/integration/channel/DirectChannelTests.java
+++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/DirectChannelTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2010 the original author or authors.
+ * Copyright 2002-2012 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.
@@ -16,20 +16,27 @@
package org.springframework.integration.channel;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
+import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.junit.Test;
import org.springframework.beans.DirectFieldAccessor;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.Message;
+import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessageHandler;
import org.springframework.integration.dispatcher.RoundRobinLoadBalancingStrategy;
import org.springframework.integration.dispatcher.UnicastingDispatcher;
+import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.message.GenericMessage;
+import org.springframework.integration.test.util.TestUtils;
+import org.springframework.util.ReflectionUtils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
/**
* @author Mark Fisher
@@ -68,6 +75,55 @@ public class DirectChannelTests {
assertEquals("test-thread", target.threadName);
}
+ @Test // See INT-2434
+ public void testChannelCreationWithBeanDefinitionOverrideTrue() throws Exception {
+ ClassPathXmlApplicationContext parentContext = new ClassPathXmlApplicationContext("parent-config.xml", this.getClass());
+ MessageChannel parentChannelA = parentContext.getBean("parentChannelA", MessageChannel.class);
+ MessageChannel parentChannelB = parentContext.getBean("parentChannelB", MessageChannel.class);
+
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
+ context.setAllowBeanDefinitionOverriding(false);
+ context.setConfigLocations(new String[]{"classpath:org/springframework/integration/channel/channel-override-config.xml"});
+ context.setParent(parentContext);
+ Method method = ReflectionUtils.findMethod(ClassPathXmlApplicationContext.class, "obtainFreshBeanFactory");
+ method.setAccessible(true);
+ method.invoke(context);
+ assertFalse(context.containsBean("channelA"));
+ assertFalse(context.containsBean("channelB"));
+ assertTrue(context.containsBean("channelC"));
+ assertTrue(context.containsBean("channelD"));
+
+ context.refresh();
+
+ PublishSubscribeChannel channelEarly = context.getBean("channelEarly", PublishSubscribeChannel.class);
+
+ assertTrue(context.containsBean("channelA"));
+ assertTrue(context.containsBean("channelB"));
+ assertTrue(context.containsBean("channelC"));
+ assertTrue(context.containsBean("channelD"));
+ EventDrivenConsumer consumerA = context.getBean("serviceA", EventDrivenConsumer.class);
+ assertEquals(context.getBean("channelA"), TestUtils.getPropertyValue(consumerA, "inputChannel"));
+ assertEquals(context.getBean("channelB"), TestUtils.getPropertyValue(consumerA, "handler.outputChannel"));
+
+ EventDrivenConsumer consumerB = context.getBean("serviceB", EventDrivenConsumer.class);
+ assertEquals(context.getBean("channelB"), TestUtils.getPropertyValue(consumerB, "inputChannel"));
+ assertEquals(context.getBean("channelC"), TestUtils.getPropertyValue(consumerB, "handler.outputChannel"));
+
+ EventDrivenConsumer consumerC = context.getBean("serviceC", EventDrivenConsumer.class);
+ assertEquals(context.getBean("channelC"), TestUtils.getPropertyValue(consumerC, "inputChannel"));
+ assertEquals(context.getBean("channelD"), TestUtils.getPropertyValue(consumerC, "handler.outputChannel"));
+
+ EventDrivenConsumer consumerD = context.getBean("serviceD", EventDrivenConsumer.class);
+ assertEquals(parentChannelA, TestUtils.getPropertyValue(consumerD, "inputChannel"));
+ assertEquals(parentChannelB, TestUtils.getPropertyValue(consumerD, "handler.outputChannel"));
+
+ EventDrivenConsumer consumerE = context.getBean("serviceE", EventDrivenConsumer.class);
+ assertEquals(parentChannelB, TestUtils.getPropertyValue(consumerE, "inputChannel"));
+
+ EventDrivenConsumer consumerF = context.getBean("serviceF", EventDrivenConsumer.class);
+ assertEquals(channelEarly, TestUtils.getPropertyValue(consumerF, "inputChannel"));
+ }
+
private static class ThreadNameExtractingTestTarget implements MessageHandler {
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/channel-override-config.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/channel-override-config.xml
new file mode 100644
index 0000000000..c2bc3d4d9f
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/channel-override-config.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+ channelC
+ channelB
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/channel/parent-config.xml b/spring-integration-core/src/test/java/org/springframework/integration/channel/parent-config.xml
new file mode 100644
index 0000000000..dd3b9a2298
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/channel/parent-config.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ChannelAutoCreationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ChannelAutoCreationTests.java
new file mode 100644
index 0000000000..7f651199d2
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/ChannelAutoCreationTests.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2002-2012 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.xml;
+
+import org.junit.Test;
+
+import org.springframework.beans.factory.BeanCreationException;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+/**
+ *
+ * @author Oleg Zhurakousky
+ *
+ */
+public class ChannelAutoCreationTests {
+
+ @Test // no assertions since it validates that no exception is thrown
+ public void testEnablingAutoChannelCreationBeforeWithCustom(){
+ new ClassPathXmlApplicationContext("TestEnableChannelAutoCreation-before-context.xml", this.getClass());
+ }
+
+ @Test // no assertions since it validates that no exception is thrown
+ public void testEnablingAutoChannelCreationAfterWithCustom(){
+ new ClassPathXmlApplicationContext("TestEnableChannelAutoCreation-after-context.xml", this.getClass());
+ }
+
+ @Test(expected=BeanCreationException.class)
+ public void testDisablingAutoChannelCreationAfter(){
+ new ClassPathXmlApplicationContext("TestDisableChannelAutoCreation-after-context.xml", this.getClass());
+ }
+
+ @Test(expected=BeanCreationException.class)
+ public void testDisablingAutoChannelCreationBefore(){
+ new ClassPathXmlApplicationContext("TestDisableChannelAutoCreation-before-context.xml", this.getClass());
+ }
+}
\ No newline at end of file
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestDisableChannelAutoCreation-after-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestDisableChannelAutoCreation-after-context.xml
new file mode 100644
index 0000000000..47524a4334
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestDisableChannelAutoCreation-after-context.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestDisableChannelAutoCreation-before-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestDisableChannelAutoCreation-before-context.xml
new file mode 100644
index 0000000000..18d97ef9ad
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestDisableChannelAutoCreation-before-context.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestEnableChannelAutoCreation-after-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestEnableChannelAutoCreation-after-context.xml
new file mode 100644
index 0000000000..feb3b0fb5c
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestEnableChannelAutoCreation-after-context.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestEnableChannelAutoCreation-before-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestEnableChannelAutoCreation-before-context.xml
new file mode 100644
index 0000000000..01e60a68fc
--- /dev/null
+++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/TestEnableChannelAutoCreation-before-context.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java
index 3ad0adb7b3..f25b838558 100644
--- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java
+++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java
@@ -12,16 +12,19 @@
*/
package org.springframework.integration.jmx.config;
+import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
-import org.springframework.core.PriorityOrdered;
+import org.springframework.core.Ordered;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.util.StringUtils;
@@ -31,24 +34,43 @@ import org.springframework.util.StringUtils;
* It helps in eliminating conflicts when more than one MBeanExporter is present. It creates a list
* of bean names that will be exported by the IntegrationMBeanExporter and merges it with the list
* of 'excludedBeans' of MBeanExporter so it will not attempt to export them again.
- *
+ *
* @author Oleg Zhurakousky
* @since 2.1
*
*/
-class MBeanExporterHelper implements BeanFactoryPostProcessor, BeanPostProcessor, PriorityOrdered {
+class MBeanExporterHelper implements BeanFactoryPostProcessor,
+ BeanPostProcessor, Ordered, BeanFactoryAware {
private final static String EXCLUDED_BEANS_PROPERTY_NAME = "excludedBeans";
private final static String SI_ROOT_PACKAGE = "org.springframework.integration.";
private final Set siBeanNames = new HashSet();
+
+ private volatile BeanFactory beanFactory;
+
+ private volatile boolean capturedAutoChannelCandidates;
- @SuppressWarnings("unchecked")
+ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
+ this.beanFactory = beanFactory;
+ }
+
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
- if (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)){
+ if (!this.capturedAutoChannelCandidates && this.beanFactory != null) {
+ Object autoCreateChannelCandidates = beanFactory.getBean("$autoCreateChannelCandidates");
+ if (autoCreateChannelCandidates != null){
+ @SuppressWarnings("unchecked")
+ Collection autoCreateChannelCandidatesNames =
+ (Collection) new DirectFieldAccessor(autoCreateChannelCandidates).getPropertyValue("channelNames");
+ this.siBeanNames.addAll(autoCreateChannelCandidatesNames);
+ }
+ this.capturedAutoChannelCandidates = true;
+ }
+ if (bean instanceof MBeanExporter && !(bean instanceof IntegrationMBeanExporter)) {
MBeanExporter mbeanExporter = (MBeanExporter) bean;
DirectFieldAccessor mbeDfa = new DirectFieldAccessor(mbeanExporter);
+ @SuppressWarnings("unchecked")
Set excludedNames = (Set) mbeDfa.getPropertyValue(EXCLUDED_BEANS_PROPERTY_NAME);
if (excludedNames != null) {
siBeanNames.addAll(excludedNames);
@@ -79,6 +101,6 @@ class MBeanExporterHelper implements BeanFactoryPostProcessor, BeanPostProcessor
}
public int getOrder() {
- return Integer.MIN_VALUE;
+ return Ordered.HIGHEST_PRECEDENCE;
}
}
diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java
index de1a6e5b80..9a4c7dc464 100644
--- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java
+++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java
@@ -34,9 +34,10 @@ import org.springframework.jmx.export.MBeanExporter;
*/
public class Int2307Tests {
+ @SuppressWarnings("unchecked")
@Test
public void testInt2307_DefaultMBeanExporter() throws Exception{
- new ClassPathXmlApplicationContext("single-config.xml", this.getClass());
+ ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("single-config.xml", this.getClass());
List servers = MBeanServerFactory.findMBeanServer(null);
assertEquals(1, servers.size());
MBeanServer server = servers.get(0);
@@ -61,6 +62,11 @@ public class Int2307Tests {
assertEquals(0xf, bits);
assertEquals(4, count);
+ Class> clazz = Class.forName("org.springframework.integration.jmx.config.MBeanExporterHelper");
+ Object mBeanExporterHelper = context.getBean(clazz);
+ assertTrue(((Set)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("z"));
+ assertTrue(((Set)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("zz"));
+
// make sure there are no duplicate MBean ObjectNames if 2 contexts loaded from same config
new ClassPathXmlApplicationContext("single-config.xml", this.getClass());
}
@@ -76,7 +82,10 @@ public class Int2307Tests {
assertTrue(excludedBeanNames.contains("x"));
assertTrue(excludedBeanNames.contains("y"));
assertTrue(excludedBeanNames.contains("foo")); // non SI bean
+ Class> clazz = Class.forName("org.springframework.integration.jmx.config.MBeanExporterHelper");
+ Object mBeanExporterHelper = context.getBean(clazz);
+ assertTrue(((Set)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("z"));
}
-
+
public static class Foo{}
}
diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml
index a3b926d6b3..0452a971bf 100644
--- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml
+++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config-custom-exporter.xml
@@ -24,18 +24,20 @@
-
+
-
+
-
+
-
+
+
+
diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml
index 4dd3005e3a..a8f69d5d33 100644
--- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml
+++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/single-config.xml
@@ -22,16 +22,26 @@
-
+
-
+
-
+
+
+
+
+
+
+ zz
+
+
+
+