diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java index 36a430e56d..a2a21181b5 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/DefaultConfiguringBeanFactoryPostProcessor.java @@ -31,9 +31,13 @@ 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.RootBeanDefinition; +import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; +import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; import org.springframework.integration.context.IntegrationProperties; +import org.springframework.integration.handler.LoggingHandler; +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; /** * A {@link BeanFactoryPostProcessor} implementation that provides default beans for the error handling and task @@ -126,21 +130,19 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce logger.info("No bean named '" + IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME + "' has been explicitly defined. Therefore, a default PublishSubscribeChannel will be created."); } - RootBeanDefinition errorChannelDef = new RootBeanDefinition(); - errorChannelDef.setBeanClassName(IntegrationConfigUtils.BASE_PACKAGE - + ".channel.PublishSubscribeChannel"); - BeanDefinitionHolder errorChannelHolder = new BeanDefinitionHolder(errorChannelDef, - IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(errorChannelHolder, registry); - BeanDefinitionBuilder loggingHandlerBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationConfigUtils.BASE_PACKAGE + ".handler.LoggingHandler"); - loggingHandlerBuilder.addConstructorArgValue("ERROR"); - BeanDefinitionBuilder loggingEndpointBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationConfigUtils.BASE_PACKAGE + ".endpoint.EventDrivenConsumer"); - loggingEndpointBuilder.addConstructorArgReference(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME); - loggingEndpointBuilder.addConstructorArgValue(loggingHandlerBuilder.getBeanDefinition()); - BeanComponentDefinition componentDefinition = new BeanComponentDefinition( - loggingEndpointBuilder.getBeanDefinition(), ERROR_LOGGER_BEAN_NAME); + registry.registerBeanDefinition(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME, + new RootBeanDefinition(PublishSubscribeChannel.class)); + + BeanDefinitionBuilder loggingHandlerBuilder = + BeanDefinitionBuilder.genericBeanDefinition(LoggingHandler.class).addConstructorArgValue("ERROR"); + + BeanDefinitionBuilder loggingEndpointBuilder = + BeanDefinitionBuilder.genericBeanDefinition(ConsumerEndpointFactoryBean.class) + .addPropertyValue("handler", loggingHandlerBuilder.getBeanDefinition()) + .addPropertyValue("inputChannelName", IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME); + + BeanComponentDefinition componentDefinition = + new BeanComponentDefinition(loggingEndpointBuilder.getBeanDefinition(), ERROR_LOGGER_BEAN_NAME); BeanDefinitionReaderUtils.registerBeanDefinition(componentDefinition, registry); } @@ -152,19 +154,14 @@ class DefaultConfiguringBeanFactoryPostProcessor implements BeanFactoryPostProce logger.info("No bean named '" + IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME + "' has been explicitly defined. Therefore, a default ThreadPoolTaskScheduler will be created."); } - BeanDefinitionBuilder schedulerBuilder = BeanDefinitionBuilder.genericBeanDefinition( - "org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler"); - String taskSchedulerPoolSizeExpression = IntegrationProperties.getExpressionFor(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE); - schedulerBuilder.addPropertyValue("poolSize", taskSchedulerPoolSizeExpression); - schedulerBuilder.addPropertyValue("threadNamePrefix", "task-scheduler-"); - schedulerBuilder.addPropertyValue("rejectedExecutionHandler", new CallerRunsPolicy()); - BeanDefinitionBuilder errorHandlerBuilder = BeanDefinitionBuilder.genericBeanDefinition( - IntegrationConfigUtils.BASE_PACKAGE + ".channel.MessagePublishingErrorHandler"); - errorHandlerBuilder.addPropertyReference("defaultErrorChannel", IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME); - schedulerBuilder.addPropertyValue("errorHandler", errorHandlerBuilder.getBeanDefinition()); - BeanComponentDefinition schedulerComponent = new BeanComponentDefinition( - schedulerBuilder.getBeanDefinition(), IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); - BeanDefinitionReaderUtils.registerBeanDefinition(schedulerComponent, registry); + BeanDefinition scheduler = BeanDefinitionBuilder.genericBeanDefinition(ThreadPoolTaskScheduler.class) + .addPropertyValue("poolSize", IntegrationProperties.getExpressionFor(IntegrationProperties.TASK_SCHEDULER_POOL_SIZE)) + .addPropertyValue("threadNamePrefix", "task-scheduler-") + .addPropertyValue("rejectedExecutionHandler", new CallerRunsPolicy()) + .addPropertyValue("errorHandler", new RootBeanDefinition(MessagePublishingErrorHandler.class)) + .getBeanDefinition(); + + registry.registerBeanDefinition(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME, scheduler); } } 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 511f1f25c6..155f2a49d1 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 @@ -80,6 +80,7 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { this.registerImplicitChannelCreator(registry); + this.registerIntegrationConfigurationBeanFactoryPostProcessor(registry); this.registerIntegrationEvaluationContext(registry); this.registerIntegrationProperties(registry); this.registerHeaderChannelRegistry(registry); @@ -89,7 +90,6 @@ public class IntegrationRegistrar implements ImportBeanDefinitionRegistrar, Bean if (importingClassMetadata != null) { this.registerMessagingAnnotationPostProcessors(importingClassMetadata, registry); } - this.registerIntegrationConfigurationBeanFactoryPostProcessor(registry); this.registerMessageBuilderFactory(registry); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessorTests.java index 3354e9dc5c..faf8783012 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/xml/DefaultConfiguringBeanFactoryPostProcessorTests.java @@ -16,9 +16,13 @@ package org.springframework.integration.config.xml; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -26,13 +30,13 @@ import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher @@ -64,18 +68,22 @@ public class DefaultConfiguringBeanFactoryPostProcessorTests { public void taskSchedulerRegistered() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } - + @Test public void taskSchedulerNotRegisteredMoreThanOnce() { ClassPathXmlApplicationContext superParentApplicationContext = new ClassPathXmlApplicationContext("superParentApplicationContext.xml", this.getClass()); - ClassPathXmlApplicationContext parentApplicationContext = + ClassPathXmlApplicationContext parentApplicationContext = new ClassPathXmlApplicationContext(new String[]{"org/springframework/integration/config/xml/parentApplicationContext.xml"}, superParentApplicationContext); - ClassPathXmlApplicationContext childApplicationContext = + ClassPathXmlApplicationContext childApplicationContext = new ClassPathXmlApplicationContext(new String[]{"org/springframework/integration/config/xml/childApplicationContext.xml"}, parentApplicationContext); TaskScheduler parentScheduler = childApplicationContext.getParent().getBean("taskScheduler", TaskScheduler.class); TaskScheduler childScheduler = childApplicationContext.getBean("taskScheduler", TaskScheduler.class); diff --git a/spring-integration-file/src/test/java/org/springframework/integration/file/config/DefaultConfigurationTests.java b/spring-integration-file/src/test/java/org/springframework/integration/file/config/DefaultConfigurationTests.java index 7a64ee3fa3..d8030386e5 100644 --- a/spring-integration-file/src/test/java/org/springframework/integration/file/config/DefaultConfigurationTests.java +++ b/spring-integration-file/src/test/java/org/springframework/integration/file/config/DefaultConfigurationTests.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2009 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. * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * 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, @@ -16,56 +16,65 @@ package org.springframework.integration.file.config; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class DefaultConfigurationTests { - @Autowired - private ApplicationContext context; + @Autowired + private ApplicationContext context; - @Test - public void verifyErrorChannel() { - Object errorChannel = context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME); - assertNotNull(errorChannel); - assertEquals(PublishSubscribeChannel.class, errorChannel.getClass()); - } + @Test + public void verifyErrorChannel() { + Object errorChannel = context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME); + assertNotNull(errorChannel); + assertEquals(PublishSubscribeChannel.class, errorChannel.getClass()); + } - @Test - public void verifyNullChannel() { - Object nullChannel = context.getBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME); - assertNotNull(nullChannel); - assertEquals(NullChannel.class, nullChannel.getClass()); - } + @Test + public void verifyNullChannel() { + Object nullChannel = context.getBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME); + assertNotNull(nullChannel); + assertEquals(NullChannel.class, nullChannel.getClass()); + } - @Test - public void verifyTaskScheduler() { - Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); - assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); - assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); - assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); - } + @Test + public void verifyTaskScheduler() { + Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); + assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); + assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); + assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); + } } diff --git a/spring-integration-http/src/test/java/org/springframework/integration/http/config/DefaultConfigurationTests.java b/spring-integration-http/src/test/java/org/springframework/integration/http/config/DefaultConfigurationTests.java index dab26b32cc..bf2284963b 100644 --- a/spring-integration-http/src/test/java/org/springframework/integration/http/config/DefaultConfigurationTests.java +++ b/spring-integration-http/src/test/java/org/springframework/integration/http/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.http.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/DefaultConfigurationTests.java b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/DefaultConfigurationTests.java index fea0cc00ef..ad1f37b16c 100644 --- a/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/DefaultConfigurationTests.java +++ b/spring-integration-jms/src/test/java/org/springframework/integration/jms/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.jms.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/EnableIntegrationMBeanExport.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/EnableIntegrationMBeanExport.java new file mode 100644 index 0000000000..73562db00b --- /dev/null +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/EnableIntegrationMBeanExport.java @@ -0,0 +1,79 @@ +/* + * 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.jmx.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; +import org.springframework.jmx.support.RegistrationPolicy; + +/** + * Enables default exporting for Spring Integration components in an existing application, as + * well as well all {@code @ManagedResource} annotated beans. + * + *

The resulting {@link org.springframework.integration.monitor.IntegrationMBeanExporter} + * bean is defined under the name {@code integrationMbeanExporter}. Alternatively, consider defining a + * custom {@link org.springframework.integration.monitor.IntegrationMBeanExporter} bean explicitly. + * + *

This annotation is modeled after and functionally equivalent to Spring XML's + * {@code } element. + * + * @author Artem Bilan + * @since 4.0 + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Import(IntegrationMBeanExportConfiguration.class) +public @interface EnableIntegrationMBeanExport { + + /** + * The default domain to use when generating JMX ObjectNames. + * Supports property placeholders (e.g. {@code ${project.domain}). + */ + String defaultDomain() default ""; + + /** + * The bean name of the MBeanServer to which MBeans should be exported. Default is to + * use the platform's default MBeanServer. + * Supports property placeholders (e.g. {@code ${project.mbeanServer}) + * and SpEL expression (e.g. {@code #{mbeanServer}). + */ + String server() default ""; + + /** + * The policy to use when attempting to register an MBean under an + * {@link javax.management.ObjectName} that already exists. Defaults to + * {@link org.springframework.jmx.support.RegistrationPolicy#FAIL_ON_EXISTING}. + */ + RegistrationPolicy registration() default RegistrationPolicy.FAIL_ON_EXISTING; + + /** + * List of simple patterns for component names to register (defaults to '*'). + * The pattern is applied to all components before they are registered, looking for a match on + * the 'name' property of the ObjectName. A MessageChannel and a MessageHandler (for instance) + * can share a name because they have a different type, so in that case they would either both + * be included or both excluded. + * Supports property placeholders (e.g. {@code ${managed.components}). Can be applied for each element. + */ + String[] managedComponents() default "*"; + +} diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java new file mode 100644 index 0000000000..a2e70eae6b --- /dev/null +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/IntegrationMBeanExportConfiguration.java @@ -0,0 +1,144 @@ +/* + * 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.jmx.config; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import javax.management.MBeanServer; +import javax.management.MBeanServerFactory; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.BeanExpressionContext; +import org.springframework.beans.factory.config.BeanExpressionResolver; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.context.EnvironmentAware; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportAware; +import org.springframework.context.annotation.Role; +import org.springframework.context.expression.StandardBeanExpressionResolver; +import org.springframework.core.annotation.AnnotationAttributes; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotationMetadata; +import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.jmx.support.RegistrationPolicy; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + +/** + * {@code @Configuration} class that registers a {@link IntegrationMBeanExporter} bean. + *

+ *

This configuration class is automatically imported when using the + * {@link EnableIntegrationMBeanExport} annotation. See its javadoc for complete usage details. + * + * @author Artem Bilan + * @since 4.0 + */ +@Configuration +public class IntegrationMBeanExportConfiguration implements ImportAware, EnvironmentAware, BeanFactoryAware { + + private static final String MBEAN_EXPORTER_NAME = "integrationMbeanExporter"; + + private AnnotationAttributes attributes; + + private BeanFactory beanFactory; + + private BeanExpressionResolver resolver = new StandardBeanExpressionResolver(); + + private BeanExpressionContext expressionContext; + + private Environment environment; + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = beanFactory; + if (beanFactory instanceof ConfigurableListableBeanFactory) { + this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver(); + this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null); + } + } + + @Override + public void setEnvironment(Environment environment) { + this.environment = environment; + } + + @Override + public void setImportMetadata(AnnotationMetadata importMetadata) { + Map map = importMetadata.getAnnotationAttributes(EnableIntegrationMBeanExport.class.getName()); + this.attributes = AnnotationAttributes.fromMap(map); + Assert.notNull(this.attributes, + "@EnableIntegrationMBeanExport is not present on importing class " + importMetadata.getClassName()); + } + + @Bean(name = MBEAN_EXPORTER_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public IntegrationMBeanExporter mbeanExporter() { + IntegrationMBeanExporter exporter = new IntegrationMBeanExporter(); + exporter.setRegistrationPolicy(this.attributes.getEnum("registration")); + setupDomain(exporter); + setupServer(exporter); + setupComponentNamePatterns(exporter); + return exporter; + } + + private void setupDomain(IntegrationMBeanExporter exporter) { + String defaultDomain = this.attributes.getString("defaultDomain"); + if (defaultDomain != null && this.environment != null) { + defaultDomain = this.environment.resolvePlaceholders(defaultDomain); + } + if (StringUtils.hasText(defaultDomain)) { + exporter.setDefaultDomain(defaultDomain); + } + } + + private void setupServer(IntegrationMBeanExporter exporter) { + String server = this.attributes.getString("server"); + if (server != null && this.environment != null) { + server = this.environment.resolvePlaceholders(server); + } + if (StringUtils.hasText(server)) { + MBeanServer bean = null; + if (server.startsWith("#{") && server.endsWith("}")) { + bean = (MBeanServer) this.resolver.evaluate(server, this.expressionContext); + } + else { + bean = this.beanFactory.getBean(server, MBeanServer.class); + } + exporter.setServer(bean); + } + else { + exporter.setServer(MBeanServerFactory.createMBeanServer()); + } + } + + private void setupComponentNamePatterns(IntegrationMBeanExporter exporter) { + List patterns = new ArrayList(); + String[] managedComponents = this.attributes.getStringArray("managedComponents"); + for (String managedComponent : managedComponents) { + String pattern = this.environment.resolvePlaceholders(managedComponent); + patterns.addAll(StringUtils.commaDelimitedListToSet(pattern)); + } + exporter.setComponentNamePatterns(patterns.toArray(new String[patterns.size()])); + } + +} diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/JmxIntegrationConfigurationInitializer.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/JmxIntegrationConfigurationInitializer.java index 78225b4594..ad62f6d659 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/JmxIntegrationConfigurationInitializer.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/JmxIntegrationConfigurationInitializer.java @@ -18,6 +18,8 @@ package org.springframework.integration.jmx.config; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.integration.config.IntegrationConfigurationInitializer; import org.springframework.integration.monitor.IntegrationMBeanExporter; @@ -37,11 +39,9 @@ public class JmxIntegrationConfigurationInitializer implements IntegrationConfig } private void registerMBeanExporterHelperIfNecessary(ConfigurableListableBeanFactory beanFactory) { - if (!beanFactory.getBeansOfType(IntegrationMBeanExporter.class, false, false).isEmpty()) { - MBeanExporterHelper mBeanExporterHelper = new MBeanExporterHelper(); - mBeanExporterHelper.postProcessBeanFactory(beanFactory); - beanFactory.registerSingleton(MBEAN_EXPORTER_HELPER_BEAN_NAME, mBeanExporterHelper); - beanFactory.initializeBean(mBeanExporterHelper, MBEAN_EXPORTER_HELPER_BEAN_NAME); + if (beanFactory.getBeanNamesForType(IntegrationMBeanExporter.class).length > 0) { + ((BeanDefinitionRegistry) beanFactory).registerBeanDefinition(MBEAN_EXPORTER_HELPER_BEAN_NAME, + new RootBeanDefinition(MBeanExporterHelper.class)); } } diff --git a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java index f25b838558..7fb50e065b 100644 --- a/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java +++ b/spring-integration-jmx/src/main/java/org/springframework/integration/jmx/config/MBeanExporterHelper.java @@ -1,11 +1,11 @@ /* - * Copyright 2002-2011 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. 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. @@ -20,42 +20,66 @@ import org.springframework.beans.BeansException; import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.core.Ordered; +import org.springframework.core.type.StandardMethodMetadata; import org.springframework.integration.monitor.IntegrationMBeanExporter; import org.springframework.jmx.export.MBeanExporter; +import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** * Most likely a temporary class mainly needed to address issue described in INT-2307. - * It helps in eliminating conflicts when more than one MBeanExporter is present. It creates a list - * of bean names that will be exported by the IntegrationMBeanExporter and merges it with the list + * It helps in eliminating conflicts when more than one MBeanExporter is present. It creates a list + * of bean names that will be exported by the IntegrationMBeanExporter and merges it with the list * of 'excludedBeans' of MBeanExporter so it will not attempt to export them again. * * @author Oleg Zhurakousky + * @author Artem Bilan * @since 2.1 * */ -class MBeanExporterHelper implements BeanFactoryPostProcessor, - BeanPostProcessor, Ordered, BeanFactoryAware { +class MBeanExporterHelper implements BeanPostProcessor, Ordered, BeanFactoryAware, InitializingBean { private final static String EXCLUDED_BEANS_PROPERTY_NAME = "excludedBeans"; - + private final static String SI_ROOT_PACKAGE = "org.springframework.integration."; private final Set siBeanNames = new HashSet(); - private volatile BeanFactory beanFactory; + private volatile DefaultListableBeanFactory beanFactory; private volatile boolean capturedAutoChannelCandidates; - + + @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { - this.beanFactory = beanFactory; + Assert.isInstanceOf(DefaultListableBeanFactory.class, beanFactory); + this.beanFactory = (DefaultListableBeanFactory) beanFactory; } + @Override + public void afterPropertiesSet() throws Exception { + if (this.beanFactory != null) { + String[] beanNames = this.beanFactory.getBeanDefinitionNames(); + for (String beanName : beanNames) { + BeanDefinition def = this.beanFactory.getBeanDefinition(beanName); + String className = def.getBeanClassName(); + if (className == null && def.getSource() instanceof StandardMethodMetadata) { + className = ((StandardMethodMetadata) def.getSource()).getIntrospectedMethod().getReturnType().getName(); + } + if (StringUtils.hasText(className)){ + if (className.startsWith(SI_ROOT_PACKAGE) && !(className.endsWith(IntegrationMBeanExporter.class.getName()))){ + siBeanNames.add(beanName); + } + } + } + } + } + + @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if (!this.capturedAutoChannelCandidates && this.beanFactory != null) { Object autoCreateChannelCandidates = beanFactory.getBean("$autoCreateChannelCandidates"); @@ -77,30 +101,18 @@ class MBeanExporterHelper implements BeanFactoryPostProcessor, } mbeDfa.setPropertyValue(EXCLUDED_BEANS_PROPERTY_NAME, siBeanNames); } + return bean; } - + @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } - - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames(); - for (String beanName : beanDefinitionNames) { - BeanDefinition bd = beanFactory.getMergedBeanDefinition(beanName); - - String className = bd.getBeanClassName(); - if (StringUtils.hasText(className)){ - if (className.startsWith(SI_ROOT_PACKAGE) && !(className.endsWith(IntegrationMBeanExporter.class.getName()))){ - siBeanNames.add(beanName); - } - } - } - } - + @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } + } diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MBeanExporterHelperTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MBeanExporterHelperTests.java deleted file mode 100644 index 1f8bd45152..0000000000 --- a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/config/MBeanExporterHelperTests.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2002-2011 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.jmx.config; - -import org.junit.Test; - -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; - -/** - * @author Oleg Zhurakousky - * - */ -public class MBeanExporterHelperTests { - - @Test - public void testNoNpeWhenClassNameIsMissing(){ - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - RootBeanDefinition bd = new RootBeanDefinition(); - factory.registerBeanDefinition("foo", bd); - - MBeanExporterHelper helper = new MBeanExporterHelper(); - helper.postProcessBeanFactory(factory); - } - - @Test - public void testNoNpeWithAbstractBean(){ - DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); - RootBeanDefinition abstractBd = new RootBeanDefinition(); - abstractBd.setAbstract(true); - abstractBd.setBeanClass(String.class); - factory.registerBeanDefinition("abstractFoo", abstractBd); - RootBeanDefinition concreteBd = new RootBeanDefinition(abstractBd); - - factory.registerBeanDefinition("concreteFoo", concreteBd); - - MBeanExporterHelper helper = new MBeanExporterHelper(); - helper.postProcessBeanFactory(factory); - } -} diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/configuration/EnableMBeanExportTests.java b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/configuration/EnableMBeanExportTests.java new file mode 100644 index 0000000000..7c605f7cb3 --- /dev/null +++ b/spring-integration-jmx/src/test/java/org/springframework/integration/jmx/configuration/EnableMBeanExportTests.java @@ -0,0 +1,139 @@ +/* + * 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.jmx.configuration; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + +import java.util.List; +import java.util.Set; + +import javax.management.MBeanServer; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; +import org.springframework.integration.monitor.IntegrationMBeanExporter; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.jmx.support.MBeanServerFactoryBean; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +/** + * @author Artem Bilan + * @since 4.0 + */ +@ContextConfiguration(initializers = EnableMBeanExportTests.EnvironmentApplicationContextInitializer.class) +@RunWith(SpringJUnit4ClassRunner.class) +@DirtiesContext +public class EnableMBeanExportTests { + + @Autowired + private BeanFactory beanFactory; + + @Autowired + private IntegrationMBeanExporter exporter; + + @Autowired + private MBeanServer mBeanServer; + + @SuppressWarnings("unchecked") + @Test + public void testEnableMBeanExport() throws MalformedObjectNameException, ClassNotFoundException { + assertTrue(AopUtils.isAopProxy(this.beanFactory.getBean(IntegrationContextUtils.NULL_CHANNEL_BEAN_NAME))); + assertTrue(AopUtils.isAopProxy(this.beanFactory.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME))); + + assertSame(this.mBeanServer, this.exporter.getServer()); + String[] componentNamePatterns = TestUtils.getPropertyValue(this.exporter, "componentNamePatterns", String[].class); + for (String componentNamePattern : componentNamePatterns) { + assertThat(componentNamePattern, Matchers.isOneOf("input", "in*")); + assertThat(componentNamePattern, Matchers.not(Matchers.equalTo("*"))); + } + Set names = this.mBeanServer.queryNames(ObjectName.getInstance("FOO:type=MessageChannel,*"), null); + // Only one registered (out of >2 available) + assertEquals(1, names.size()); + assertEquals("input", names.iterator().next().getKeyProperty("name")); + names = this.mBeanServer.queryNames(ObjectName.getInstance("FOO:type=MessageHandler,*"), null); + assertEquals(0, names.size()); + + Class clazz = Class.forName("org.springframework.integration.jmx.config.MBeanExporterHelper"); + List beanPostProcessors = TestUtils.getPropertyValue(beanFactory, "beanPostProcessors", List.class); + Object mBeanExporterHelper = null; + for (Object beanPostProcessor : beanPostProcessors) { + if (clazz.isAssignableFrom(beanPostProcessor.getClass())) { + mBeanExporterHelper = beanPostProcessor; + break; + } + } + assertNotNull(mBeanExporterHelper); + assertTrue(TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames", Set.class).contains("input")); + assertTrue(TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames", Set.class).contains("output")); + } + + @Configuration + @EnableIntegration + @EnableIntegrationMBeanExport(server = "#{mbeanServer}", defaultDomain = "${managed.domain}", managedComponents = {"input", "${managed.component}"}) + public static class ContextConfiguration { + + @Bean + public MBeanServerFactoryBean mbeanServer() { + return new MBeanServerFactoryBean(); + } + + @Bean + public QueueChannel input() { + return new QueueChannel(); + } + + @Bean + public QueueChannel output() { + return new QueueChannel(); + } + + } + + public static class EnvironmentApplicationContextInitializer implements ApplicationContextInitializer { + + @Override + public void initialize(GenericApplicationContext applicationContext) { + applicationContext.setEnvironment(new MockEnvironment() + .withProperty("managed.component", "input,in*") + .withProperty("managed.domain", "FOO")); + } + + } + +} diff --git a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java index b33c3b6a6f..7ce8d37637 100644 --- a/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.java +++ b/spring-integration-jmx/src/test/java/org/springframework/integration_/mbeanexporterhelper/Int2307Tests.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. You may obtain a copy of the License at @@ -13,6 +13,7 @@ package org.springframework.integration_.mbeanexporterhelper; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import java.util.List; @@ -33,6 +34,7 @@ import org.springframework.jmx.export.MBeanExporter; /** * @author Oleg Zhurakousky * @author Gary Russell + * @author Artem Bilan * */ public class Int2307Tests { @@ -70,9 +72,17 @@ public class Int2307Tests { assertEquals(4, count); Class clazz = Class.forName("org.springframework.integration.jmx.config.MBeanExporterHelper"); - Object mBeanExporterHelper = context.getBean(clazz); - assertTrue(((Set)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("z")); - assertTrue(((Set)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("zz")); + List beanPostProcessors = TestUtils.getPropertyValue(context, "beanFactory.beanPostProcessors", List.class); + Object mBeanExporterHelper = null; + for (Object beanPostProcessor : beanPostProcessors) { + if (clazz.isAssignableFrom(beanPostProcessor.getClass())) { + mBeanExporterHelper = beanPostProcessor; + break; + } + } + assertNotNull(mBeanExporterHelper); + assertTrue(TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames", Set.class).contains("z")); + assertTrue(TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames", Set.class).contains("zz")); // make sure there are no duplicate MBean ObjectNames if 2 contexts loaded from same config new ClassPathXmlApplicationContext("single-config.xml", this.getClass()); @@ -90,8 +100,16 @@ public class Int2307Tests { assertTrue(excludedBeanNames.contains("y")); assertTrue(excludedBeanNames.contains("foo")); // non SI bean Class clazz = Class.forName("org.springframework.integration.jmx.config.MBeanExporterHelper"); - Object mBeanExporterHelper = context.getBean(clazz); - assertTrue(((Set)TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames")).contains("z")); + List beanPostProcessors = TestUtils.getPropertyValue(context, "beanFactory.beanPostProcessors", List.class); + Object mBeanExporterHelper = null; + for (Object beanPostProcessor : beanPostProcessors) { + if (clazz.isAssignableFrom(beanPostProcessor.getClass())) { + mBeanExporterHelper = beanPostProcessor; + break; + } + } + assertNotNull(mBeanExporterHelper); + assertTrue(TestUtils.getPropertyValue(mBeanExporterHelper, "siBeanNames", Set.class).contains("z")); } public static class Foo{} diff --git a/spring-integration-jmx/src/test/resources/.svnignore b/spring-integration-jmx/src/test/resources/.svnignore deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/spring-integration-jmx/src/test/java/log4j.properties b/spring-integration-jmx/src/test/resources/log4j.properties similarity index 100% rename from spring-integration-jmx/src/test/java/log4j.properties rename to spring-integration-jmx/src/test/resources/log4j.properties diff --git a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/DefaultConfigurationTests.java b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/DefaultConfigurationTests.java index ec2646a815..e2d24a8d41 100644 --- a/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/DefaultConfigurationTests.java +++ b/spring-integration-mail/src/test/java/org/springframework/integration/mail/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.mail.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java index 630fac9e4e..ff22b8af97 100644 --- a/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.java +++ b/spring-integration-rmi/src/test/java/org/springframework/integration/rmi/config/DefaultConfigurationTests.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. @@ -18,23 +18,27 @@ package org.springframework.integration.rmi.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-security/src/test/java/org/springframework/integration/security/config/DefaultConfigurationTests.java b/spring-integration-security/src/test/java/org/springframework/integration/security/config/DefaultConfigurationTests.java index c19835ad0c..91dfc02e23 100644 --- a/spring-integration-security/src/test/java/org/springframework/integration/security/config/DefaultConfigurationTests.java +++ b/spring-integration-security/src/test/java/org/springframework/integration/security/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.security.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/DefaultConfigurationTests.java b/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/DefaultConfigurationTests.java index ade9a8461b..47c54b3bbb 100644 --- a/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/DefaultConfigurationTests.java +++ b/spring-integration-stream/src/test/java/org/springframework/integration/stream/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.stream.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/DefaultConfigurationTests.java b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/DefaultConfigurationTests.java index 94392f8d07..4b88b565bd 100644 --- a/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/DefaultConfigurationTests.java +++ b/spring-integration-ws/src/test/java/org/springframework/integration/ws/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.ws.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/DefaultConfigurationTests.java b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/DefaultConfigurationTests.java index 5835e4e241..1f9434e5f6 100644 --- a/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/DefaultConfigurationTests.java +++ b/spring-integration-xml/src/test/java/org/springframework/integration/xml/config/DefaultConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2009 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. @@ -18,23 +18,27 @@ package org.springframework.integration.xml.config; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import org.junit.Test; import org.junit.runner.RunWith; -import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.integration.channel.MessagePublishingErrorHandler; import org.springframework.integration.channel.NullChannel; import org.springframework.integration.channel.PublishSubscribeChannel; import org.springframework.integration.context.IntegrationContextUtils; +import org.springframework.integration.test.util.TestUtils; +import org.springframework.messaging.MessageChannel; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.util.ErrorHandler; /** * @author Mark Fisher + * @author Artem Bilan * @since 1.0.3 */ @RunWith(SpringJUnit4ClassRunner.class) @@ -63,9 +67,13 @@ public class DefaultConfigurationTests { public void verifyTaskScheduler() { Object taskScheduler = context.getBean(IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME); assertEquals(ThreadPoolTaskScheduler.class, taskScheduler.getClass()); - Object errorHandler = new DirectFieldAccessor(taskScheduler).getPropertyValue("errorHandler"); + ErrorHandler errorHandler = TestUtils.getPropertyValue(taskScheduler, "errorHandler", ErrorHandler.class); assertEquals(MessagePublishingErrorHandler.class, errorHandler.getClass()); - Object defaultErrorChannel = new DirectFieldAccessor(errorHandler).getPropertyValue("defaultErrorChannel"); + MessageChannel defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNull(defaultErrorChannel); + errorHandler.handleError(new Throwable()); + defaultErrorChannel = TestUtils.getPropertyValue(errorHandler, "defaultErrorChannel", MessageChannel.class); + assertNotNull(defaultErrorChannel); assertEquals(context.getBean(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME), defaultErrorChannel); } diff --git a/src/reference/docbook/jmx.xml b/src/reference/docbook/jmx.xml index d39e8742d8..d9413b2f48 100644 --- a/src/reference/docbook/jmx.xml +++ b/src/reference/docbook/jmx.xml @@ -140,12 +140,12 @@
Tree Polling Channel Adapter - The Tree Polling Channel Adapter queries the JMX MBean tree and - sends a message with a payload that is the graph of objects that matches the query. By - default the MBeans are mapped to primitives and simple Objects like Map, List and arrays - - permitting simple transformation, for example, to JSON. An MBeanServer reference is also - required, but it will automatically check for a bean named mbeanServer - by default, just like the Notification-listening Channel Adapter + The Tree Polling Channel Adapter queries the JMX MBean tree and + sends a message with a payload that is the graph of objects that matches the query. By + default the MBeans are mapped to primitives and simple Objects like Map, List and arrays - + permitting simple transformation, for example, to JSON. An MBeanServer reference is also + required, but it will automatically check for a bean named mbeanServer + by default, just like the Notification-listening Channel Adapter described above. A basic configuration would be: . + + Starting with Spring Integration 4.0 the @EnableIntegrationMBeanExport + annotation has been introduced for convenient configuration of a default + (integrationMbeanExporter) bean of type + IntegrationMBeanExporter with several useful options + at the @Configuration class level. For example: + + If there is a need to provide more options, or have several IntegrationMBeanExporter beans + e.g. for different MBean Servers, or to avoid conflicts with the standard Spring MBeanExporter + (e.g. via @EnableMBeanExport), you can simply configure an IntegrationMBeanExporter + as a generic bean. +
MBean ObjectNames diff --git a/src/reference/docbook/whats-new.xml b/src/reference/docbook/whats-new.xml index 268453841a..b80a8dd1d7 100644 --- a/src/reference/docbook/whats-new.xml +++ b/src/reference/docbook/whats-new.xml @@ -94,6 +94,13 @@ For more information, see .
+
+ @EnableIntegrationMBeanExport + + The IntegrationMBeanExporter can now be enabled with the @EnableIntegrationMBeanExport + annotation in a @Configuration class. For more information, see . + +