From 473b9ca7fc03444888bf96c33fa58985249984f5 Mon Sep 17 00:00:00 2001 From: Ryan Rupp Date: Sun, 25 May 2014 15:38:29 -0500 Subject: [PATCH] Allow the JMX ObjectNameMapper to be specified as a property (com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass) when the DefaultMonitorRegistry is bootstrapped. --- .../netflix/servo/DefaultMonitorRegistry.java | 40 ++++++++++++++++- .../servo/DefaultMonitorRegistryTest.java | 45 +++++++++++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/servo-core/src/main/java/com/netflix/servo/DefaultMonitorRegistry.java b/servo-core/src/main/java/com/netflix/servo/DefaultMonitorRegistry.java index caf69acd..d6a6fbed 100644 --- a/servo-core/src/main/java/com/netflix/servo/DefaultMonitorRegistry.java +++ b/servo-core/src/main/java/com/netflix/servo/DefaultMonitorRegistry.java @@ -16,7 +16,9 @@ package com.netflix.servo; import com.netflix.servo.jmx.JmxMonitorRegistry; +import com.netflix.servo.jmx.ObjectNameMapper; import com.netflix.servo.monitor.Monitor; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -29,6 +31,13 @@ * specified registry class must have a constructor with no arguments. If the * property is not specified or the class cannot be loaded an instance of * {@link com.netflix.servo.jmx.JmxMonitorRegistry} will be used. + *

+ * If the default {@link com.netflix.servo.jmx.JmxMonitorRegistry} is used, the property + * {@code com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass} can optionally be + * specified to control how monitors are mapped to JMX {@link javax.management.ObjectName}. + * This property specifies the {@link com.netflix.servo.jmx.ObjectNameMapper} + * implementation class to use. The implementation must have a constructor with + * no arguments. */ public final class DefaultMonitorRegistry implements MonitorRegistry { @@ -36,6 +45,7 @@ public final class DefaultMonitorRegistry implements MonitorRegistry { private static final String CLASS_NAME = DefaultMonitorRegistry.class.getCanonicalName(); private static final String REGISTRY_CLASS_PROP = CLASS_NAME + ".registryClass"; private static final String REGISTRY_NAME_PROP = CLASS_NAME + ".registryName"; + private static final String REGISTRY_JMX_NAME_PROP = CLASS_NAME + ".jmxMapperClass"; private static final MonitorRegistry INSTANCE = new DefaultMonitorRegistry(); private static final String DEFAULT_REGISTRY_NAME = "com.netflix.servo"; @@ -77,8 +87,36 @@ public static MonitorRegistry getInstance() { } registry = r; } else { - registry = new JmxMonitorRegistry(registryName); + registry = new JmxMonitorRegistry(registryName, + getObjectNameMapper(props)); + } + } + + /** + * Gets the {@link ObjectNameMapper} to use by looking at the + * {@code com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass} + * property. If not specified, then {@link ObjectNameMapper#DEFAULT} + * is used. + * @param props the properties + * @return the mapper to use + */ + private static ObjectNameMapper getObjectNameMapper(Properties props) { + ObjectNameMapper mapper = ObjectNameMapper.DEFAULT; + final String jmxNameMapperClass = props.getProperty(REGISTRY_JMX_NAME_PROP); + if (jmxNameMapperClass != null) { + try { + Class mapperClazz = Class.forName(jmxNameMapperClass); + mapper = (ObjectNameMapper) mapperClazz.newInstance(); + } catch (Throwable t) { + LOG.error( + "failed to create the JMX ObjectNameMapper instance of class " + + jmxNameMapperClass + + ", using the default naming scheme", + t); + } } + + return mapper; } /** diff --git a/servo-core/src/test/java/com/netflix/servo/DefaultMonitorRegistryTest.java b/servo-core/src/test/java/com/netflix/servo/DefaultMonitorRegistryTest.java index c2f66792..80dde37c 100644 --- a/servo-core/src/test/java/com/netflix/servo/DefaultMonitorRegistryTest.java +++ b/servo-core/src/test/java/com/netflix/servo/DefaultMonitorRegistryTest.java @@ -17,13 +17,19 @@ import com.google.common.collect.Sets; import com.netflix.servo.jmx.JmxMonitorRegistry; +import com.netflix.servo.jmx.ObjectNameMapper; +import com.netflix.servo.monitor.BasicCounter; import com.netflix.servo.monitor.Monitor; import com.netflix.servo.monitor.MonitorConfig; + import org.testng.annotations.Test; +import java.lang.management.ManagementFactory; import java.util.Properties; import java.util.Set; +import javax.management.ObjectName; + import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; @@ -136,4 +142,43 @@ public void testGetRegisteredMonitors() throws Exception { assertTrue(monitors.contains(m1)); assertTrue(monitors.contains(m2)); }*/ + + @Test + public void testCustomJmxObjectMapper() { + Properties props = new Properties(); + props.put("com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass", + "com.netflix.servo.DefaultMonitorRegistryTest$ChangeDomainMapper"); + DefaultMonitorRegistry registry = new DefaultMonitorRegistry(props); + BasicCounter counter = new BasicCounter( + new MonitorConfig.Builder("testCustomJmxObjectMapper").build()); + registry.register(counter); + ObjectName expectedName = + new ChangeDomainMapper().createObjectName("com.netflix.servo", counter); + assertEquals(expectedName.getDomain(), "com.netflix.servo.Renamed"); + assertTrue(ManagementFactory.getPlatformMBeanServer().isRegistered(expectedName)); + } + + @Test + public void testInvalidMapperDefaults() { + Properties props = new Properties(); + props.put("com.netflix.servo.DefaultMonitorRegistry.jmxMapperClass", + "com.my.invalid.class"); + DefaultMonitorRegistry registry = new DefaultMonitorRegistry(props); + BasicCounter counter = new BasicCounter( + new MonitorConfig.Builder("testInvalidMapperDefaults").build()); + registry.register(counter); + ObjectName expectedName = + ObjectNameMapper.DEFAULT.createObjectName("com.netflix.servo", counter); + assertEquals(expectedName.getDomain(), "com.netflix.servo"); + assertTrue(ManagementFactory.getPlatformMBeanServer().isRegistered(expectedName)); + } + + public static class ChangeDomainMapper implements ObjectNameMapper { + + @Override + public ObjectName createObjectName(String domain, Monitor monitor) { + return ObjectNameMapper.DEFAULT.createObjectName(domain + ".Renamed", monitor); + } + + } }