From 4ae5152effa50192e68bad22837957891a3a4975 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 30 Dec 2013 13:21:28 +0200 Subject: [PATCH] INT-3250: Add @EnableIntegration Infrastructure JIRA: https://jira.springsource.org/browse/INT-3250 * The main purpose of this change to move all infrastructure bean definitions from `AbstractIntegrationNamespaceHandler` to a new `IntegrationRegistrar`. * `IntegrationRegistrar` is invoked by the standard `@Configuration` process and also directly from `AbstractIntegrationNamespaceHandler`. * Provide some refactoring for consistency. * Add AnnotationContext tests INT-3250: Polishing * Remove `@EnableIntegration#defaultPublisherChannel` * Revert `#jsonPath` & `#xpath` registration logic * Fix for DEBUG message on each parse JIRA: https://jira.springsource.org/browse/INT-3258 INT-3250 Fix JavaDocs & IDEA+Gradle srcDirs issue INT-3250: Revert `build.gradle` changes INT-3250 Polishing * Remove commented out code * Add javadoc to IntegrationRegistrar.registerBeanDefinitions() * Fix javadoc links to other Spring projects (and JVM, JEE) * Add reference documentation and what's new --- build.gradle | 14 + .../config/IntegrationRegistrar.java | 320 ++++++++++++++++++ .../config/annotation/EnableIntegration.java | 48 +++ .../xml/AbstractConsumerEndpointParser.java | 7 +- .../AbstractIntegrationNamespaceHandler.java | 246 +------------- .../config/xml/AnnotationConfigParser.java | 42 +-- .../config/xml/ChannelInitializer.java | 12 +- .../context/IntegrationContextUtils.java | 15 + .../configuration/EnableIntegrationTests.java | 108 ++++++ src/reference/docbook/overview.xml | 28 ++ src/reference/docbook/whats-new.xml | 29 +- 11 files changed, 586 insertions(+), 283 deletions(-) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/annotation/EnableIntegration.java create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java diff --git a/build.gradle b/build.gradle index 6337f32a36..3d5da6ac4f 100644 --- a/build.gradle +++ b/build.gradle @@ -32,6 +32,19 @@ allprojects { maven { url 'http://repo.spring.io/plugins-release' } mavenCentral() } + + ext.javadocLinks = [ + 'http://docs.oracle.com/javase/7/docs/api/', + 'http://docs.oracle.com/javaee/6/api/', + 'http://docs.spring.io/spring/docs/current/javadoc-api/', + 'http://docs.spring.io/spring-amqp/docs/latest-ga/api/', + 'http://docs.spring.io/spring-data-gemfire/docs/current/api/', + 'http://docs.spring.io/spring-data/data-mongo/docs/current/api/', + 'http://docs.spring.io/spring-data/data-redis/docs/current/api/', + 'http://docs.spring.io/spring-social-twitter/docs/current/api/', + 'http://docs.spring.io/spring-ws/sites/2.0/apidocs/' + ] as String[] + } subprojects { subproject -> @@ -614,6 +627,7 @@ task api(type: Javadoc) { options.header = rootProject.description options.overview = 'src/api/overview.html' options.stylesheetFile = file("src/api/stylesheet.css") + options.links(project.ext.javadocLinks) source subprojects.collect { project -> project.sourceSets.main.allJava } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java new file mode 100644 index 0000000000..624c5b4bcc --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/IntegrationRegistrar.java @@ -0,0 +1,320 @@ +/* + * Copyright 2014 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.config; + +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.BeanClassLoaderAware; +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.PropertiesFactoryBean; +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.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.core.io.support.ResourcePatternResolver; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor; +import org.springframework.integration.channel.DefaultHeaderChannelRegistry; +import org.springframework.integration.config.annotation.EnableIntegration; +import org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.context.IntegrationProperties; +import org.springframework.integration.expression.IntegrationEvaluationContextAwareBeanPostProcessor; +import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; + +/** + * {@link ImportBeanDefinitionRegistrar} implementation that configures integration infrastructure. + * + * @author Artem Bilan + * @since 4.0 + */ +public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, BeanClassLoaderAware { + + private static final Log logger = LogFactory.getLog(IntegrationRegistrar.class); + + private static final Set registriesProcessed = new HashSet(); + + private ClassLoader classLoader; + + @Override + public void setBeanClassLoader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + /** + * Invoked by the framework when an @EnableIntegration annotation is encountered. + * Also called with {@code null} {@code importingClassMetadata} from {@code AbstractIntegrationNamespaceHandler} + * to register the same beans when using XML configuration. Also called by {@code AnnotationConfigParser} + * to register the messaging annotation post processors (for {@code }). + */ + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + this.registerImplicitChannelCreator(registry); + this.registerIntegrationEvaluationContext(registry); + this.registerIntegrationProperties(registry); + this.registerHeaderChannelRegistry(registry); + this.registerBuiltInBeans(registry); + this.registerDefaultConfiguringBeanFactoryPostProcessor(registry); + if (importingClassMetadata != null) { + this.registerMessagingAnnotationPostProcessors(importingClassMetadata, registry); + } + } + + /** + * This method will auto-register a ChannelInitializer which could also be overridden by the user + * by simply registering a ChannelInitializer {@code } with its {@code autoCreate} property + * set to false to suppress channel creation. + * It will also register a ChannelInitializer$AutoCreateCandidatesCollector which simply collects candidate channel names. + * + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerImplicitChannelCreator(BeanDefinitionRegistry registry) { + if (!registry.containsBeanDefinition(IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME)) { + String channelsAutoCreateExpression = IntegrationProperties.getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE); + BeanDefinitionBuilder channelDef = BeanDefinitionBuilder + .genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml.ChannelInitializer") + .addPropertyValue("autoCreate", channelsAutoCreateExpression); + BeanDefinitionHolder channelCreatorHolder = new BeanDefinitionHolder(channelDef.getBeanDefinition(), + IntegrationContextUtils.CHANNEL_INITIALIZER_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(channelCreatorHolder, registry); + } + + if (!registry.containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) { + BeanDefinitionBuilder channelRegistryBuilder = BeanDefinitionBuilder + .genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml.ChannelInitializer$AutoCreateCandidatesCollector"); + channelRegistryBuilder.addConstructorArgValue(new ManagedSet()); + BeanDefinitionHolder channelRegistryHolder = new BeanDefinitionHolder(channelRegistryBuilder.getBeanDefinition(), + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(channelRegistryHolder, registry); + } + } + + /** + * Register {@code integrationGlobalProperties} bean if necessary. + * + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerIntegrationProperties(BeanDefinitionRegistry registry) { + boolean alreadyRegistered = false; + if (registry instanceof ListableBeanFactory) { + alreadyRegistered = ((ListableBeanFactory) registry) + .containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME); + } + else { + alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME); + } + if (!alreadyRegistered) { + ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(this.classLoader); + try { + Resource[] defaultResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.default.properties"); + Resource[] userResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.properties"); + + List resources = new LinkedList(Arrays.asList(defaultResources)); + resources.addAll(Arrays.asList(userResources)); + + BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder + .genericBeanDefinition(PropertiesFactoryBean.class) + .addPropertyValue("locations", resources); + + registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, + integrationPropertiesBuilder.getBeanDefinition()); + } + catch (IOException e) { + logger.warn("Cannot load 'spring.integration.properties' Resources.", e); + } + } + } + + /** + * Register {@link IntegrationEvaluationContextFactoryBean} bean + * and {@link IntegrationEvaluationContextAwareBeanPostProcessor}, if necessary. + * + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerIntegrationEvaluationContext(BeanDefinitionRegistry registry) { + if (!registry.containsBeanDefinition(IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME)) { + BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder + .genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class); + integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + BeanDefinitionHolder integrationEvaluationContextHolder = + new BeanDefinitionHolder(integrationEvaluationContextBuilder.getBeanDefinition(), + IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); + + BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, + registry); + + RootBeanDefinition integrationEvalContextBPP = new RootBeanDefinition(IntegrationEvaluationContextAwareBeanPostProcessor.class); + BeanDefinitionReaderUtils.registerWithGeneratedName(integrationEvalContextBPP, registry); + } + } + + /** + * Register {@code jsonPath} and {@code xpath} SpEL-function beans, if necessary. + * + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerBuiltInBeans(BeanDefinitionRegistry registry) { + int registryId = System.identityHashCode(registry); + + String jsonPathBeanName = "jsonPath"; + boolean alreadyRegistered = false; + if (registry instanceof ListableBeanFactory) { + alreadyRegistered = ((ListableBeanFactory) registry).containsBean(jsonPathBeanName); + } + else { + alreadyRegistered = registry.isBeanNameInUse(jsonPathBeanName); + } + if (!alreadyRegistered && !registriesProcessed.contains(registryId)) { + Class jsonPathClass = null; + try { + jsonPathClass = ClassUtils.forName("com.jayway.jsonpath.JsonPath", this.classLoader); + } + catch (ClassNotFoundException e) { + logger.debug("SpEL function '#jsonPath' isn't registered: there is no jayway json-path.jar on the classpath."); + } + + if (jsonPathClass != null) { + IntegrationNamespaceUtils.registerSpelFunctionBean(registry, jsonPathBeanName, + IntegrationNamespaceUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate"); + } + } + + alreadyRegistered = false; + String xpathBeanName = "xpath"; + if (registry instanceof ListableBeanFactory) { + alreadyRegistered = ((ListableBeanFactory) registry).containsBean(xpathBeanName); + } + else { + alreadyRegistered = registry.isBeanNameInUse(xpathBeanName); + } + if (!alreadyRegistered && !registriesProcessed.contains(registryId)) { + Class xpathClass = null; + try { + xpathClass = ClassUtils.forName(IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", + this.classLoader); + } + catch (ClassNotFoundException e) { + logger.debug("SpEL function '#xpath' isn't registered: there is no spring-integration-xml.jar on the classpath."); + } + + if (xpathClass != null) { + IntegrationNamespaceUtils.registerSpelFunctionBean(registry, xpathBeanName, + IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate"); + } + } + + registriesProcessed.add(registryId); + } + + /** + * Register {@code DefaultConfiguringBeanFactoryPostProcessor}, if necessary. + * + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerDefaultConfiguringBeanFactoryPostProcessor(BeanDefinitionRegistry registry) { + boolean alreadyRegistered = false; + if (registry instanceof ListableBeanFactory) { + alreadyRegistered = ((ListableBeanFactory) registry).containsBean(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); + } + else { + alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); + } + if (!alreadyRegistered) { + BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder + .genericBeanDefinition(IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml.DefaultConfiguringBeanFactoryPostProcessor"); + BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder( + postProcessorBuilder.getBeanDefinition(), IntegrationContextUtils.DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, registry); + } + } + + /** + * Register a {@link DefaultHeaderChannelRegistry} in the given {@link BeanDefinitionRegistry}, if necessary. + * + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerHeaderChannelRegistry(BeanDefinitionRegistry registry) { + boolean alreadyRegistered = false; + if (registry instanceof ListableBeanFactory) { + alreadyRegistered = ((ListableBeanFactory) registry).containsBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME); + } + else { + alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME); + } + if (!alreadyRegistered) { + if (logger.isInfoEnabled()) { + logger.info("No bean named '" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME + + "' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created."); + } + BeanDefinitionBuilder schedulerBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultHeaderChannelRegistry.class); + BeanDefinitionHolder replyChannelRegistryComponent = new BeanDefinitionHolder( + schedulerBuilder.getBeanDefinition(), + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME); + BeanDefinitionReaderUtils.registerBeanDefinition(replyChannelRegistryComponent, registry); + } + } + + /** + * Register {@link MessagingAnnotationPostProcessor} and {@link PublisherAnnotationBeanPostProcessor}, if necessary. + * Inject {@code defaultPublishedChannel} from provided {@link AnnotationMetadata}, if any. + * + * @param meta The {@link AnnotationMetadata} to get additional properties for {@link org.springframework.beans.factory.config.BeanDefinition}s. + * @param registry The {@link BeanDefinitionRegistry} to register additional {@link org.springframework.beans.factory.config.BeanDefinition}s. + */ + private void registerMessagingAnnotationPostProcessors(AnnotationMetadata meta, BeanDefinitionRegistry registry) { + if (!registry.containsBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME)) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MessagingAnnotationPostProcessor.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + registry.registerBeanDefinition(IntegrationContextUtils.MESSAGING_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition()); + } + + if (!registry.containsBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME)) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PublisherAnnotationBeanPostProcessor.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + Map attrs = meta.getAnnotationAttributes(EnableIntegration.class.getName()); + + String defaultPublisherChannel = (String) attrs.get("defaultPublisherChannel"); + if (StringUtils.hasText(defaultPublisherChannel)) { + builder.addPropertyReference("defaultChannel", defaultPublisherChannel); + } + + registry.registerBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition()); + } + + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/EnableIntegration.java b/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/EnableIntegration.java new file mode 100644 index 0000000000..b56b790a3d --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/annotation/EnableIntegration.java @@ -0,0 +1,48 @@ +/* + * Copyright 2014 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Import; +import org.springframework.integration.config.IntegrationRegistrar; + +/** + * Add this annotation to an {@code @Configuration} class to have + * the imported Spring Integration configuration : + *
+ * @Configuration
+ * @EnableIntegration
+ * @ComponentScan(basePackageClasses = { MyConfiguration.class })
+ * public class MyIntegrationConfiguration {
+ * }
+ * 
+ * + * @author Artem Bilan + * @since 4.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(IntegrationRegistrar.class) +public @interface EnableIntegration { + +} 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 e16b813260..2946c30432 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 @@ -33,6 +33,7 @@ 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.config.ConsumerEndpointFactoryBean; +import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import org.springframework.util.xml.DomUtils; @@ -122,9 +123,9 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit String inputChannelName = element.getAttribute(inputChannelAttributeName); if (!parserContext.getRegistry().containsBeanDefinition(inputChannelName)) { - if (parserContext.getRegistry().containsBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) { + if (parserContext.getRegistry().containsBeanDefinition(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME)) { BeanDefinition channelRegistry = parserContext.getRegistry(). - getBeanDefinition(ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); + getBeanDefinition(IntegrationContextUtils.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 @@ -137,7 +138,7 @@ public abstract class AbstractConsumerEndpointParser extends AbstractBeanDefinit } else { parserContext.getReaderContext().error("Failed to locate '" + - ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'", parserContext.getRegistry()); + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME + "'", parserContext.getRegistry()); } } 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 41a54e1213..621cc45cfe 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-2013 the original author or authors. + * Copyright 2002-2014 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,40 +16,19 @@ package org.springframework.integration.config.xml; -import java.io.IOException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; 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.PropertiesFactoryBean; -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.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.core.io.Resource; -import org.springframework.core.io.support.PathMatchingResourcePatternResolver; -import org.springframework.core.io.support.ResourcePatternResolver; -import org.springframework.integration.channel.DefaultHeaderChannelRegistry; -import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; -import org.springframework.integration.config.xml.ChannelInitializer.AutoCreateCandidatesCollector; -import org.springframework.integration.context.IntegrationContextUtils; -import org.springframework.integration.context.IntegrationProperties; -import org.springframework.integration.expression.IntegrationEvaluationContextAwareBeanPostProcessor; -import org.springframework.util.ClassUtils; +import org.springframework.integration.config.IntegrationRegistrar; import org.springframework.util.StringUtils; /** @@ -59,6 +38,7 @@ import org.springframework.util.StringUtils; * @author Mark Fisher * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan */ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHandler { @@ -66,238 +46,21 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa private static final String VERSION = "4.0"; - public static final String CHANNEL_INITIALIZER_BEAN_NAME = "channelInitializer"; - - private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME = - "DefaultConfiguringBeanFactoryPostProcessor"; - - private static final String DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME = - IntegrationNamespaceUtils.BASE_PACKAGE + ".internal" + DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME; - - private final NamespaceHandlerDelegate delegate = new NamespaceHandlerDelegate(); @Override public final BeanDefinition parse(Element element, ParserContext parserContext) { this.verifySchemaVersion(element, parserContext); - this.registerImplicitChannelCreator(parserContext); - this.registerIntegrationEvaluationContext(parserContext); - this.registerIntegrationProperties(parserContext); - this.registerHeaderChannelRegistry(parserContext); - this.registerBuiltInBeans(parserContext); - this.registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(parserContext); + new IntegrationRegistrar().registerBeanDefinitions(null, parserContext.getRegistry()); return this.delegate.parse(element, parserContext); } - private void registerIntegrationProperties(ParserContext parserContext) { - - boolean alreadyRegistered = false; - BeanDefinitionRegistry registry = parserContext.getRegistry(); - if (registry instanceof ListableBeanFactory) { - alreadyRegistered = ((ListableBeanFactory) registry) - .containsBean(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME); - } - else { - alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME); - } - if (!alreadyRegistered) { - ResourcePatternResolver resourceResolver = - new PathMatchingResourcePatternResolver(parserContext.getReaderContext().getBeanClassLoader()); - try { - Resource[] defaultResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.default.properties"); - Resource[] userResources = resourceResolver.getResources("classpath*:META-INF/spring.integration.properties"); - - List resources = new LinkedList(Arrays.asList(defaultResources)); - resources.addAll(Arrays.asList(userResources)); - - BeanDefinitionBuilder integrationPropertiesBuilder = BeanDefinitionBuilder - .genericBeanDefinition(PropertiesFactoryBean.class) - .addPropertyValue("locations", resources); - - registry.registerBeanDefinition(IntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME, - integrationPropertiesBuilder.getBeanDefinition()); - } - catch (IOException e) { - parserContext.getReaderContext().warning("Cannot load 'spring.integration.properties' Resources.", null, e); - } - } - } - @Override public final BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext) { 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) { - String channelsAutoCreateExpression = IntegrationProperties.getExpressionFor(IntegrationProperties.CHANNELS_AUTOCREATE); - BeanDefinitionBuilder channelDef = BeanDefinitionBuilder.genericBeanDefinition(ChannelInitializer.class) - .addPropertyValue("autoCreate", channelsAutoCreateExpression); - 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 the 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 registerIntegrationEvaluationContext(ParserContext parserContext) { - 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 the parent registry - alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBeanDefinition( - IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); - } - else { - alreadyRegistered = parserContext.getRegistry().isBeanNameInUse( - IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); - } - if (!alreadyRegistered) { - BeanDefinitionBuilder integrationEvaluationContextBuilder = BeanDefinitionBuilder - .genericBeanDefinition(IntegrationEvaluationContextFactoryBean.class); - integrationEvaluationContextBuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - BeanDefinitionHolder integrationEvaluationContextHolder = new BeanDefinitionHolder( - integrationEvaluationContextBuilder.getBeanDefinition(), - IntegrationContextUtils.INTEGRATION_EVALUATION_CONTEXT_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(integrationEvaluationContextHolder, - parserContext.getRegistry()); - RootBeanDefinition integrationEvalContextBPP = new RootBeanDefinition( - IntegrationEvaluationContextAwareBeanPostProcessor.class); - BeanDefinitionReaderUtils.registerWithGeneratedName(integrationEvalContextBPP, parserContext.getRegistry()); - } - } - - private void registerBuiltInBeans(ParserContext parserContext) { - String jsonPathBeanName = "jsonPath"; - boolean alreadyRegistered = false; - if (parserContext.getRegistry() instanceof ListableBeanFactory) { - alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(jsonPathBeanName); - } - else { - alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(jsonPathBeanName); - } - if (!alreadyRegistered) { - Class jsonPathClass = null; - try { - jsonPathClass = ClassUtils.forName("com.jayway.jsonpath.JsonPath", parserContext.getReaderContext().getBeanClassLoader()); - } - catch (ClassNotFoundException e) { - logger.debug("SpEL function '#jsonPath' isn't registered: there is no jayway json-path.jar on the classpath."); - } - - if (jsonPathClass != null) { - IntegrationNamespaceUtils.registerSpelFunctionBean(parserContext.getRegistry(), jsonPathBeanName, - IntegrationNamespaceUtils.BASE_PACKAGE + ".json.JsonPathUtils", "evaluate"); - } - } - - String xpathBeanName = "xpath"; - alreadyRegistered = false; - if (parserContext.getRegistry() instanceof ListableBeanFactory) { - alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(xpathBeanName); - } - else { - alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(xpathBeanName); - } - if (!alreadyRegistered) { - Class xpathClass = null; - try { - xpathClass = ClassUtils.forName(IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", - parserContext.getReaderContext().getBeanClassLoader()); - } - catch (ClassNotFoundException e) { - logger.debug("SpEL function '#xpath' isn't registered: there is no spring-integration-xml.jar on the classpath."); - } - - if (xpathClass != null) { - IntegrationNamespaceUtils.registerSpelFunctionBean(parserContext.getRegistry(), xpathBeanName, - IntegrationNamespaceUtils.BASE_PACKAGE + ".xml.xpath.XPathUtils", "evaluate"); - } - } - - - this.doRegisterBuiltInBeans(parserContext); - } - - protected void doRegisterBuiltInBeans(ParserContext parserContext) { - - } - - private void registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(ParserContext parserContext) { - boolean alreadyRegistered = false; - if (parserContext.getRegistry() instanceof ListableBeanFactory) { - alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()).containsBean(DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); - } - else { - alreadyRegistered = parserContext.getRegistry().isBeanNameInUse(DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); - } - if (!alreadyRegistered) { - BeanDefinitionBuilder postProcessorBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationNamespaceUtils.BASE_PACKAGE + ".config.xml." + DEFAULT_CONFIGURING_POSTPROCESSOR_SIMPLE_CLASS_NAME); - BeanDefinitionHolder postProcessorHolder = new BeanDefinitionHolder( - postProcessorBuilder.getBeanDefinition(), DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(postProcessorHolder, parserContext.getRegistry()); - } - } - - /** - * Register a DefaultHeaderChannelRegistry in the given BeanDefinitionRegistry, if necessary. - */ - private void registerHeaderChannelRegistry(ParserContext parserContext) { - boolean alreadyRegistered = false; - if (parserContext.getRegistry() instanceof ListableBeanFactory) { - alreadyRegistered = ((ListableBeanFactory) parserContext.getRegistry()) - .containsBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME); - } - else { - alreadyRegistered = parserContext.getRegistry().isBeanNameInUse( - IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME); - } - if (!alreadyRegistered) { - if (logger.isInfoEnabled()) { - logger.info("No bean named '" + IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME + - "' has been explicitly defined. Therefore, a default DefaultHeaderChannelRegistry will be created."); - } - BeanDefinitionBuilder schedulerBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultHeaderChannelRegistry.class); - BeanDefinitionHolder replyChannelRegistryComponent = new BeanDefinitionHolder( - schedulerBuilder.getBeanDefinition(), - IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(replyChannelRegistryComponent, parserContext.getRegistry()); - } - } - - protected final void registerBeanDefinitionDecorator(String elementName, BeanDefinitionDecorator decorator) { this.delegate.doRegisterBeanDefinitionDecorator(elementName, decorator); } @@ -346,4 +109,5 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa } } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java index 7d5a2b7ced..4dd70cf4c2 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/xml/AnnotationConfigParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2010 the original author or authors. + * Copyright 2002-2014 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,42 +16,36 @@ package org.springframework.integration.config.xml; +import java.util.Collections; +import java.util.Map; + import org.w3c.dom.Element; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.BeanDefinitionParser; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.util.StringUtils; +import org.springframework.core.type.StandardAnnotationMetadata; +import org.springframework.integration.config.IntegrationRegistrar; /** * Parser for the <annotation-config> element of the integration namespace. - * Adds a {@link org.springframework.integration.config.annotation.MessagingAnnotationPostProcessor} - * and a {@link org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor} - * to the application context. - * + * Just delegate the real configuration to the {@link IntegrationRegistrar}. + * * @author Mark Fisher + * @author Artem Bilan */ public class AnnotationConfigParser implements BeanDefinitionParser { - public BeanDefinition parse(Element element, ParserContext parserContext) { - RootBeanDefinition messagingAnnotationPostProcessorDef = new RootBeanDefinition( - IntegrationNamespaceUtils.BASE_PACKAGE + ".config.annotation.MessagingAnnotationPostProcessor"); - messagingAnnotationPostProcessorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - String messagingAnnotationPostProcessorName = IntegrationNamespaceUtils.BASE_PACKAGE + ".internalMessagingAnnotationPostProcessor"; - parserContext.getRegistry().registerBeanDefinition(messagingAnnotationPostProcessorName, messagingAnnotationPostProcessorDef); - RootBeanDefinition publisherAnnotationPostProcessorDef = new RootBeanDefinition( - IntegrationNamespaceUtils.BASE_PACKAGE + ".aop.PublisherAnnotationBeanPostProcessor"); - publisherAnnotationPostProcessorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); - String defaultPublisherChannel = element.getAttribute("default-publisher-channel"); - if (StringUtils.hasText(defaultPublisherChannel)) { - publisherAnnotationPostProcessorDef.getPropertyValues().add("defaultChannel", new RuntimeBeanReference(defaultPublisherChannel)); - } - String publisherAnnotationPostProcessorName = IntegrationNamespaceUtils.BASE_PACKAGE + ".internalPublisherAnnotationBeanPostProcessor"; - parserContext.getRegistry().registerBeanDefinition(publisherAnnotationPostProcessorName, publisherAnnotationPostProcessorDef); + public BeanDefinition parse(final Element element, ParserContext parserContext) { + new IntegrationRegistrar().registerBeanDefinitions(new StandardAnnotationMetadata(Object.class) { + + @Override + public Map getAnnotationAttributes(String annotationType) { + return Collections. singletonMap("defaultPublisherChannel", element.getAttribute("default-publisher-channel")); + } + }, parserContext.getRegistry()); + return null; } - } 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 index 7b81cad0b4..9c893a8d0c 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -25,6 +25,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.util.Assert; /** @@ -42,8 +43,6 @@ import org.springframework.util.Assert; */ final class ChannelInitializer implements BeanFactoryAware, InitializingBean { - public static String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates"; - private Log logger = LogFactory.getLog(this.getClass()); private volatile BeanFactory beanFactory; @@ -66,9 +65,8 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean { } else { AutoCreateCandidatesCollector channelCandidatesCollector = - beanFactory.getBean(AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class); - Assert.notNull(channelCandidatesCollector, "Failed to locate '" + - ChannelInitializer.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); + beanFactory.getBean(IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME, AutoCreateCandidatesCollector.class); + Assert.notNull(channelCandidatesCollector, "Failed to locate '" + IntegrationContextUtils.AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME); // at this point channelNames are all resolved with placeholders and SpEL Collection channelNames = channelCandidatesCollector.getChannelNames(); if (channelNames != null){ @@ -98,5 +96,7 @@ final class ChannelInitializer implements BeanFactoryAware, InitializingBean { public Collection getChannelNames() { return channelNames; } + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java index f1d45ad71f..02d6fde81f 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationContextUtils.java @@ -21,6 +21,7 @@ import java.util.Properties; import org.springframework.beans.factory.BeanFactory; import org.springframework.core.convert.ConversionService; import org.springframework.expression.spel.support.StandardEvaluationContext; +import org.springframework.integration.config.xml.IntegrationNamespaceUtils; import org.springframework.integration.metadata.MetadataStore; import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.TaskScheduler; @@ -51,6 +52,20 @@ public abstract class IntegrationContextUtils { public static final String INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAME = "integrationGlobalProperties"; + public static final String CHANNEL_INITIALIZER_BEAN_NAME = "channelInitializer"; + + public static final String AUTO_CREATE_CHANNEL_CANDIDATES_BEAN_NAME = "$autoCreateChannelCandidates"; + + public static final String DEFAULT_CONFIGURING_POSTPROCESSOR_BEAN_NAME = "DefaultConfiguringBeanFactoryPostProcessor"; + + public static final String MESSAGING_ANNOTATION_POSTPROCESSOR_NAME = IntegrationNamespaceUtils.BASE_PACKAGE + + ".internalMessagingAnnotationPostProcessor"; + + public static final String PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME = IntegrationNamespaceUtils.BASE_PACKAGE + + ".internalPublisherAnnotationBeanPostProcessor"; + +// public static final String FLOW_POST_PROCESSOR_BEAN_NAME = IntegrationFlowBeanFactoryPostProcessor.class.getSimpleName(); + /** * @param beanFactory BeanFactory for lookup, must not be null. * @return The {@link MetadataStore} bean whose name is "metadataStore". diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java new file mode 100644 index 0000000000..bfb9152288 --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests.java @@ -0,0 +1,108 @@ +/* + * Copyright 2014 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.configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.integration.annotation.MessageEndpoint; +import org.springframework.integration.annotation.Payload; +import org.springframework.integration.annotation.Publisher; +import org.springframework.integration.annotation.ServiceActivator; +import org.springframework.integration.channel.DirectChannel; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.annotation.EnableIntegration; +import org.springframework.integration.support.MessageBuilder; +import org.springframework.messaging.Message; +import org.springframework.messaging.MessageChannel; +import org.springframework.messaging.PollableChannel; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +/** + * @author Artem Bilan + * @since 4.0 + */ +@ContextConfiguration(loader = AnnotationConfigContextLoader.class) +@RunWith(SpringJUnit4ClassRunner.class) +public class EnableIntegrationTests { + + @Autowired + private MessageChannel input; + + @Autowired + private PollableChannel output; + + @Autowired + private PollableChannel publishedChannel; + + @Test + public void testAnnotatedServiceActivator() { + this.input.send(MessageBuilder.withPayload("Foo").build()); + Message receive = this.output.receive(1000); + assertNotNull(receive); + assertEquals("FOO", receive.getPayload()); + + receive = this.publishedChannel.receive(1000); + assertNotNull(receive); + assertEquals("foo", receive.getPayload()); + } + + + @Configuration + @ComponentScan(basePackageClasses = EnableIntegrationTests.class) + @EnableIntegration + public static class ContextConfiguration { + + @Bean + public MessageChannel input() { + return new DirectChannel(); + } + + @Bean + public PollableChannel output() { + return new QueueChannel(); + } + + @Bean + public PollableChannel publishedChannel() { + return new QueueChannel(); + } + + } + + @MessageEndpoint + public static class AnnotationTestService { + + @ServiceActivator(inputChannel = "input", outputChannel = "output") + @Publisher(channel = "publishedChannel") + @Payload("#args[0].toLowerCase()") + public String handle(String payload) { + return payload.toUpperCase(); + } + + } + +} diff --git a/src/reference/docbook/overview.xml b/src/reference/docbook/overview.xml index 3425f2f57e..6b8fa3330e 100644 --- a/src/reference/docbook/overview.xml +++ b/src/reference/docbook/overview.xml @@ -318,4 +318,32 @@ +
+ Configuration + + Throughout this document you will see references to XML namespace support for declaring elements in a Spring + Integration flow. This support is provided by a series of namespace parsers that generate appropriate + bean definitions to implement a particular component. For example, many endpoints consist of a + MessageHandler bean and a ConsumerEndpointFactoryBean + into which the handler and an input channel name are injected. + + + The first time a Spring Integration namespace element is encountered, the framework automatically declares + a number of beans that are used to support the runtime environment (task scheduler, + implicit channel creator, etc). + + + Starting with version 4.0, these support beans can also be defined when using + @Configuration classes, by adding a new annotation @EnableIntegration. + This is useful when declaring a simple Spring Integration flow using purely Java Configuration. + For example; you can declare an endpoint with a MessageHandler @Bean + as well as a ConsumerEndpointFactoryBean @Bean. + + + @EnableIntegration is also useful when you have a parent context with no Spring Integration + components and 2 or more child contexts that do use Spring Integration. It would enable these common + components to be declared once only, in the parent context. + +
+ diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 52401e7672..6bad8d8e8e 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -23,15 +23,26 @@
General Changes - - Core messaging abstractions (Message, - MessageChannel etc) have moved to the Spring - Framework spring-messaging module. Users who reference these - classes directly in their code will need to make changes as described in - the first section of the - Migration Guide. - +
+ Requires Spring Framework 4.0 + + Core messaging abstractions (Message, + MessageChannel etc) have moved to the Spring + Framework spring-messaging module. Users who reference these + classes directly in their code will need to make changes as described in + the first section of the + Migration Guide. + +
+
+ @EnableConfiguration + + The @EnableIntegration annotation has been added, to permit declaration of + standard Spring Integration beans when using @Configuration classes. See + for more information. + +
Header Type for XPath Header Enricher