From 23a26ca38e3669082360a6379dae6edc97a30185 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Mon, 17 Mar 2014 18:24:18 +0200 Subject: [PATCH] INT-3328: Add `EnablePublisher` Annotation JIRA: https://jira.spring.io/browse/INT-3328 INT-3328: Polishing --- .../integration/config/EnablePublisher.java | 48 +++++++++++ .../config/IntegrationRegistrar.java | 17 +--- .../config/PublisherRegistrar.java | 84 +++++++++++++++++++ .../config/xml/AnnotationConfigParser.java | 2 +- .../EnableIntegrationTests-context.xml | 2 + .../configuration/EnableIntegrationTests.java | 4 +- src/reference/docbook/overview.xml | 12 ++- src/reference/docbook/whats-new.xml | 8 ++ 8 files changed, 157 insertions(+), 20 deletions(-) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/EnablePublisher.java create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/EnablePublisher.java b/spring-integration-core/src/main/java/org/springframework/integration/config/EnablePublisher.java new file mode 100644 index 0000000000..2f0db8a10d --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/EnablePublisher.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; + +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; + +/** + * Provides the registration for the {@link org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor} + * to allow the use of the {@link org.springframework.integration.annotation.Publisher} annotation. + * In addition the {@code default-publisher-channel} name has to be configured as the {@code value} of this annotation. + *

+ * Note: the {@link org.springframework.integration.annotation.Publisher} annotation is enabled by default via + * {@link EnableIntegration} processing, but there is no hook to configure the {@code default-publisher-channel}. + * + * @author Artem Bilan + * @since 4.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(PublisherRegistrar.class) +public @interface EnablePublisher { + + /** + * @return the {@code default-publisher-channel} name. + */ + String value(); +} 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 index 3266247181..511f1f25c6 100644 --- 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 @@ -21,7 +21,6 @@ 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; @@ -51,7 +50,6 @@ import org.springframework.integration.expression.IntegrationEvaluationContextAw import org.springframework.integration.support.DefaultMessageBuilderFactory; import org.springframework.integration.support.converter.DefaultDatatypeChannelMessageConverter; import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; /** * {@link ImportBeanDefinitionRegistrar} implementation that configures integration infrastructure. @@ -302,20 +300,7 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean 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()); - } - + new PublisherRegistrar().registerBeanDefinitions(meta, registry); } /** diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java b/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java new file mode 100644 index 0000000000..49fa77a297 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/PublisherRegistrar.java @@ -0,0 +1,84 @@ +/* + * 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.util.Map; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.MutablePropertyValues; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.util.StringUtils; + +/** + * @author Artem Bilan + * @since 4.0 + */ +public class PublisherRegistrar implements ImportBeanDefinitionRegistrar { + + private static final Log logger = LogFactory.getLog(PublisherRegistrar.class); + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + Map annotationAttributes = importingClassMetadata.getAnnotationAttributes(EnablePublisher.class.getName()); + if (annotationAttributes == null) { + return; + } + String value = (String) annotationAttributes.get("value"); + if (!registry.containsBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME)) { + BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(PublisherAnnotationBeanPostProcessor.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + + if (StringUtils.hasText(value)) { + builder.addPropertyReference("defaultChannel", value); + if (logger.isInfoEnabled()) { + logger.info("Setting '@Publisher' default-output-channel to '" + value + "'."); + } + } + + registry.registerBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME, builder.getBeanDefinition()); + } + else { + BeanDefinition beanDefinition = registry.getBeanDefinition(IntegrationContextUtils.PUBLISHER_ANNOTATION_POSTPROCESSOR_NAME); + MutablePropertyValues propertyValues = beanDefinition.getPropertyValues(); + RuntimeBeanReference defaultChannel = (RuntimeBeanReference) propertyValues.getPropertyValue("defaultChannel").getValue(); + if (StringUtils.hasText(value)) { + if (defaultChannel == null) { + propertyValues.addPropertyValue("defaultChannel", new RuntimeBeanReference(value)); + if (logger.isInfoEnabled()) { + logger.info("Setting '@Publisher' default-output-channel to '" + value + "'."); + } + } + else if (!value.equals(defaultChannel.getBeanName())) { + throw new BeanDefinitionStoreException("When more than one enable publisher definition " + + "(@EnablePublisher or )" + + " is found in the context, they all must have the same 'default-publisher-channel' value."); + } + } + } + } + +} 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 4dd70cf4c2..3147f73f7e 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 @@ -41,7 +41,7 @@ public class AnnotationConfigParser implements BeanDefinitionParser { @Override public Map getAnnotationAttributes(String annotationType) { - return Collections. singletonMap("defaultPublisherChannel", element.getAttribute("default-publisher-channel")); + return Collections. singletonMap("value", element.getAttribute("default-publisher-channel")); } }, parserContext.getRegistry()); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml index 38ca103759..9bcd29be09 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml +++ b/spring-integration-core/src/test/java/org/springframework/integration/configuration/EnableIntegrationTests-context.xml @@ -8,4 +8,6 @@ + + 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 index 4c758df43f..ee9c637150 100644 --- 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 @@ -47,6 +47,7 @@ import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; import org.springframework.integration.config.EnableIntegration; import org.springframework.integration.config.EnableMessageHistory; +import org.springframework.integration.config.EnablePublisher; import org.springframework.integration.history.MessageHistory; import org.springframework.integration.history.MessageHistoryConfigurer; import org.springframework.integration.support.MessageBuilder; @@ -152,6 +153,7 @@ public class EnableIntegrationTests { @EnableIntegration @ImportResource("classpath:org/springframework/integration/configuration/EnableIntegrationTests-context.xml") @EnableMessageHistory("${message.history.tracked.components}") + @EnablePublisher("publishedChannel") public static class ContextConfiguration2 { @Bean @@ -175,7 +177,7 @@ public class EnableIntegrationTests { public static class AnnotationTestService { @ServiceActivator(inputChannel = "input", outputChannel = "output") - @Publisher(channel = "publishedChannel") + @Publisher @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 0b9b7775c6..c5ffbbd2d7 100644 --- a/src/reference/docbook/overview.xml +++ b/src/reference/docbook/overview.xml @@ -345,12 +345,20 @@ components to be declared once only, in the parent context. - The @IntegrationComponentScan annotation has also been introduced to permit classpath + The @IntegrationComponentScan annotation has also been introduced to permit classpath scanning. This annotation plays a similar role as the standard Spring Framework @ComponentScan annotation, but it is restricted just to Spring Integration specific components and annotations, which aren't reachable by the standard Spring Framework component scan mechanism. For example . - + + + The @EnablePublisher annotation has been introduced to register a + PublisherAnnotationBeanPostProcessor bean and configure the default-publisher-channel + for those @Publisher annotations which are provided without a channel attribute. + If more than one @EnablePublisher annotation is found, they must all have the same value + for the default channel. + See for more information. + diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 41bc362a7d..b0acab053f 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -65,6 +65,14 @@ >Spring Boot - AutoConfigure. +

+ @EnablePublisher + + The @EnablePublisher annotation has been added, to allow the specification of a + default-publisher-channel for @Publisher annotations. + See for more information. + +
Redis Channel Message Stores