From 09200361defde95ec48bd9169ab5db7590c9440b Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 6 Jun 2014 16:10:56 +0100 Subject: [PATCH] Align expectations of various JMX configurations They all want to create an MBeanServer and when that happens user sees no MBeans, or sometimes just one set (Spring Core, Spring Integration or Spring Boot). To harmonise them we create a @Bean of type MBeanServer and link to it in the other autoconfigs Fixes gh-1046 --- .../EndpointMBeanExportAutoConfiguration.java | 8 +++++-- ...ointMBeanExportAutoConfigurationTests.java | 15 ++++++++----- spring-boot-autoconfigure/pom.xml | 5 +++++ .../IntegrationAutoConfiguration.java | 16 ++++++++++++++ .../jmx/JmxAutoConfiguration.java | 15 +++++++++++-- .../IntegrationAutoConfigurationTests.java | 4 +++- .../main/asciidoc/spring-boot-features.adoc | 22 +++++++++++++++++++ .../spring-boot-sample-integration/pom.xml | 4 ++++ .../src/main/resources/application.properties | 3 ++- 9 files changed, 81 insertions(+), 11 deletions(-) diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfiguration.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfiguration.java index 4f5d81c9af..a47728f777 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfiguration.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfiguration.java @@ -16,12 +16,15 @@ package org.springframework.boot.actuate.autoconfigure; +import javax.management.MBeanServer; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -35,7 +38,7 @@ import org.springframework.util.StringUtils; */ @Configuration @ConditionalOnExpression("${endpoints.jmx.enabled:true} && ${spring.jmx.enabled:true}") -@AutoConfigureAfter({ EndpointAutoConfiguration.class }) +@AutoConfigureAfter({ EndpointAutoConfiguration.class, JmxAutoConfiguration.class }) @EnableConfigurationProperties(EndpointMBeanExportProperties.class) public class EndpointMBeanExportAutoConfiguration { @@ -43,13 +46,14 @@ public class EndpointMBeanExportAutoConfiguration { EndpointMBeanExportProperties properties = new EndpointMBeanExportProperties(); @Bean - public EndpointMBeanExporter endpointMBeanExporter() { + public EndpointMBeanExporter endpointMBeanExporter(MBeanServer server) { EndpointMBeanExporter mbeanExporter = new EndpointMBeanExporter(); String domain = this.properties.getDomain(); if (StringUtils.hasText(domain)) { mbeanExporter.setDomain(domain); } + mbeanExporter.setServer(server); mbeanExporter.setEnsureUniqueRuntimeObjectNames(this.properties.isUniqueNames()); mbeanExporter.setObjectNameStaticProperties(this.properties.getStaticNames()); diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfigurationTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfigurationTests.java index 2d6bb00f0f..29f1ed6fa9 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfigurationTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/autoconfigure/EndpointMBeanExportAutoConfigurationTests.java @@ -26,6 +26,7 @@ import org.junit.After; import org.junit.Test; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; @@ -56,7 +57,8 @@ public class EndpointMBeanExportAutoConfigurationTests { @Test public void testEndpointMBeanExporterIsInstalled() { this.context = new AnnotationConfigApplicationContext(); - this.context.register(TestConfiguration.class, EndpointAutoConfiguration.class, + this.context.register(TestConfiguration.class, JmxAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class); this.context.refresh(); assertNotNull(this.context.getBean(EndpointMBeanExporter.class)); @@ -68,7 +70,8 @@ public class EndpointMBeanExportAutoConfigurationTests { environment.setProperty("endpoints.jmx.enabled", "false"); this.context = new AnnotationConfigApplicationContext(); this.context.setEnvironment(environment); - this.context.register(EndpointAutoConfiguration.class, + this.context.register(JmxAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class); this.context.refresh(); this.context.getBean(EndpointMBeanExporter.class); @@ -84,7 +87,8 @@ public class EndpointMBeanExportAutoConfigurationTests { environment.setProperty("endpoints.jmx.static_names", "key1=value1, key2=value2"); this.context = new AnnotationConfigApplicationContext(); this.context.setEnvironment(environment); - this.context.register(EndpointAutoConfiguration.class, + this.context.register(JmxAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class); this.context.refresh(); this.context.getBean(EndpointMBeanExporter.class); @@ -101,11 +105,12 @@ public class EndpointMBeanExportAutoConfigurationTests { public void testEndpointMBeanExporterInParentChild() throws IntrospectionException, InstanceNotFoundException, MalformedObjectNameException, ReflectionException { this.context = new AnnotationConfigApplicationContext(); - this.context.register(EndpointAutoConfiguration.class, + this.context.register(JmxAutoConfiguration.class, + EndpointAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class); AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); - parent.register(EndpointAutoConfiguration.class, + parent.register(JmxAutoConfiguration.class, EndpointAutoConfiguration.class, EndpointMBeanExportAutoConfiguration.class); this.context.setParent(parent); diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml index 16d865e228..fa51c3640a 100644 --- a/spring-boot-autoconfigure/pom.xml +++ b/spring-boot-autoconfigure/pom.xml @@ -136,6 +136,11 @@ spring-integration-core true + + org.springframework.integration + spring-integration-jmx + true + org.springframework spring-jms diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java index f4ed848585..154f313dbd 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfiguration.java @@ -16,19 +16,27 @@ package org.springframework.boot.autoconfigure.integration; +import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.integration.config.EnableIntegration; +import org.springframework.integration.jmx.config.EnableIntegrationMBeanExport; +import org.springframework.integration.jmx.config.IntegrationMBeanExportConfiguration; /** * {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration * Auto-configuration} for Spring Integration. * * @author Artem Bilan + * @author Dave Syer * @since 1.1 */ @Configuration @ConditionalOnClass(EnableIntegration.class) +@AutoConfigureAfter(JmxAutoConfiguration.class) public class IntegrationAutoConfiguration { @Configuration @@ -36,4 +44,12 @@ public class IntegrationAutoConfiguration { protected static class IntegrationConfiguration { } + @Configuration + @ConditionalOnClass(EnableIntegrationMBeanExport.class) + @ConditionalOnMissingBean(IntegrationMBeanExportConfiguration.class) + @ConditionalOnExpression("${spring.jmx.enabled:true}") + @EnableIntegrationMBeanExport(defaultDomain = "${spring.jmx.default_domain:}", server = "${spring.jmx.server:mbeanServer}") + protected static class IntegrationJmxConfiguration { + } + } diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java index a3d01ed95e..1b53b3baaa 100644 --- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java +++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java @@ -16,13 +16,17 @@ package org.springframework.boot.autoconfigure.jmx; +import javax.management.MBeanServer; + import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableMBeanExport; import org.springframework.jmx.export.MBeanExporter; +import org.springframework.jmx.support.MBeanServerFactoryBean; /** * {@link EnableAutoConfiguration Auto-configuration} to enable/disable Spring's @@ -34,14 +38,21 @@ import org.springframework.jmx.export.MBeanExporter; */ @Configuration @ConditionalOnClass({ MBeanExporter.class }) -@ConditionalOnMissingBean({ MBeanExporter.class }) @ConditionalOnExpression("${spring.jmx.enabled:true}") public class JmxAutoConfiguration { @Configuration - @EnableMBeanExport(defaultDomain = "${spring.jmx.default_domain:}", server = "${spring.jmx.server:}") + @ConditionalOnMissingBean({ MBeanExporter.class }) + @EnableMBeanExport(defaultDomain = "${spring.jmx.default_domain:}", server = "${spring.jmx.server:mbeanServer}") public static class MBeanExport { + } + @Bean + @ConditionalOnMissingBean(MBeanServer.class) + public MBeanServerFactoryBean mbeanServer() { + MBeanServerFactoryBean factory = new MBeanServerFactoryBean(); + factory.setLocateExistingServerIfPossible(true); + return factory; } } diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java index ff3dc4d87e..d3774b7344 100644 --- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java +++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java @@ -17,6 +17,7 @@ package org.springframework.boot.autoconfigure.integration; import org.junit.Test; +import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.integration.support.channel.HeaderChannelRegistry; @@ -32,7 +33,8 @@ public class IntegrationAutoConfigurationTests { @Test public void integrationIsAvailable() { - this.context.register(IntegrationAutoConfiguration.class); + this.context.register(JmxAutoConfiguration.class, + IntegrationAutoConfiguration.class); this.context.refresh(); assertNotNull(this.context.getBean(HeaderChannelRegistry.class)); this.context.close(); diff --git a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 3ebc52de28..a57771246b 100644 --- a/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -1688,6 +1688,17 @@ infrastructure to receive messages asynchronously. Spring AMQP provides a simila feature set for the ``Advanced Message Queuing Protocol'' and Boot also provides auto-configuration options for `RabbitTemplate` and RabbitMQ. +[[boot-features-messaging]] +== Spring Integration + +Spring Integration provides abstractions over messaging and also other +transports such as HTTP, TCP etc. If Spring Integration is available +on your classpath it will be initialized through the `@EnableIntegration` +annotation. Message processing statistics will be published over JMX if +``spring-integration-jmx'' is also on the classpath. +See the {sc-spring-boot-autoconfigure}/integration/IntegrationAutoConfiguration.{sc-ext}[`IntegrationAutoConfiguration`] +class for more details. + [[boot-features-jms]] == JMS @@ -1792,6 +1803,17 @@ into your own beans: } ---- +[[boot-features-jmx]] +== Monitoring and management over JMX + +Java Management Extensions (JMX) provide a standard mechanism to +monitor and manage applications. By default Spring Boot will create an +`MBeanServer` with bean id "mbeanServer" and expose any of your beans +that are annotated with Spring JMX annotations (`@ManagedResource`, +`@ManagedAttribute`, `@ManagedOperation`). + +See the {sc-spring-boot-autoconfigure}/jmx/JmxAutoConfiguration.{sc-ext}[`JmxAutoConfiguration`] +class for more details. [[boot-features-testing]] == Testing diff --git a/spring-boot-samples/spring-boot-sample-integration/pom.xml b/spring-boot-samples/spring-boot-sample-integration/pom.xml index e4ead4891b..454e9571ed 100644 --- a/spring-boot-samples/spring-boot-sample-integration/pom.xml +++ b/spring-boot-samples/spring-boot-sample-integration/pom.xml @@ -23,6 +23,10 @@ org.springframework.boot spring-boot-starter-integration + + org.springframework.integration + spring-integration-jmx + org.springframework.boot spring-boot-starter-test diff --git a/spring-boot-samples/spring-boot-sample-integration/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-integration/src/main/resources/application.properties index 2ec8b7cad2..1fd64e4d13 100644 --- a/spring-boot-samples/spring-boot-sample-integration/src/main/resources/application.properties +++ b/spring-boot-samples/spring-boot-sample-integration/src/main/resources/application.properties @@ -1,2 +1,3 @@ logging.file: /tmp/logs/app.log -service.greeting: Hello \ No newline at end of file +service.greeting: Hello +debug: true \ No newline at end of file