Skip to content

Commit

Permalink
Merge pull request #360 from olegz/INT-2434-v3
Browse files Browse the repository at this point in the history
  • Loading branch information
garyrussell committed Mar 30, 2012
2 parents 93c0f65 + 458eac4 commit e847342
Show file tree
Hide file tree
Showing 16 changed files with 456 additions and 66 deletions.
17 changes: 0 additions & 17 deletions spring-integration-core/.springBeans

This file was deleted.

@@ -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.
Expand All @@ -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;

Expand All @@ -46,7 +49,6 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit

protected static final String EXPRESSION_ATTRIBUTE = "expression";


@Override
protected boolean shouldGenerateId() {
return false;
Expand Down Expand Up @@ -83,28 +85,34 @@ protected final AbstractBeanDefinition parseInternal(Element element, ParserCont
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)) {
Expand All @@ -120,10 +128,4 @@ protected final AbstractBeanDefinition parseInternal(Element element, ParserCont
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);
}

}
}
@@ -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.
Expand All @@ -24,23 +24,28 @@
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;

/**
* Base class for NamespaceHandlers that registers a BeanFactoryPostProcessor
* 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";

Expand All @@ -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);
}
Expand All @@ -61,6 +67,46 @@ public final BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder def
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) {
Expand Down Expand Up @@ -125,5 +171,4 @@ private void doRegisterBeanDefinitionParser(String elementName, BeanDefinitionPa
}

}

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

0 comments on commit e847342

Please sign in to comment.