From 22194f3d21fd28b97c6197a8dd1917d3d23d7cc8 Mon Sep 17 00:00:00 2001 From: Xiaoyu Yao Date: Mon, 26 Mar 2018 10:45:29 -0700 Subject: [PATCH] HADOOP-15339. Support additional key/value propereties in JMX bean registration. Contributed by Elek Marton. --- .../apache/hadoop/metrics2/util/MBeans.java | 52 ++++++++- .../hadoop/metrics2/util/DummyMXBean.java | 26 +++++ .../hadoop/metrics2/util/TestMBeans.java | 107 ++++++++++++++++++ 3 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/DummyMXBean.java create mode 100644 hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/TestMBeans.java diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/util/MBeans.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/util/MBeans.java index ded49d6a6e..916367f043 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/util/MBeans.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/util/MBeans.java @@ -18,27 +18,33 @@ package org.apache.hadoop.metrics2.util; import java.lang.management.ManagementFactory; +import java.util.HashMap; +import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import javax.management.InstanceAlreadyExistsException; import javax.management.MBeanServer; import javax.management.ObjectName; +import com.google.common.annotations.VisibleForTesting; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.metrics2.lib.DefaultMetricsSystem; + +import com.google.common.base.Preconditions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This util class provides a method to register an MBean using * our standard naming convention as described in the doc - * for {link {@link #register(String, String, Object)} + * for {link {@link #register(String, String, Object)}. */ @InterfaceAudience.Public @InterfaceStability.Stable -public class MBeans { +public final class MBeans { private static final Logger LOG = LoggerFactory.getLogger(MBeans.class); private static final String DOMAIN_PREFIX = "Hadoop:"; private static final String SERVICE_PREFIX = "service="; @@ -48,10 +54,13 @@ public class MBeans { "^" + DOMAIN_PREFIX + SERVICE_PREFIX + "([^,]+)," + NAME_PREFIX + "(.+)$"); + private MBeans() { + } + /** * Register the MBean using our standard MBeanName format * "hadoop:service=,name=" - * Where the and are the supplied parameters + * Where the and are the supplied parameters. * * @param serviceName * @param nameName @@ -60,8 +69,30 @@ public class MBeans { */ static public ObjectName register(String serviceName, String nameName, Object theMbean) { + return register(serviceName, nameName, new HashMap(), + theMbean); + } + + /** + * Register the MBean using our standard MBeanName format + * "hadoop:service=,name=" + * Where the and are the supplied parameters. + * + * @param serviceName + * @param nameName + * @param properties - Key value pairs to define additional JMX ObjectName + * properties. + * @param theMbean - the MBean to register + * @return the named used to register the MBean + */ + static public ObjectName register(String serviceName, String nameName, + Map properties, + Object theMbean) { final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - ObjectName name = getMBeanName(serviceName, nameName); + Preconditions.checkNotNull(properties, + "JMX bean properties should not be null for " + + "bean registration."); + ObjectName name = getMBeanName(serviceName, nameName, properties); if (name != null) { try { mbs.registerMBean(theMbean, name); @@ -116,9 +147,18 @@ static public void unregister(ObjectName mbeanName) { DefaultMetricsSystem.removeMBeanName(mbeanName); } - static private ObjectName getMBeanName(String serviceName, String nameName) { + @VisibleForTesting + static ObjectName getMBeanName(String serviceName, String nameName, + Map additionalParameters) { + + String additionalKeys = additionalParameters.entrySet() + .stream() + .map(entry -> entry.getKey() + "=" + entry.getValue()) + .collect(Collectors.joining(",")); + String nameStr = DOMAIN_PREFIX + SERVICE_PREFIX + serviceName + "," + - NAME_PREFIX + nameName; + NAME_PREFIX + nameName + + (additionalKeys.isEmpty() ? "" : "," + additionalKeys); try { return DefaultMetricsSystem.newMBeanName(nameStr); } catch (Exception e) { diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/DummyMXBean.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/DummyMXBean.java new file mode 100644 index 0000000000..e1ff35ca94 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/DummyMXBean.java @@ -0,0 +1,26 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.hadoop.metrics2.util; + +/** + * Sample JMX Bean interface to test JMX registration. + */ +public interface DummyMXBean { + + int getCounter(); +} diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/TestMBeans.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/TestMBeans.java new file mode 100644 index 0000000000..3c93dbee06 --- /dev/null +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/metrics2/util/TestMBeans.java @@ -0,0 +1,107 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * http://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.apache.hadoop.metrics2.util; + +import org.junit.Assert; +import org.junit.Test; + +import javax.management.MBeanServer; +import javax.management.ObjectName; +import java.lang.management.ManagementFactory; +import java.util.HashMap; +import java.util.Map; + +/** + * Test MXBean addition of key/value pairs to registered MBeans. + */ +public class TestMBeans implements DummyMXBean { + + private int counter = 1; + + @Test + public void testRegister() throws Exception { + ObjectName objectName = null; + try { + counter = 23; + objectName = MBeans.register("UnitTest", + "RegisterTest", this); + + MBeanServer platformMBeanServer = + ManagementFactory.getPlatformMBeanServer(); + + int jmxCounter = (int) platformMBeanServer + .getAttribute(objectName, "Counter"); + Assert.assertEquals(counter, jmxCounter); + } finally { + if (objectName != null) { + MBeans.unregister(objectName); + } + } + } + + + @Test + public void testRegisterWithAdditionalProperties() throws Exception { + ObjectName objectName = null; + try { + counter = 42; + + Map properties = new HashMap(); + properties.put("flavour", "server"); + objectName = MBeans.register("UnitTest", "RegisterTest", + properties, this); + + MBeanServer platformMBeanServer = + ManagementFactory.getPlatformMBeanServer(); + int jmxCounter = + (int) platformMBeanServer.getAttribute(objectName, "Counter"); + Assert.assertEquals(counter, jmxCounter); + } finally { + if (objectName != null) { + MBeans.unregister(objectName); + } + } + } + + @Test + public void testGetMbeanNameName() { + HashMap properties = new HashMap<>(); + + ObjectName mBeanName = MBeans.getMBeanName("Service", + "Name", properties); + + Assert.assertEquals("Service", + MBeans.getMbeanNameService(mBeanName)); + + properties.put("key", "value"); + mBeanName = MBeans.getMBeanName( + "Service", + "Name", + properties); + + Assert.assertEquals("Service", + MBeans.getMbeanNameService(mBeanName)); + + } + + @Override + public int getCounter() { + return counter; + } + +} \ No newline at end of file