Commit
and @garyrussell. Added support for disabling aut-creatioin of channels
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ | |
|
||
package org.springframework.integration.config.xml; | ||
|
||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
|
@@ -91,10 +92,14 @@ protected final AbstractBeanDefinition parseInternal(Element element, ParserCont | |
String inputChannelName = element.getAttribute(inputChannelAttributeName); | ||
|
||
if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)){ | ||
BeanDefinition channelCreatorDefinition = parserContext.getRegistry().getBeanDefinition(AbstractIntegrationNamespaceHandler.CHANNEL_INITIALIZER_BEAN_NAME); | ||
BeanDefinition channelRegistry = parserContext.getRegistry().getBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); | ||
|
||
@SuppressWarnings("unchecked") | ||
Set<String> channelNames = (Set<String>) channelCreatorDefinition.getConstructorArgumentValues(). | ||
getArgumentValue(0, Set.class).getValue(); | ||
Set<String> channelNames = (Set<String>) channelRegistry.getAttribute(ChannelInitializer.CHANNEL_NAMES_ATTR); | ||
if (channelNames == null){ | ||
channelNames = (Set<String>) new HashSet<String>(); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
olegz
Author
Owner
|
||
channelRegistry.setAttribute(ChannelInitializer.CHANNEL_NAMES_ATTR, channelNames); | ||
} | ||
channelNames.add(inputChannelName); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,22 +16,21 @@ | |
|
||
package org.springframework.integration.config.xml; | ||
|
||
import java.util.Set; | ||
|
||
import org.w3c.dom.Element; | ||
import org.w3c.dom.Node; | ||
|
||
import org.springframework.beans.factory.ListableBeanFactory; | ||
import org.springframework.beans.factory.config.BeanDefinition; | ||
import org.springframework.beans.factory.config.BeanDefinitionHolder; | ||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | ||
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; | ||
|
||
/** | ||
|
@@ -68,23 +67,46 @@ public final BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder def | |
return this.delegate.decorate(source, definition, parserContext); | ||
} | ||
|
||
/* | ||
* This method will auto-register ChannelInitializer which could also be overridden by the user | ||
* by simply registering a ChannelInitializer <bean> with 'autoCreate' property set to false. | ||
* It will also register ChannelInitializer$AutoCreateCandidatesCollector which simply collects channelNames to be created. | ||
* It does a little more so please read its comments. ChannelInitializer$AutoCreateCandidatesCollector | ||
* can NOT be overridden by the user | ||
This comment has been minimized.
Sorry, something went wrong.
garyrussell
|
||
*/ | ||
private void registerImplicitChannelCreator(ParserContext parserContext) { | ||
// ChannelInitializer | ||
boolean alreadyRegistered = false; | ||
if (parserContext.getRegistry() instanceof ListableBeanFactory) { | ||
if (parserContext.getRegistry() instanceof ConfigurableListableBeanFactory) { | ||
// 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); | ||
alreadyRegistered = ((ConfigurableListableBeanFactory) parserContext.getRegistry()).containsBeanDefinition(CHANNEL_INITIALIZER_BEAN_NAME); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong. |
||
} | ||
else { | ||
alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(CHANNEL_INITIALIZER_BEAN_NAME); | ||
} | ||
if (!alreadyRegistered) { | ||
BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class); | ||
Set<String> channelNames = new ManagedSet<String>(); | ||
channelDef.addConstructorArgValue(channelNames); | ||
BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), CHANNEL_INITIALIZER_BEAN_NAME); | ||
BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, parserContext.getRegistry()); | ||
} | ||
// ChannelInitializer$AutoCreateCandidatesCollector | ||
alreadyRegistered = false; | ||
if (parserContext.getRegistry() instanceof ConfigurableListableBeanFactory) { | ||
// unlike DefaultConfiguringBeanFactoryPostProcessor we need one of these per registry | ||
// therefore we need to call containsBeanDefinition(..) which does not consider parent registry | ||
alreadyRegistered = ((ConfigurableListableBeanFactory) 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); | ||
BeanDefinitionHolder channelRegistryHolder = | ||
new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(), ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); | ||
BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, parserContext.getRegistry()); | ||
} | ||
} | ||
|
||
private void registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(ParserContext parserContext) { | ||
|
@@ -151,5 +173,4 @@ private void doRegisterBeanDefinitionParser(String elementName, BeanDefinitionPa | |
} | ||
|
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,7 +15,7 @@ | |
*/ | ||
package org.springframework.integration.config.xml; | ||
|
||
import java.util.Set; | ||
import java.util.Collection; | ||
|
||
import org.apache.commons.logging.Log; | ||
import org.apache.commons.logging.LogFactory; | ||
|
@@ -24,47 +24,109 @@ | |
import org.springframework.beans.factory.BeanFactory; | ||
import org.springframework.beans.factory.BeanFactoryAware; | ||
import org.springframework.beans.factory.InitializingBean; | ||
import org.springframework.beans.factory.config.BeanDefinition; | ||
import org.springframework.beans.factory.config.BeanDefinitionHolder; | ||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor; | ||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; | ||
import org.springframework.beans.factory.support.BeanDefinitionBuilder; | ||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; | ||
import org.springframework.beans.factory.support.BeanDefinitionRegistry; | ||
import org.springframework.beans.factory.support.ManagedSet; | ||
import org.springframework.beans.factory.support.RootBeanDefinition; | ||
import org.springframework.integration.channel.DirectChannel; | ||
|
||
/** | ||
* A {@link InitializingBean} implementation that is responsible for creating channels that | ||
* are not explicitly defined but identified via '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}. | ||
* | ||
* A {@link InitializingBean} implementation that is responsible for creating | ||
* channels that are not explicitly defined but identified via 'input-channel' | ||
This comment has been minimized.
Sorry, something went wrong. |
||
* 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 | ||
* @since 2.1.1 | ||
*/ | ||
class ChannelInitializer implements BeanFactoryAware, InitializingBean { | ||
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 final Set<String> channelNames; | ||
private volatile boolean autoCreate = true; | ||
|
||
public ChannelInitializer(Set<String> channelNames){ | ||
this.channelNames = channelNames; | ||
public void setAutoCreate(boolean autoCreate) { | ||
this.autoCreate = autoCreate; | ||
} | ||
|
||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException { | ||
this.beanFactory = beanFactory; | ||
} | ||
|
||
public void afterPropertiesSet() throws Exception { | ||
for (String channelName : this.channelNames) { | ||
if (!beanFactory.containsBean(channelName)){ | ||
if (this.logger.isDebugEnabled()){ | ||
this.logger.debug("Auto-creating channel '" + channelName + "' as DirectChannel"); | ||
if (!autoCreate){ | ||
return; | ||
} | ||
else { | ||
AutoCreateCandidatesCollector channelCandidatesColector = | ||
(AutoCreateCandidatesCollector) beanFactory.getBean(AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class); | ||
// at this point channelNames are all resolved with placeholders and SpEL | ||
Collection<String> channelNames = channelCandidatesColector.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); | ||
} | ||
} | ||
RootBeanDefinition messageChannel = new RootBeanDefinition(); | ||
messageChannel.setBeanClass(DirectChannel.class); | ||
BeanDefinitionHolder messageChannelHolder = new BeanDefinitionHolder(messageChannel, channelName); | ||
BeanDefinitionReaderUtils.registerBeanDefinition(messageChannelHolder, (BeanDefinitionRegistry) this.beanFactory); | ||
} | ||
} | ||
} | ||
|
||
/* | ||
* This class serves two purposes | ||
* 1. A BeanDefinition that collects candidate channel names as its BeanDefinition attributes | ||
* 2. A holder of the resolved (property placeholders and SpEL expressions) channel names to be auto-created by the | ||
* ChannelInitializer. | ||
* Once this bean reaches Factory post processing phase it creates a new definition of itself injecting it with | ||
* the ManagedSet of channelNames property. The ManagedSet will be naturally resolved before it is used by the | ||
* ChannelInitializer. It also unregisters its old definition (since its only value was to provide a place | ||
* to collect candidate channel names) and registers new definition of itself with "to-be resolved" channelNames | ||
* property | ||
*/ | ||
public static class AutoCreateCandidatesCollector implements BeanFactoryPostProcessor{ | ||
This comment has been minimized.
Sorry, something went wrong.
garyrussell
|
||
|
||
private volatile Collection<String> channelNames; | ||
|
||
public void setChannelNames(Collection<String> channelNames) { | ||
this.channelNames = channelNames; | ||
} | ||
|
||
public Collection<String> getChannelNames() { | ||
return channelNames; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { | ||
BeanDefinition definition = beanFactory.getBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); | ||
Collection<String> _channelNames = (Collection<String>) definition.getAttribute(ChannelInitializer.CHANNEL_NAMES_ATTR); | ||
if (_channelNames != null){ | ||
((BeanDefinitionRegistry)beanFactory).removeBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); | ||
BeanDefinitionBuilder candidatesBuilder = BeanDefinitionBuilder.genericBeanDefinition(this.getClass().getName()); | ||
ManagedSet<String> candidateChannelNamesToBeResolved = new ManagedSet<String>(); | ||
candidateChannelNamesToBeResolved.addAll(_channelNames); | ||
candidatesBuilder.addPropertyValue("channelNames", candidateChannelNamesToBeResolved); | ||
BeanDefinitionHolder holder = new BeanDefinitionHolder(candidatesBuilder.getBeanDefinition(), | ||
ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); | ||
BeanDefinitionReaderUtils.registerBeanDefinition(holder, (BeanDefinitionRegistry) beanFactory); | ||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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> |
Shouldn't this be a ManagedSet?