HADOOP-15339. Support additional key/value propereties in JMX bean registration. Contributed by Elek Marton.
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 ded49d6..916367f 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 @@
"^" + DOMAIN_PREFIX + SERVICE_PREFIX + "([^,]+)," +
NAME_PREFIX + "(.+)$");
+ private MBeans() {
+ }
+
/**
* Register the MBean using our standard MBeanName format
* "hadoop:service=<serviceName>,name=<nameName>"
- * Where the <serviceName> and <nameName> are the supplied parameters
+ * Where the <serviceName> and <nameName> are the supplied parameters.
*
* @param serviceName
* @param nameName
@@ -60,8 +69,30 @@
*/
static public ObjectName register(String serviceName, String nameName,
Object theMbean) {
+ return register(serviceName, nameName, new HashMap<String, String>(),
+ theMbean);
+ }
+
+ /**
+ * Register the MBean using our standard MBeanName format
+ * "hadoop:service=<serviceName>,name=<nameName>"
+ * Where the <serviceName> and <nameName> 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<String, String> 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 @@
DefaultMetricsSystem.removeMBeanName(mbeanName);
}
- static private ObjectName getMBeanName(String serviceName, String nameName) {
+ @VisibleForTesting
+ static ObjectName getMBeanName(String serviceName, String nameName,
+ Map<String, String> 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 0000000..e1ff35c
--- /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 0000000..3c93dbe
--- /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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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<String, String> properties = new HashMap<String, String>();
+ 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<String, String> 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