From 31f7807acfc017aa54aa1b684686e8fd66066b0d Mon Sep 17 00:00:00 2001 From: Christian Dupuis Date: Thu, 19 Dec 2013 15:20:51 +0100 Subject: [PATCH] Change naming strategy for endpoint mbeans --- .../EndpointMBeanExportAutoConfiguration.java | 18 +---- .../actuate/endpoint/jmx/EndpointMBean.java | 29 ++++++- .../endpoint/jmx/EndpointMBeanExporter.java | 37 +-------- .../jmx/EndpointMBeanExporterTests.java | 77 ++++++------------- 4 files changed, 51 insertions(+), 110 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 9cd0f8cb619..4197ed153a0 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,19 +16,15 @@ package org.springframework.boot.actuate.autoconfigure; -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.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; -import org.springframework.boot.bind.RelaxedPropertyResolver; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.core.env.Environment; import org.springframework.jmx.export.MBeanExporter; -import org.springframework.util.StringUtils; /** * {@link EnableAutoConfiguration Auto-configuration} to enable JMX export for @@ -42,20 +38,8 @@ import org.springframework.util.StringUtils; @ConditionalOnExpression("${endpoints.jmx.enabled:true}") class EndpointMBeanExportAutoConfiguration { - private RelaxedPropertyResolver environment; - - @Autowired - public void setEnvironment(Environment environment) { - this.environment = new RelaxedPropertyResolver(environment); - } - @Bean public EndpointMBeanExporter endpointMBeanExporter() { - EndpointMBeanExporter mbeanExporter = new EndpointMBeanExporter(); - String domainName = this.environment.getProperty("endpoints.jmx.domain_name"); - if (StringUtils.hasText(domainName)) { - mbeanExporter.setDomainName(domainName); - } - return mbeanExporter; + return new EndpointMBeanExporter(); } } \ No newline at end of file diff --git a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java index b355ac2c19a..d53662155ce 100644 --- a/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java +++ b/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBean.java @@ -19,10 +19,16 @@ package org.springframework.boot.actuate.endpoint.jmx; import java.util.List; import java.util.Map; +import javax.management.MalformedObjectNameException; +import javax.management.ObjectName; + import org.springframework.boot.actuate.endpoint.Endpoint; +import org.springframework.jmx.export.annotation.AnnotationJmxAttributeSource; import org.springframework.jmx.export.annotation.ManagedAttribute; -import org.springframework.jmx.export.annotation.ManagedOperation; import org.springframework.jmx.export.annotation.ManagedResource; +import org.springframework.jmx.export.naming.MetadataNamingStrategy; +import org.springframework.jmx.export.naming.SelfNaming; +import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import com.fasterxml.jackson.databind.ObjectMapper; @@ -33,14 +39,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; * @author Christian Dupuis */ @ManagedResource -public class EndpointMBean { +public class EndpointMBean implements SelfNaming { + + private AnnotationJmxAttributeSource annotationSource = new AnnotationJmxAttributeSource(); + + private MetadataNamingStrategy metadataNamingStrategy = new MetadataNamingStrategy( + this.annotationSource); private Endpoint endpoint; + private String beanName; + private ObjectMapper mapper = new ObjectMapper(); - public EndpointMBean(Endpoint endpoint) { + public EndpointMBean(String beanName, Endpoint endpoint) { + Assert.notNull(beanName, "BeanName must not be null"); + Assert.notNull(endpoint, "Endpoint must not be null"); this.endpoint = endpoint; + this.beanName = beanName; } @ManagedAttribute(description = "Returns the class of the underlying endpoint") @@ -53,7 +69,7 @@ public class EndpointMBean { return this.endpoint.isSensitive(); } - @ManagedOperation(description = "Invoke the underlying endpoint") + @ManagedAttribute(description = "Invoke the underlying endpoint") public Object invoke() { Object result = this.endpoint.invoke(); if (result == null) { @@ -70,4 +86,9 @@ public class EndpointMBean { return this.mapper.convertValue(result, Map.class); } + + @Override + public ObjectName getObjectName() throws MalformedObjectNameException { + return this.metadataNamingStrategy.getObjectName(this, this.beanName); + } } 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 f049e02421d..05c9dd9b531 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 @@ -17,14 +17,11 @@ package org.springframework.boot.actuate.endpoint.jmx; import java.util.HashSet; -import java.util.Hashtable; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReentrantLock; import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -37,10 +34,7 @@ import org.springframework.context.ApplicationListener; import org.springframework.context.SmartLifecycle; import org.springframework.jmx.export.MBeanExportException; import org.springframework.jmx.export.MBeanExporter; -import org.springframework.jmx.support.JmxUtils; -import org.springframework.jmx.support.ObjectNameManager; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; /** * {@link ApplicationListener} that registers all known {@link Endpoint}s with an @@ -51,13 +45,8 @@ import org.springframework.util.ClassUtils; */ public class EndpointMBeanExporter implements SmartLifecycle, ApplicationContextAware { - private static final String DEFAULT_DOMAIN_NAME = ClassUtils - .getPackageName(Endpoint.class); - private static Log logger = LogFactory.getLog(EndpointMBeanExporter.class); - private String domainName = DEFAULT_DOMAIN_NAME; - private Set> registeredEndpoints = new HashSet>(); private volatile boolean autoStartup = true; @@ -76,11 +65,6 @@ public class EndpointMBeanExporter implements SmartLifecycle, ApplicationContext this.applicationContext = applicationContext; } - public void setDomainName(String domainName) { - Assert.notNull(domainName, "DomainName must not be null"); - this.domainName = domainName; - } - protected void doStart() { try { MBeanExporter mbeanExporter = this.applicationContext @@ -108,29 +92,14 @@ public class EndpointMBeanExporter implements SmartLifecycle, ApplicationContext } } - protected void registerEndpoint(String beanKey, Endpoint endpoint, + protected void registerEndpoint(String beanName, Endpoint endpoint, MBeanExporter mbeanExporter) { try { - mbeanExporter.registerManagedResource(new EndpointMBean(endpoint), - getObjectName(beanKey, endpoint)); + mbeanExporter.registerManagedResource(new EndpointMBean(beanName, endpoint)); } catch (MBeanExportException ex) { - logger.error("Could not register MBean for endpoint [" + beanKey + "]", ex); + logger.error("Could not register MBean for endpoint [" + beanName + "]", ex); } - catch (MalformedObjectNameException ex) { - logger.error("Could not register MBean for endpoint [" + beanKey + "]", ex); - } - } - - protected ObjectName getObjectName(String beanKey, Endpoint endpoint) - throws MalformedObjectNameException { - // We have to be super careful to not create name clashes as multiple Boot - // applications can potentially run in the same VM or MBeanServer. Therefore - // append the object identity to the ObjectName. - Hashtable properties = new Hashtable(); - properties.put("bean", beanKey); - return JmxUtils.appendIdentityToObjectName( - ObjectNameManager.getInstance(this.domainName, properties), endpoint); } // SmartLifeCycle implementation diff --git a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java index 1955345b6fb..17a183b4dcd 100644 --- a/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java +++ b/spring-boot-actuator/src/test/java/org/springframework/boot/actuate/endpoint/jmx/EndpointMBeanExporterTests.java @@ -16,9 +16,7 @@ package org.springframework.boot.actuate.endpoint.jmx; -import java.util.HashMap; -import java.util.Hashtable; -import java.util.Map; +import java.util.Collections; import javax.management.MBeanInfo; import javax.management.MalformedObjectNameException; @@ -29,11 +27,10 @@ import org.junit.Test; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.actuate.endpoint.AbstractEndpoint; +import org.springframework.boot.actuate.endpoint.Endpoint; import org.springframework.context.ApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.jmx.export.MBeanExporter; -import org.springframework.jmx.support.JmxUtils; -import org.springframework.jmx.support.ObjectNameManager; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -61,8 +58,11 @@ public class EndpointMBeanExporterTests { new RootBeanDefinition(EndpointMBeanExporter.class)); this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition( TestEndpoint.class)); - this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( - MBeanExporter.class)); + this.context.registerBeanDefinition( + "mbeanExporter", + new RootBeanDefinition(MBeanExporter.class, null, + new MutablePropertyValues(Collections.singletonMap( + "ensureUniqueRuntimeObjectNames", "false")))); this.context.refresh(); MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); @@ -70,8 +70,8 @@ public class EndpointMBeanExporterTests { MBeanInfo mbeanInfo = mbeanExporter.getServer().getMBeanInfo( getObjectName("endpoint1", this.context)); assertNotNull(mbeanInfo); - assertEquals(3, mbeanInfo.getOperations().length); - assertEquals(2, mbeanInfo.getAttributes().length); + assertEquals(4, mbeanInfo.getOperations().length); + assertEquals(3, mbeanInfo.getAttributes().length); } @Test @@ -83,8 +83,11 @@ public class EndpointMBeanExporterTests { TestEndpoint.class)); this.context.registerBeanDefinition("endpoint2", new RootBeanDefinition( TestEndpoint.class)); - this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( - MBeanExporter.class)); + this.context.registerBeanDefinition( + "mbeanExporter", + new RootBeanDefinition(MBeanExporter.class, null, + new MutablePropertyValues(Collections.singletonMap( + "ensureUniqueRuntimeObjectNames", "false")))); this.context.refresh(); MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); @@ -102,66 +105,30 @@ public class EndpointMBeanExporterTests { new RootBeanDefinition(EndpointMBeanExporter.class)); this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition( TestEndpoint.class)); - this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( - MBeanExporter.class)); + this.context.registerBeanDefinition( + "mbeanExporter", + new RootBeanDefinition(MBeanExporter.class, null, + new MutablePropertyValues(Collections.singletonMap( + "ensureUniqueRuntimeObjectNames", "false")))); GenericApplicationContext parent = new GenericApplicationContext(); - parent.registerBeanDefinition("endpointMbeanExporter", new RootBeanDefinition( - EndpointMBeanExporter.class)); - parent.registerBeanDefinition("endpoint1", new RootBeanDefinition( - TestEndpoint.class)); - parent.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( - MBeanExporter.class)); this.context.setParent(parent); parent.refresh(); this.context.refresh(); - MBeanExporter mbeanExporter = parent.getBean(MBeanExporter.class); + MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); assertNotNull(mbeanExporter.getServer().getMBeanInfo( getObjectName("endpoint1", this.context))); - assertNotNull(mbeanExporter.getServer().getMBeanInfo( - getObjectName("endpoint1", parent))); parent.close(); } - @Test - public void testRegistrationWithCustomDomainAndKey() throws Exception { - Map propertyValues = new HashMap(); - propertyValues.put("domainName", "test.domain"); - - this.context = new GenericApplicationContext(); - this.context.registerBeanDefinition("endpointMbeanExporter", - new RootBeanDefinition(EndpointMBeanExporter.class, null, - new MutablePropertyValues(propertyValues))); - this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition( - TestEndpoint.class)); - this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( - MBeanExporter.class)); - this.context.refresh(); - - MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); - - assertNotNull(mbeanExporter.getServer().getMBeanInfo( - getObjectName("test.domain", "endpoint1", this.context, - this.context.getBean("endpoint1")))); - } - private ObjectName getObjectName(String beanKey, ApplicationContext applicationContext) throws MalformedObjectNameException { - return getObjectName("org.springframework.boot.actuate.endpoint", beanKey, - applicationContext, applicationContext.getBean(beanKey)); - } - - private ObjectName getObjectName(String domainName, String beanKey, - ApplicationContext applicationContext, Object object) - throws MalformedObjectNameException { - Hashtable properties = new Hashtable(); - properties.put("bean", beanKey); - return JmxUtils.appendIdentityToObjectName( - ObjectNameManager.getInstance(domainName, properties), object); + return new EndpointMBean(beanKey, + (Endpoint) applicationContext.getBean(beanKey)).getObjectName(); } public static class TestEndpoint extends AbstractEndpoint {