Merge pull request #360 from olegz/INT-2434-v3

This commit is contained in:
Gary Russell
2012-03-30 14:43:01 -04:00
16 changed files with 456 additions and 66 deletions

View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<beansProjectDescription>
<version>1</version>
<pluginVersion><![CDATA[2.7.1.201107082359-RELEASE]]></pluginVersion>
<configSuffixes>
<configSuffix><![CDATA[xml]]></configSuffix>
</configSuffixes>
<enableImports><![CDATA[false]]></enableImports>
<configs>
<config>src/test/java/org/springframework/integration/gateway/GatewayInterfaceTest-context.xml</config>
<config>src/test/java/org/springframework/integration/gateway/InnerGatewayWithChainTests-context.xml</config>
<config>src/test/java/org/springframework/integration/history/annotated-config.xml</config>
<config>src/test/java/org/springframework/integration/aggregator/config.xml</config>
</configs>
<configSets>
</configSets>
</beansProjectDescription>

View File

@@ -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<String>());
}
@SuppressWarnings("unchecked")
Collection<String> channelCandidateNames = (Collection<String>) 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<Element> 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);
}
}
}

View File

@@ -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 <bean> 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<String>());
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
}
}
}

View File

@@ -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<String> 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<String> channelNames;
public AutoCreateCandidatesCollector(Collection<String> channelNames){
this.channelNames = channelNames;
}
public Collection<String> getChannelNames() {
return channelNames;
}
}
}

View File

@@ -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 {

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder properties-ref="props"/>
<util:properties id="props">
<prop key="channelC" >channelC</prop>
<prop key="channelB" >channelB</prop>
</util:properties>
<int:service-activator id="serviceA" input-channel="channelA" output-channel="#{'channelB'}" expression="''"/>
<int:service-activator id="serviceB" input-channel="#{'channelB'}" output-channel="${channelC}" expression="''"/>
<int:service-activator id="serviceC" input-channel="${channelC}" output-channel="#{@channelD.getComponentName()}" expression="''"/>
<int:service-activator id="serviceD" input-channel="parentChannelA" output-channel="#{@parentChannelB.getComponentName()}" expression="''"/>
<int:service-activator id="serviceE" input-channel="#{'parentChannelB'}" expression="''"/>
<int:publish-subscribe-channel id="channelEarly"/>
<int:service-activator id="serviceF" input-channel="channelEarly" expression="''"/>
<int:channel id="channelC"/>
<int:channel id="channelD"/>
</beans>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd">
<int:channel id="parentChannelA"/>
<int:channel id="parentChannelB"/>
</beans>

View File

@@ -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());
}
}

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd">
<int:service-activator input-channel="inputChannel" expression="'hello'"/>
<bean id="ChannelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="false"/>
</bean>
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd">
<bean id="ChannelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="false"/>
</bean>
<int:service-activator input-channel="inputChannel" expression="'hello'"/>
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd">
<int:service-activator input-channel="inputChannel" expression="'hello'"/>
<bean id="ChannelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="true"/>
</bean>
</beans>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-2.1.xsd">
<bean id="ChannelInitializer" class="org.springframework.integration.config.xml.ChannelInitializer">
<property name="autoCreate" value="true"/>
</bean>
<int:service-activator input-channel="inputChannel" expression="'hello'"/>
</beans>

View File

@@ -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<String> siBeanNames = new HashSet<String>();
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<String> autoCreateChannelCandidatesNames =
(Collection<String>) 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<String> excludedNames = (Set<String>) 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;
}
}

View File

@@ -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<MBeanServer> 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<String>)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("z"));
assertTrue(((Set<String>)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<String>)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("z"));
}
public static class Foo{}
}

View File

@@ -24,18 +24,20 @@
<bean id="myExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="excludedBeans" value="foo"/>
</bean>
<bean id="foo" class="org.springframework.integration_.mbeanexporterhelper.Int2307Tests.Foo"/>
<int:recipient-list-router id="rlr" input-channel="x">
<int:recipient channel="y"/>
</int:recipient-list-router>
<int:header-value-router id="hvr" input-channel="x" header-name="ghgf">
<int:mapping value="foo" channel="y"/>
</int:header-value-router>
<int:channel id="x" />
<int:channel id="y" />
<int:service-activator input-channel="z" expression="''"/>
</beans>

View File

@@ -22,16 +22,26 @@
</util:properties>
<context:mbean-export/>
<int:recipient-list-router id="rlr" input-channel="x">
<int:recipient channel="y"/>
</int:recipient-list-router>
<int:header-value-router id="hvr" input-channel="x" header-name="ghgf">
<int:mapping value="foo" channel="y"/>
</int:header-value-router>
<int:channel id="x" />
<int:channel id="y" />
<int:service-activator input-channel="z" expression="''"/>
<int:service-activator input-channel="${zz}" expression="''"/>
<util:properties id="placeholders">
<prop key="zz">zz</prop>
</util:properties>
<context:property-placeholder properties-ref="placeholders"/>
</beans>