Commit 31f7807a authored by Christian Dupuis's avatar Christian Dupuis

Change naming strategy for endpoint mbeans

parent e2c962ac
...@@ -16,19 +16,15 @@ ...@@ -16,19 +16,15 @@
package org.springframework.boot.actuate.autoconfigure; 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.Endpoint;
import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter; import org.springframework.boot.actuate.endpoint.jmx.EndpointMBeanExporter;
import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.bind.RelaxedPropertyResolver;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jmx.export.MBeanExporter; import org.springframework.jmx.export.MBeanExporter;
import org.springframework.util.StringUtils;
/** /**
* {@link EnableAutoConfiguration Auto-configuration} to enable JMX export for * {@link EnableAutoConfiguration Auto-configuration} to enable JMX export for
...@@ -42,20 +38,8 @@ import org.springframework.util.StringUtils; ...@@ -42,20 +38,8 @@ import org.springframework.util.StringUtils;
@ConditionalOnExpression("${endpoints.jmx.enabled:true}") @ConditionalOnExpression("${endpoints.jmx.enabled:true}")
class EndpointMBeanExportAutoConfiguration { class EndpointMBeanExportAutoConfiguration {
private RelaxedPropertyResolver environment;
@Autowired
public void setEnvironment(Environment environment) {
this.environment = new RelaxedPropertyResolver(environment);
}
@Bean @Bean
public EndpointMBeanExporter endpointMBeanExporter() { public EndpointMBeanExporter endpointMBeanExporter() {
EndpointMBeanExporter mbeanExporter = new EndpointMBeanExporter(); return new EndpointMBeanExporter();
String domainName = this.environment.getProperty("endpoints.jmx.domain_name");
if (StringUtils.hasText(domainName)) {
mbeanExporter.setDomainName(domainName);
}
return mbeanExporter;
} }
} }
\ No newline at end of file
...@@ -19,10 +19,16 @@ package org.springframework.boot.actuate.endpoint.jmx; ...@@ -19,10 +19,16 @@ package org.springframework.boot.actuate.endpoint.jmx;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.springframework.boot.actuate.endpoint.Endpoint; 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.ManagedAttribute;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource; 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 org.springframework.util.ClassUtils;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
...@@ -33,14 +39,24 @@ import com.fasterxml.jackson.databind.ObjectMapper; ...@@ -33,14 +39,24 @@ import com.fasterxml.jackson.databind.ObjectMapper;
* @author Christian Dupuis * @author Christian Dupuis
*/ */
@ManagedResource @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 Endpoint<?> endpoint;
private String beanName;
private ObjectMapper mapper = new ObjectMapper(); 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.endpoint = endpoint;
this.beanName = beanName;
} }
@ManagedAttribute(description = "Returns the class of the underlying endpoint") @ManagedAttribute(description = "Returns the class of the underlying endpoint")
...@@ -53,7 +69,7 @@ public class EndpointMBean { ...@@ -53,7 +69,7 @@ public class EndpointMBean {
return this.endpoint.isSensitive(); return this.endpoint.isSensitive();
} }
@ManagedOperation(description = "Invoke the underlying endpoint") @ManagedAttribute(description = "Invoke the underlying endpoint")
public Object invoke() { public Object invoke() {
Object result = this.endpoint.invoke(); Object result = this.endpoint.invoke();
if (result == null) { if (result == null) {
...@@ -70,4 +86,9 @@ public class EndpointMBean { ...@@ -70,4 +86,9 @@ public class EndpointMBean {
return this.mapper.convertValue(result, Map.class); return this.mapper.convertValue(result, Map.class);
} }
@Override
public ObjectName getObjectName() throws MalformedObjectNameException {
return this.metadataNamingStrategy.getObjectName(this, this.beanName);
}
} }
...@@ -17,14 +17,11 @@ ...@@ -17,14 +17,11 @@
package org.springframework.boot.actuate.endpoint.jmx; package org.springframework.boot.actuate.endpoint.jmx;
import java.util.HashSet; import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
...@@ -37,10 +34,7 @@ import org.springframework.context.ApplicationListener; ...@@ -37,10 +34,7 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.SmartLifecycle; import org.springframework.context.SmartLifecycle;
import org.springframework.jmx.export.MBeanExportException; import org.springframework.jmx.export.MBeanExportException;
import org.springframework.jmx.export.MBeanExporter; 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.Assert;
import org.springframework.util.ClassUtils;
/** /**
* {@link ApplicationListener} that registers all known {@link Endpoint}s with an * {@link ApplicationListener} that registers all known {@link Endpoint}s with an
...@@ -51,13 +45,8 @@ import org.springframework.util.ClassUtils; ...@@ -51,13 +45,8 @@ import org.springframework.util.ClassUtils;
*/ */
public class EndpointMBeanExporter implements SmartLifecycle, ApplicationContextAware { 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 static Log logger = LogFactory.getLog(EndpointMBeanExporter.class);
private String domainName = DEFAULT_DOMAIN_NAME;
private Set<Endpoint<?>> registeredEndpoints = new HashSet<Endpoint<?>>(); private Set<Endpoint<?>> registeredEndpoints = new HashSet<Endpoint<?>>();
private volatile boolean autoStartup = true; private volatile boolean autoStartup = true;
...@@ -76,11 +65,6 @@ public class EndpointMBeanExporter implements SmartLifecycle, ApplicationContext ...@@ -76,11 +65,6 @@ public class EndpointMBeanExporter implements SmartLifecycle, ApplicationContext
this.applicationContext = applicationContext; this.applicationContext = applicationContext;
} }
public void setDomainName(String domainName) {
Assert.notNull(domainName, "DomainName must not be null");
this.domainName = domainName;
}
protected void doStart() { protected void doStart() {
try { try {
MBeanExporter mbeanExporter = this.applicationContext MBeanExporter mbeanExporter = this.applicationContext
...@@ -108,29 +92,14 @@ public class EndpointMBeanExporter implements SmartLifecycle, 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) { MBeanExporter mbeanExporter) {
try { try {
mbeanExporter.registerManagedResource(new EndpointMBean(endpoint), mbeanExporter.registerManagedResource(new EndpointMBean(beanName, endpoint));
getObjectName(beanKey, endpoint));
} }
catch (MBeanExportException ex) { 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<String, String> properties = new Hashtable<String, String>();
properties.put("bean", beanKey);
return JmxUtils.appendIdentityToObjectName(
ObjectNameManager.getInstance(this.domainName, properties), endpoint);
} }
// SmartLifeCycle implementation // SmartLifeCycle implementation
......
...@@ -16,9 +16,7 @@ ...@@ -16,9 +16,7 @@
package org.springframework.boot.actuate.endpoint.jmx; package org.springframework.boot.actuate.endpoint.jmx;
import java.util.HashMap; import java.util.Collections;
import java.util.Hashtable;
import java.util.Map;
import javax.management.MBeanInfo; import javax.management.MBeanInfo;
import javax.management.MalformedObjectNameException; import javax.management.MalformedObjectNameException;
...@@ -29,11 +27,10 @@ import org.junit.Test; ...@@ -29,11 +27,10 @@ import org.junit.Test;
import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.actuate.endpoint.AbstractEndpoint; import org.springframework.boot.actuate.endpoint.AbstractEndpoint;
import org.springframework.boot.actuate.endpoint.Endpoint;
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext; import org.springframework.context.support.GenericApplicationContext;
import org.springframework.jmx.export.MBeanExporter; 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.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
...@@ -61,8 +58,11 @@ public class EndpointMBeanExporterTests { ...@@ -61,8 +58,11 @@ public class EndpointMBeanExporterTests {
new RootBeanDefinition(EndpointMBeanExporter.class)); new RootBeanDefinition(EndpointMBeanExporter.class));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition( this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.class)); TestEndpoint.class));
this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( this.context.registerBeanDefinition(
MBeanExporter.class)); "mbeanExporter",
new RootBeanDefinition(MBeanExporter.class, null,
new MutablePropertyValues(Collections.singletonMap(
"ensureUniqueRuntimeObjectNames", "false"))));
this.context.refresh(); this.context.refresh();
MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class);
...@@ -70,8 +70,8 @@ public class EndpointMBeanExporterTests { ...@@ -70,8 +70,8 @@ public class EndpointMBeanExporterTests {
MBeanInfo mbeanInfo = mbeanExporter.getServer().getMBeanInfo( MBeanInfo mbeanInfo = mbeanExporter.getServer().getMBeanInfo(
getObjectName("endpoint1", this.context)); getObjectName("endpoint1", this.context));
assertNotNull(mbeanInfo); assertNotNull(mbeanInfo);
assertEquals(3, mbeanInfo.getOperations().length); assertEquals(4, mbeanInfo.getOperations().length);
assertEquals(2, mbeanInfo.getAttributes().length); assertEquals(3, mbeanInfo.getAttributes().length);
} }
@Test @Test
...@@ -83,8 +83,11 @@ public class EndpointMBeanExporterTests { ...@@ -83,8 +83,11 @@ public class EndpointMBeanExporterTests {
TestEndpoint.class)); TestEndpoint.class));
this.context.registerBeanDefinition("endpoint2", new RootBeanDefinition( this.context.registerBeanDefinition("endpoint2", new RootBeanDefinition(
TestEndpoint.class)); TestEndpoint.class));
this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( this.context.registerBeanDefinition(
MBeanExporter.class)); "mbeanExporter",
new RootBeanDefinition(MBeanExporter.class, null,
new MutablePropertyValues(Collections.singletonMap(
"ensureUniqueRuntimeObjectNames", "false"))));
this.context.refresh(); this.context.refresh();
MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class); MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class);
...@@ -102,66 +105,30 @@ public class EndpointMBeanExporterTests { ...@@ -102,66 +105,30 @@ public class EndpointMBeanExporterTests {
new RootBeanDefinition(EndpointMBeanExporter.class)); new RootBeanDefinition(EndpointMBeanExporter.class));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition( this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.class)); TestEndpoint.class));
this.context.registerBeanDefinition("mbeanExporter", new RootBeanDefinition( this.context.registerBeanDefinition(
MBeanExporter.class)); "mbeanExporter",
new RootBeanDefinition(MBeanExporter.class, null,
new MutablePropertyValues(Collections.singletonMap(
"ensureUniqueRuntimeObjectNames", "false"))));
GenericApplicationContext parent = new GenericApplicationContext(); 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); this.context.setParent(parent);
parent.refresh(); parent.refresh();
this.context.refresh(); this.context.refresh();
MBeanExporter mbeanExporter = parent.getBean(MBeanExporter.class); MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo( assertNotNull(mbeanExporter.getServer().getMBeanInfo(
getObjectName("endpoint1", this.context))); getObjectName("endpoint1", this.context)));
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
getObjectName("endpoint1", parent)));
parent.close(); parent.close();
} }
@Test
public void testRegistrationWithCustomDomainAndKey() throws Exception {
Map<String, String> propertyValues = new HashMap<String, String>();
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) private ObjectName getObjectName(String beanKey, ApplicationContext applicationContext)
throws MalformedObjectNameException { throws MalformedObjectNameException {
return getObjectName("org.springframework.boot.actuate.endpoint", beanKey, return new EndpointMBean(beanKey,
applicationContext, applicationContext.getBean(beanKey)); (Endpoint<?>) applicationContext.getBean(beanKey)).getObjectName();
}
private ObjectName getObjectName(String domainName, String beanKey,
ApplicationContext applicationContext, Object object)
throws MalformedObjectNameException {
Hashtable<String, String> properties = new Hashtable<String, String>();
properties.put("bean", beanKey);
return JmxUtils.appendIdentityToObjectName(
ObjectNameManager.getInstance(domainName, properties), object);
} }
public static class TestEndpoint extends AbstractEndpoint<String> { public static class TestEndpoint extends AbstractEndpoint<String> {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment