From afb369c0eb93e5c22d8d61b064f785f9256d73bc Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Fri, 15 Nov 2013 18:30:13 +0200 Subject: [PATCH] INT-2500: Add Support for Global Properties `META-INF/spring.integration.properties` JIRA: https://jira.springsource.org/browse/INT-2500 --- .../AbstractIntegrationNamespaceHandler.java | 45 ++++++++++++++++ .../context/IntegrationContextUtils.java | 25 +++++++++ .../context/IntegrationObjectSupport.java | 10 ++++ .../context/IntegrationProperties.java | 29 ++++++++++ .../spring.integration.default.properties | 1 + .../IntegrationContextTests-context.xml | 14 +++++ .../context/IntegrationContextTests.java | 54 +++++++++++++++++++ .../META-INF/spring.integration.properties | 1 + 8 files changed, 179 insertions(+) create mode 100644 spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java create mode 100644 spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests-context.xml create mode 100644 spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests.java create mode 100644 spring-integration-core/src/test/resources/META-INF/spring.integration.properties 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 ba610212cf..052b644dbb 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 @@ -16,6 +16,11 @@ 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; @@ -24,8 +29,10 @@ 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; @@ -33,6 +40,9 @@ 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.registry.DefaultHeaderChannelRegistry; import org.springframework.integration.config.IntegrationEvaluationContextFactoryBean; import org.springframework.integration.config.xml.ChannelInitializer.AutoCreateCandidatesCollector; @@ -72,12 +82,47 @@ public abstract class AbstractIntegrationNamespaceHandler implements NamespaceHa this.verifySchemaVersion(element, parserContext); this.registerImplicitChannelCreator(parserContext); this.registerIntegrationEvaluationContext(parserContext); + this.registerIntegrationProperties(parserContext); this.registerHeaderChannelRegistry(parserContext); this.registerBuiltInBeans(parserContext); this.registerDefaultConfiguringBeanFactoryPostProcessorIfNecessary(parserContext); 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_PROPERTIES_BEAN_NAME); + } + else { + alreadyRegistered = registry.isBeanNameInUse(IntegrationContextUtils.INTEGRATION_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_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); 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 f432a0ea5e..67c5a2c9ef 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 @@ -16,6 +16,8 @@ package org.springframework.integration.context; +import java.util.Properties; + import org.springframework.beans.factory.BeanFactory; import org.springframework.core.convert.ConversionService; import org.springframework.expression.spel.support.StandardEvaluationContext; @@ -47,6 +49,9 @@ public abstract class IntegrationContextUtils { public static final String INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME = "integrationHeaderChannelRegistry"; + public static final String INTEGRATION_PROPERTIES_BEAN_NAME = "integrationProperties"; + + private static final Properties EMPTY_PROPERTIES = new Properties(); /** * Return the {@link MetadataStore} bean whose name is "metadataStore". @@ -107,4 +112,24 @@ public abstract class IntegrationContextUtils { return beanFactory.getBean(beanName, type); } + /** + * @return the global {@link IntegrationContextUtils#INTEGRATION_PROPERTIES_BEAN_NAME} + * bean from provided {@code #beanFactory}, which represents the merged + * properties values from all 'META-INF/spring.integration.default.properties' + * and 'META-INF/spring.integration.properties'. + * May return {@link IntegrationContextUtils#EMPTY_PROPERTIES} if there is no + * {@link IntegrationContextUtils#INTEGRATION_PROPERTIES_BEAN_NAME} bean within + * provided {@code #beanFactory} or provided {@code #beanFactory} is null. + */ + public static Properties getIntegrationProperties(BeanFactory beanFactory) { + Properties properties = null; + if (beanFactory != null) { + properties = getBeanOfType(beanFactory, INTEGRATION_PROPERTIES_BEAN_NAME, Properties.class); + } + if (properties == null) { + properties = EMPTY_PROPERTIES; + } + return properties; + } + } diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java index f3f7756e60..18a3051927 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationObjectSupport.java @@ -16,6 +16,8 @@ package org.springframework.integration.context; +import java.util.Properties; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -47,6 +49,7 @@ import org.springframework.util.StringUtils; * @author Josh Long * @author Stefan Ferstl * @author Gary Russell + * @author Artem Bilan */ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedComponent, ApplicationContextAware, BeanFactoryAware, InitializingBean { @@ -169,6 +172,13 @@ public abstract class IntegrationObjectSupport implements BeanNameAware, NamedCo return this.applicationContext == null ? null : this.applicationContext.getId(); } + /** + * @see IntegrationContextUtils#getIntegrationProperties + */ + protected Properties getIntegrationProperties() { + return IntegrationContextUtils.getIntegrationProperties(this.beanFactory); + } + @Override public String toString() { return (this.beanName != null) ? this.beanName : super.toString(); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java new file mode 100644 index 0000000000..fb86ed38f3 --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/context/IntegrationProperties.java @@ -0,0 +1,29 @@ +/* + * Copyright 2013 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.context; + +/** + * Convention Enumeration to represent keys from 'META-INF/spring.integration.properties'. + * + * @author Artem Bilan + * @since 3.0 + */ +public interface IntegrationProperties { + + String LATE_REPLY_LOGGING_LEVEL = "messagingTemplate.lateReply.logging.level"; + +} diff --git a/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties b/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties new file mode 100644 index 0000000000..f8e341af95 --- /dev/null +++ b/spring-integration-core/src/main/resources/META-INF/spring.integration.default.properties @@ -0,0 +1 @@ +messagingTemplate.lateReply.logging.level=warn diff --git a/spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests-context.xml b/spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests-context.xml new file mode 100644 index 0000000000..4bbd23523b --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests-context.xml @@ -0,0 +1,14 @@ + + + + + + + + diff --git a/spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests.java b/spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests.java new file mode 100644 index 0000000000..64d9ee572e --- /dev/null +++ b/spring-integration-core/src/test/java/org/springframework/integration/context/IntegrationContextTests.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013 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.context; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; + +import java.util.Properties; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Artem Bilan + * @since 3.0 + */ +@ContextConfiguration +@RunWith(SpringJUnit4ClassRunner.class) +public class IntegrationContextTests { + + @Autowired + @Qualifier(IntegrationContextUtils.INTEGRATION_PROPERTIES_BEAN_NAME) + private Properties integrationProperties; + + @Autowired + @Qualifier("fooService") + private IntegrationObjectSupport serviceActivator; + + @Test + public void testIntegrationContextComponents() { + assertEquals("error", this.integrationProperties.get(IntegrationProperties.LATE_REPLY_LOGGING_LEVEL)); + assertSame(this.integrationProperties, this.serviceActivator.getIntegrationProperties()); + } + +} diff --git a/spring-integration-core/src/test/resources/META-INF/spring.integration.properties b/spring-integration-core/src/test/resources/META-INF/spring.integration.properties new file mode 100644 index 0000000000..89b14918d2 --- /dev/null +++ b/spring-integration-core/src/test/resources/META-INF/spring.integration.properties @@ -0,0 +1 @@ +messagingTemplate.lateReply.logging.level=error