diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporter.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporter.java index 172413b5b07..d4cb03ccd8c 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporter.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporter.java @@ -28,11 +28,14 @@ import javax.management.ObjectName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.boot.actuate.endpoint.ShutdownEndpoint; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; import org.springframework.context.ApplicationListener; import org.springframework.context.SmartLifecycle; import org.springframework.jmx.export.MBeanExportException; @@ -52,7 +55,7 @@ import org.springframework.util.ObjectUtils; * @author Christian Dupuis */ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecycle, - BeanFactoryAware { + BeanFactoryAware, ApplicationContextAware { public static final String DEFAULT_DOMAIN = "org.springframework.boot"; @@ -76,6 +79,8 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc private final ReentrantLock lifecycleLock = new ReentrantLock(); + private ApplicationContext applicationContext; + private ListableBeanFactory beanFactory; private String domain = DEFAULT_DOMAIN; @@ -91,6 +96,12 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc setAssembler(this.assembler); } + @Override + public void setApplicationContext(ApplicationContext applicationContext) + throws BeansException { + this.applicationContext = applicationContext; + } + @Override public void setBeanFactory(BeanFactory beanFactory) { super.setBeanFactory(beanFactory); @@ -159,6 +170,10 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc StringBuilder builder = new StringBuilder(); builder.append(this.domain); builder.append(":type=Endpoint"); + if (parentContextContainsSameBean(this.applicationContext, beanKey)) { + builder.append(",context=" + + ObjectUtils.getIdentityHexString(this.applicationContext)); + } builder.append(",name=" + beanKey); if (this.ensureUniqueRuntimeObjectNames) { builder.append(",identity=" @@ -172,6 +187,21 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc return this.defaultNamingStrategy.getObjectName(bean, beanKey); } + private boolean parentContextContainsSameBean(ApplicationContext applicationContext, + String beanKey) { + if (applicationContext.getParent() != null) { + try { + this.applicationContext.getParent().getBean(beanKey, Endpoint.class); + return true; + } + catch (BeansException ex) { + return parentContextContainsSameBean(applicationContext.getParent(), + beanKey); + } + } + return false; + } + private String getStaticNames() { if (this.objectNameStaticProperties.isEmpty()) { return ""; @@ -243,4 +273,5 @@ public class EndpointMBeanExporter extends MBeanExporter implements SmartLifecyc this.lifecycleLock.unlock(); } } + } 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 c8c65240b37..2d6bb00f0fa 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 @@ -97,11 +97,45 @@ public class EndpointMBeanExportAutoConfigurationTests { + ",key1=value1,key2=value2"))); } + @Test + public void testEndpointMBeanExporterInParentChild() throws IntrospectionException, + InstanceNotFoundException, MalformedObjectNameException, ReflectionException { + this.context = new AnnotationConfigApplicationContext(); + this.context.register(EndpointAutoConfiguration.class, + EndpointMBeanExportAutoConfiguration.class); + + AnnotationConfigApplicationContext parent = new AnnotationConfigApplicationContext(); + parent.register(EndpointAutoConfiguration.class, + EndpointMBeanExportAutoConfiguration.class); + this.context.setParent(parent); + + parent.refresh(); + this.context.refresh(); + + parent.close(); + + System.out.println("parent " + ObjectUtils.getIdentityHexString(parent)); + System.out.println("child " + ObjectUtils.getIdentityHexString(this.context)); + } + private ObjectName getObjectName(String domain, String beanKey, ApplicationContext applicationContext) throws MalformedObjectNameException { - return ObjectNameManager.getInstance(String.format( - "%s:type=Endpoint,name=%s,identity=%s", domain, beanKey, - ObjectUtils.getIdentityHexString(applicationContext.getBean(beanKey)))); + if (applicationContext.getParent() != null) { + return ObjectNameManager + .getInstance(String.format( + "%s:type=Endpoint,name=%s,context=%s,identity=%s", domain, + beanKey, + ObjectUtils.getIdentityHexString(applicationContext), + ObjectUtils.getIdentityHexString(applicationContext + .getBean(beanKey)))); + } + else { + return ObjectNameManager + .getInstance(String.format("%s:type=Endpoint,name=%s,identity=%s", + domain, beanKey, ObjectUtils + .getIdentityHexString(applicationContext + .getBean(beanKey)))); + } } @Configuration