Skip to content

Commit 36130b2

Browse files
author
Dave Syer
committed
Support for Jmx (and Integration) autoconfig in parent contexts
When there are parent contexts we already had a strategy for registering the actuator endpoints, but not the regular JMX or Integration MBeans. This chnage makes the autoconfigs for JMX aware of the parent context. Also adds a sample with a parent context. See gh-847
1 parent 5adbf32 commit 36130b2

File tree

17 files changed

+537
-4
lines changed

17 files changed

+537
-4
lines changed

spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfiguration.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import javax.management.MBeanServer;
2020

21+
import org.springframework.beans.factory.BeanFactory;
22+
import org.springframework.beans.factory.annotation.Autowired;
2123
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
2224
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
2325
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
@@ -26,7 +28,13 @@
2628
import org.springframework.context.annotation.Bean;
2729
import org.springframework.context.annotation.Configuration;
2830
import org.springframework.context.annotation.EnableMBeanExport;
31+
import org.springframework.context.annotation.MBeanExportConfiguration;
32+
import org.springframework.core.env.Environment;
33+
import org.springframework.core.type.StandardAnnotationMetadata;
2934
import org.springframework.jmx.export.MBeanExporter;
35+
import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource;
36+
import org.springframework.jmx.export.annotation.AnnotationMBeanExporter;
37+
import org.springframework.jmx.export.naming.ObjectNamingStrategy;
3038
import org.springframework.jmx.support.MBeanServerFactoryBean;
3139

3240
/**
@@ -42,10 +50,33 @@
4250
@ConditionalOnExpression("${spring.jmx.enabled:true}")
4351
public class JmxAutoConfiguration {
4452

45-
@Configuration
53+
@Autowired
54+
private Environment environment;
55+
56+
@Autowired
57+
private BeanFactory beanFactory;
58+
59+
@Autowired
60+
private ObjectNamingStrategy namingStrategy;
61+
62+
@Bean
4663
@ConditionalOnMissingBean(value = MBeanExporter.class, search = SearchStrategy.CURRENT)
47-
@EnableMBeanExport(defaultDomain = "${spring.jmx.default_domain:}", server = "${spring.jmx.server:mbeanServer}")
48-
public static class MBeanExport {
64+
public AnnotationMBeanExporter mbeanExporter() {
65+
// Re-use the @EnableMBeanExport configuration
66+
MBeanExportConfiguration config = new MBeanExportConfiguration();
67+
config.setEnvironment(this.environment);
68+
config.setBeanFactory(this.beanFactory);
69+
config.setImportMetadata(new StandardAnnotationMetadata(Empty.class));
70+
// But add a custom naming strategy
71+
AnnotationMBeanExporter exporter = config.mbeanExporter();
72+
exporter.setNamingStrategy(this.namingStrategy);
73+
return exporter;
74+
}
75+
76+
@Bean
77+
@ConditionalOnMissingBean(ObjectNamingStrategy.class)
78+
public ParentAwareNamingStrategy objectNamingStrategy() {
79+
return new ParentAwareNamingStrategy(new AnnotationJmxAttributeSource());
4980
}
5081

5182
@Bean
@@ -56,4 +87,9 @@ public MBeanServerFactoryBean mbeanServer() {
5687
return factory;
5788
}
5889

90+
@EnableMBeanExport(defaultDomain = "${spring.jmx.default_domain:}", server = "${spring.jmx.server:mbeanServer}")
91+
private static class Empty {
92+
93+
}
94+
5995
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2012-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.jmx;
18+
19+
import javax.management.MalformedObjectNameException;
20+
import javax.management.ObjectName;
21+
22+
import org.springframework.beans.BeansException;
23+
import org.springframework.context.ApplicationContext;
24+
import org.springframework.context.ApplicationContextAware;
25+
import org.springframework.jmx.export.metadata.JmxAttributeSource;
26+
import org.springframework.jmx.export.naming.MetadataNamingStrategy;
27+
import org.springframework.util.ObjectUtils;
28+
29+
/**
30+
* @author Dave Syer
31+
* @since 1.1.1
32+
*/
33+
public class ParentAwareNamingStrategy extends MetadataNamingStrategy implements
34+
ApplicationContextAware {
35+
36+
private ApplicationContext applicationContext;
37+
private boolean ensureUniqueRuntimeObjectNames = false;
38+
39+
public ParentAwareNamingStrategy(JmxAttributeSource attributeSource) {
40+
super(attributeSource);
41+
}
42+
43+
/**
44+
* @param ensureUniqueRuntimeObjectNames the ensureUniqueRuntimeObjectNames to set
45+
*/
46+
public void setEnsureUniqueRuntimeObjectNames(boolean ensureUniqueRuntimeObjectNames) {
47+
this.ensureUniqueRuntimeObjectNames = ensureUniqueRuntimeObjectNames;
48+
}
49+
50+
@Override
51+
public ObjectName getObjectName(Object managedBean, String beanKey)
52+
throws MalformedObjectNameException {
53+
StringBuilder builder = new StringBuilder(beanKey);
54+
if (parentContextContainsSameBean(this.applicationContext, beanKey)) {
55+
builder.append(",context="
56+
+ ObjectUtils.getIdentityHexString(this.applicationContext));
57+
}
58+
if (this.ensureUniqueRuntimeObjectNames) {
59+
builder.append(",identity=" + ObjectUtils.getIdentityHexString(managedBean));
60+
}
61+
ObjectName name = super.getObjectName(managedBean, beanKey);
62+
return name;
63+
}
64+
65+
@Override
66+
public void setApplicationContext(ApplicationContext applicationContext)
67+
throws BeansException {
68+
this.applicationContext = applicationContext;
69+
}
70+
71+
private boolean parentContextContainsSameBean(ApplicationContext applicationContext,
72+
String beanKey) {
73+
if (applicationContext.getParent() != null) {
74+
try {
75+
this.applicationContext.getParent().getBean(beanKey);
76+
return true;
77+
}
78+
catch (BeansException ex) {
79+
return parentContextContainsSameBean(applicationContext.getParent(),
80+
beanKey);
81+
}
82+
}
83+
return false;
84+
}
85+
86+
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/integration/IntegrationAutoConfigurationTests.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.junit.Test;
2020
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
21+
import org.springframework.context.ConfigurableApplicationContext;
2122
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2223
import org.springframework.integration.support.channel.HeaderChannelRegistry;
2324

@@ -29,15 +30,37 @@
2930
*/
3031
public class IntegrationAutoConfigurationTests {
3132

32-
private final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
33+
private AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
3334

3435
@Test
3536
public void integrationIsAvailable() {
37+
this.context.register(IntegrationAutoConfiguration.class);
38+
this.context.refresh();
39+
assertNotNull(this.context.getBean(HeaderChannelRegistry.class));
40+
this.context.close();
41+
}
42+
43+
@Test
44+
public void addJmxAuto() {
3645
this.context.register(JmxAutoConfiguration.class,
3746
IntegrationAutoConfiguration.class);
3847
this.context.refresh();
3948
assertNotNull(this.context.getBean(HeaderChannelRegistry.class));
4049
this.context.close();
4150
}
4251

52+
@Test
53+
public void parentContext() {
54+
this.context.register(IntegrationAutoConfiguration.class);
55+
this.context.refresh();
56+
AnnotationConfigApplicationContext parent = this.context;
57+
this.context = new AnnotationConfigApplicationContext();
58+
this.context.setParent(parent);
59+
this.context.register(IntegrationAutoConfiguration.class);
60+
this.context.refresh();
61+
assertNotNull(this.context.getBean(HeaderChannelRegistry.class));
62+
((ConfigurableApplicationContext) this.context.getParent()).close();
63+
this.context.close();
64+
}
65+
4366
}

spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/jmx/JmxAutoConfigurationTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.junit.Test;
2222
import org.junit.rules.ExpectedException;
2323
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
24+
import org.springframework.context.ConfigurableApplicationContext;
2425
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2526
import org.springframework.context.annotation.Bean;
2627
import org.springframework.context.annotation.Configuration;
@@ -51,6 +52,9 @@ public class JmxAutoConfigurationTests {
5152
public void tearDown() {
5253
if (this.context != null) {
5354
this.context.close();
55+
if (this.context.getParent() != null) {
56+
((ConfigurableApplicationContext) this.context.getParent()).close();
57+
}
5458
}
5559
}
5660

@@ -104,6 +108,18 @@ public void testDefaultDomainConfiguredOnMBeanExport() {
104108
ReflectionTestUtils.getField(naming, "defaultDomain"));
105109
}
106110

111+
@Test
112+
public void testParentContext() {
113+
this.context = new AnnotationConfigApplicationContext();
114+
this.context.register(JmxAutoConfiguration.class);
115+
this.context.refresh();
116+
AnnotationConfigApplicationContext parent = this.context;
117+
this.context = new AnnotationConfigApplicationContext();
118+
this.context.setParent(parent);
119+
this.context.register(JmxAutoConfiguration.class);
120+
this.context.refresh();
121+
}
122+
107123
@Configuration
108124
public static class TestConfiguration {
109125

spring-boot-samples/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
<module>spring-boot-sample-integration</module>
4040
<module>spring-boot-sample-jetty</module>
4141
<module>spring-boot-sample-liquibase</module>
42+
<module>spring-boot-sample-parent-context</module>
4243
<module>spring-boot-sample-profile</module>
4344
<module>spring-boot-sample-secure</module>
4445
<module>spring-boot-sample-servlet</module>

spring-boot-samples/spring-boot-sample-integration/pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
<groupId>org.springframework.boot</groupId>
2424
<artifactId>spring-boot-starter-integration</artifactId>
2525
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-actuator</artifactId>
29+
</dependency>
2630
<dependency>
2731
<groupId>org.springframework.integration</groupId>
2832
<artifactId>spring-integration-jmx</artifactId>

spring-boot-samples/spring-boot-sample-integration/src/main/java/sample/integration/ServiceProperties.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@
1717
package sample.integration;
1818

1919
import org.springframework.boot.context.properties.ConfigurationProperties;
20+
import org.springframework.jmx.export.annotation.ManagedAttribute;
21+
import org.springframework.jmx.export.annotation.ManagedResource;
2022

2123
@ConfigurationProperties(prefix = "service", ignoreUnknownFields = false)
24+
@ManagedResource
2225
public class ServiceProperties {
2326

2427
private String greeting = "Hello";
2528

29+
@ManagedAttribute
2630
public String getGreeting() {
2731
return this.greeting;
2832
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<parent>
5+
<!-- Your own application should inherit from spring-boot-starter-parent -->
6+
<groupId>org.springframework.boot</groupId>
7+
<artifactId>spring-boot-samples</artifactId>
8+
<version>1.1.1.BUILD-SNAPSHOT</version>
9+
</parent>
10+
<artifactId>spring-boot-sample-parent-context</artifactId>
11+
<name>spring-boot-sample-parent-context</name>
12+
<description>Spring Boot Integration Sample</description>
13+
<url>http://projects.spring.io/spring-boot/</url>
14+
<organization>
15+
<name>Pivotal Software, Inc.</name>
16+
<url>http://www.spring.io</url>
17+
</organization>
18+
<properties>
19+
<main.basedir>${basedir}/../..</main.basedir>
20+
</properties>
21+
<dependencies>
22+
<dependency>
23+
<groupId>org.springframework.boot</groupId>
24+
<artifactId>spring-boot-starter-integration</artifactId>
25+
</dependency>
26+
<dependency>
27+
<groupId>org.springframework.boot</groupId>
28+
<artifactId>spring-boot-starter-actuator</artifactId>
29+
</dependency>
30+
<dependency>
31+
<groupId>org.springframework.integration</groupId>
32+
<artifactId>spring-integration-jmx</artifactId>
33+
</dependency>
34+
<dependency>
35+
<groupId>org.springframework.boot</groupId>
36+
<artifactId>spring-boot-starter-test</artifactId>
37+
<scope>test</scope>
38+
</dependency>
39+
</dependencies>
40+
<build>
41+
<plugins>
42+
<plugin>
43+
<groupId>org.springframework.boot</groupId>
44+
<artifactId>spring-boot-maven-plugin</artifactId>
45+
</plugin>
46+
</plugins>
47+
</build>
48+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright 2012-2013 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package sample.parent;
18+
19+
import org.springframework.beans.factory.annotation.Autowired;
20+
import org.springframework.stereotype.Component;
21+
22+
@Component
23+
public class HelloWorldService {
24+
25+
@Autowired
26+
private ServiceProperties configuration;
27+
28+
public String getHelloMessage(String name) {
29+
return this.configuration.getGreeting() + " " + name;
30+
}
31+
32+
}

0 commit comments

Comments
 (0)