Rework EndpointMBeanExporter to prevent name clashes and to provide more flexibility in naming of endpoint MBeans

This commit is contained in:
Christian Dupuis
2013-12-26 15:07:42 +01:00
parent b81930fcce
commit 65d6757a10
5 changed files with 274 additions and 78 deletions

View File

@@ -16,13 +16,24 @@
package org.springframework.boot.actuate.autoconfigure;
import javax.management.InstanceNotFoundException;
import javax.management.IntrospectionException;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.management.ReflectionException;
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.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableMBeanExport;
import org.springframework.jmx.export.MBeanExporter;
import org.springframework.jmx.support.ObjectNameManager;
import org.springframework.mock.env.MockEnvironment;
import org.springframework.util.ObjectUtils;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.fail;
@@ -53,7 +64,10 @@ public class EndpointMBeanExportAutoConfigurationTests {
@Test(expected = NoSuchBeanDefinitionException.class)
public void testEndpointMBeanExporterIsNotInstalled() {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("endpoints.jmx.enabled", "false");
this.context = new AnnotationConfigApplicationContext();
this.context.setEnvironment(environment);
this.context.register(EndpointAutoConfiguration.class,
EndpointMBeanExportAutoConfiguration.class);
this.context.refresh();
@@ -61,6 +75,35 @@ public class EndpointMBeanExportAutoConfigurationTests {
fail();
}
@Test
public void testEndpointMBeanExporterWithProperties() throws IntrospectionException,
InstanceNotFoundException, MalformedObjectNameException, ReflectionException {
MockEnvironment environment = new MockEnvironment();
environment.setProperty("endpoints.jmx.domain", "test-domain");
environment.setProperty("endpoints.jmx.unique_names", "true");
environment.setProperty("endpoints.jmx.static_names", "key1=value1, key2=value2");
this.context = new AnnotationConfigApplicationContext();
this.context.setEnvironment(environment);
this.context.register(EndpointAutoConfiguration.class,
EndpointMBeanExportAutoConfiguration.class);
this.context.refresh();
this.context.getBean(EndpointMBeanExporter.class);
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
ObjectNameManager.getInstance(getObjectName("test-domain",
"healthEndpoint", this.context).toString()
+ ",key1=value1,key2=value2")));
}
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))));
}
@Configuration
@EnableMBeanExport
public static class TestConfiguration {

View File

@@ -17,6 +17,9 @@
package org.springframework.boot.actuate.endpoint.jmx;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.management.MBeanInfo;
import javax.management.MalformedObjectNameException;
@@ -27,10 +30,11 @@ 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.ObjectNameManager;
import org.springframework.util.ObjectUtils;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -58,20 +62,15 @@ public class EndpointMBeanExporterTests {
new RootBeanDefinition(EndpointMBeanExporter.class));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.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);
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
MBeanInfo mbeanInfo = mbeanExporter.getServer().getMBeanInfo(
getObjectName("endpoint1", this.context));
assertNotNull(mbeanInfo);
assertEquals(5, mbeanInfo.getOperations().length);
assertEquals(5, mbeanInfo.getAttributes().length);
assertEquals(3, mbeanInfo.getOperations().length);
assertEquals(3, mbeanInfo.getAttributes().length);
}
@Test
@@ -83,14 +82,9 @@ public class EndpointMBeanExporterTests {
TestEndpoint.class));
this.context.registerBeanDefinition("endpoint2", new RootBeanDefinition(
TestEndpoint.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);
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
getObjectName("endpoint1", this.context)));
@@ -98,6 +92,69 @@ public class EndpointMBeanExporterTests {
getObjectName("endpoint2", this.context)));
}
@Test
public void testRegistrationWithDifferentDomain() throws Exception {
this.context = new GenericApplicationContext();
this.context.registerBeanDefinition(
"endpointMbeanExporter",
new RootBeanDefinition(EndpointMBeanExporter.class, null,
new MutablePropertyValues(Collections.singletonMap("domain",
"test-domain"))));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.class));
this.context.refresh();
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
getObjectName("test-domain", "endpoint1", false, this.context)));
}
@Test
public void testRegistrationWithDifferentDomainAndIdentity() throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("domain", "test-domain");
properties.put("ensureUniqueRuntimeObjectNames", true);
this.context = new GenericApplicationContext();
this.context.registerBeanDefinition("endpointMbeanExporter",
new RootBeanDefinition(EndpointMBeanExporter.class, null,
new MutablePropertyValues(properties)));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.class));
this.context.refresh();
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
getObjectName("test-domain", "endpoint1", true, this.context)));
}
@Test
public void testRegistrationWithDifferentDomainAndIdentityAndStaticNames()
throws Exception {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("domain", "test-domain");
properties.put("ensureUniqueRuntimeObjectNames", true);
Properties staticNames = new Properties();
staticNames.put("key1", "value1");
staticNames.put("key2", "value2");
properties.put("objectNameStaticProperties", staticNames);
this.context = new GenericApplicationContext();
this.context.registerBeanDefinition("endpointMbeanExporter",
new RootBeanDefinition(EndpointMBeanExporter.class, null,
new MutablePropertyValues(properties)));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.class));
this.context.refresh();
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
ObjectNameManager.getInstance(getObjectName("test-domain", "endpoint1",
true, this.context).toString()
+ ",key1=value1,key2=value2")));
}
@Test
public void testRegistrationWithParentContext() throws Exception {
this.context = new GenericApplicationContext();
@@ -105,19 +162,13 @@ public class EndpointMBeanExporterTests {
new RootBeanDefinition(EndpointMBeanExporter.class));
this.context.registerBeanDefinition("endpoint1", new RootBeanDefinition(
TestEndpoint.class));
this.context.registerBeanDefinition(
"mbeanExporter",
new RootBeanDefinition(MBeanExporter.class, null,
new MutablePropertyValues(Collections.singletonMap(
"ensureUniqueRuntimeObjectNames", "false"))));
GenericApplicationContext parent = new GenericApplicationContext();
this.context.setParent(parent);
parent.refresh();
this.context.refresh();
MBeanExporter mbeanExporter = this.context.getBean(MBeanExporter.class);
MBeanExporter mbeanExporter = this.context.getBean(EndpointMBeanExporter.class);
assertNotNull(mbeanExporter.getServer().getMBeanInfo(
getObjectName("endpoint1", this.context)));
@@ -125,10 +176,25 @@ public class EndpointMBeanExporterTests {
parent.close();
}
private ObjectName getObjectName(String beanKey, ApplicationContext applicationContext)
private ObjectName getObjectName(String beanKey, GenericApplicationContext context)
throws MalformedObjectNameException {
return new DataEndpointMBean(beanKey,
(Endpoint<?>) applicationContext.getBean(beanKey)).getObjectName();
return getObjectName("org.springframework.boot", beanKey, false, context);
}
private ObjectName getObjectName(String domain, String beanKey,
boolean includeIdentity, ApplicationContext applicationContext)
throws MalformedObjectNameException {
if (includeIdentity) {
return ObjectNameManager
.getInstance(String.format("%s:type=Endpoint,name=%s,identity=%s",
domain, beanKey, ObjectUtils
.getIdentityHexString(applicationContext
.getBean(beanKey))));
}
else {
return ObjectNameManager.getInstance(String.format(
"%s:type=Endpoint,name=%s", domain, beanKey));
}
}
public static class TestEndpoint extends AbstractEndpoint<String> {