Repository: incubator-ratis
Updated Branches:
  refs/heads/master 55764cc24 -> 2a781c220


RATIS-119. RaftServerImpl.registerMBean may throw MalformedObjectNameException.


Project: http://git-wip-us.apache.org/repos/asf/incubator-ratis/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ratis/commit/2a781c22
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ratis/tree/2a781c22
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ratis/diff/2a781c22

Branch: refs/heads/master
Commit: 2a781c220e0b9fe564cfa130b18fb952ece90a1e
Parents: 55764cc
Author: Tsz-Wo Nicholas Sze <[email protected]>
Authored: Thu Oct 19 16:03:35 2017 -0700
Committer: Tsz-Wo Nicholas Sze <[email protected]>
Committed: Tue Oct 24 13:04:59 2017 -0700

----------------------------------------------------------------------
 .../java/org/apache/ratis/util/JmxRegister.java |  77 +++++++++++++
 .../apache/ratis/server/RaftServerMXBean.java   |   2 -
 .../ratis/server/impl/RaftServerImpl.java       |  37 ++++---
 .../apache/ratis/server/TestRaftServerJmx.java  |  92 ----------------
 .../ratis/server/impl/TestRaftServerJmx.java    | 109 +++++++++++++++++++
 5 files changed, 205 insertions(+), 112 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/2a781c22/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java
----------------------------------------------------------------------
diff --git a/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java 
b/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java
new file mode 100644
index 0000000..ba4551a
--- /dev/null
+++ b/ratis-common/src/main/java/org/apache/ratis/util/JmxRegister.java
@@ -0,0 +1,77 @@
+/**
+ * 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.ratis.util;
+
+import org.apache.ratis.shaded.com.google.common.base.Supplier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.management.JMException;
+import javax.management.ObjectName;
+import java.lang.management.ManagementFactory;
+
+/** For registering JMX beans. */
+public class JmxRegister {
+  static final Logger LOG = LoggerFactory.getLogger(JmxRegister.class);
+
+  private ObjectName registeredName;
+
+  static ObjectName tryRegister(String name, Object mBean) {
+    final ObjectName objectName;
+    try {
+      objectName = new ObjectName(name);
+      ManagementFactory.getPlatformMBeanServer().registerMBean(mBean, 
objectName);
+    } catch (Exception e) {
+      LOG.error("Failed to register JMX Bean with name " + name, e);
+      return null;
+    }
+
+    LOG.info("Successfully registered JMX Bean with object name " + 
objectName);
+    return objectName;
+  }
+
+  /**
+   * Try registering the mBean with the names one by one.
+   * @return the registered name, or, if it fails, return null.
+   */
+  public synchronized String register(Object mBean, Iterable<Supplier<String>> 
names) {
+    if (registeredName == null) {
+      for (Supplier<String> supplier : names) {
+        final String name = supplier.get();
+        registeredName = tryRegister(name, mBean);
+        if (registeredName != null) {
+          return name;
+        }
+      }
+    }
+
+    // failed
+    return null;
+  }
+
+  /** Un-register the previously registered mBean. */
+  public synchronized boolean unregister() throws JMException {
+    if (registeredName == null) {
+      return false;
+    }
+    ManagementFactory.getPlatformMBeanServer().unregisterMBean(registeredName);
+    LOG.info("Successfully un-registered JMX Bean with object name " + 
registeredName);
+    registeredName = null;
+    return true;
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/2a781c22/ratis-server/src/main/java/org/apache/ratis/server/RaftServerMXBean.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/RaftServerMXBean.java 
b/ratis-server/src/main/java/org/apache/ratis/server/RaftServerMXBean.java
index 9e6dc1b..cadebbb 100644
--- a/ratis-server/src/main/java/org/apache/ratis/server/RaftServerMXBean.java
+++ b/ratis-server/src/main/java/org/apache/ratis/server/RaftServerMXBean.java
@@ -17,8 +17,6 @@
  */
 package org.apache.ratis.server;
 
-import org.apache.ratis.protocol.RaftPeer;
-
 import java.util.List;
 
 /**

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/2a781c22/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
index 2525fb0..f761a24 100644
--- 
a/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
+++ 
b/ratis-server/src/main/java/org/apache/ratis/server/impl/RaftServerImpl.java
@@ -34,11 +34,9 @@ import org.apache.ratis.util.*;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.management.MBeanServer;
 import javax.management.ObjectName;
 import java.io.IOException;
 import java.io.InterruptedIOException;
-import java.lang.management.ManagementFactory;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutionException;
@@ -75,6 +73,7 @@ public class RaftServerImpl implements RaftServerProtocol,
 
   private final LifeCycle lifeCycle;
   private final ServerState state;
+  private final RaftGroupId groupId;
   private volatile Role role;
 
   /** used when the peer is follower, to monitor election timeout */
@@ -88,7 +87,7 @@ public class RaftServerImpl implements RaftServerProtocol,
 
   private final RetryCache retryCache;
 
-  private final RaftGroupId groupId;
+  private final RaftServerJmxAdapter jmxAdapter;
 
   RaftServerImpl(RaftPeerId id, RaftGroup group, RaftServerProxy proxy,
       RaftProperties properties) throws IOException {
@@ -102,6 +101,8 @@ public class RaftServerImpl implements RaftServerProtocol,
     this.proxy = proxy;
     this.state = new ServerState(id, group, properties, this, 
proxy.getStateMachine());
     this.retryCache = initRetryCache(properties);
+
+    this.jmxAdapter = new RaftServerJmxAdapter();
   }
 
   private RetryCache initRetryCache(RaftProperties prop) {
@@ -168,20 +169,17 @@ public class RaftServerImpl implements RaftServerProtocol,
       LOG.debug("{} starts with initializing state, conf={}", getId(), conf);
       startInitializing();
     }
-    registerMBean();
 
+    registerMBean(getId(), getGroupId(), jmxAdapter, jmxAdapter);
   }
 
-  private void registerMBean() {
-    try {
-      final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
-      ObjectName name =
-          new ObjectName("Ratis:service=RaftServer,group=" + getGroupId() + 
",id=" + getId());
-      mbs.registerMBean(new RaftServerJmxAdapter(), name);
-    } catch (Exception ex) {
-      LOG.error("RaftServer JMX bean can't be registered", ex);
-    }
-
+  static boolean registerMBean(
+      RaftPeerId id, RaftGroupId groupdId, RaftServerMXBean mBean, JmxRegister 
jmx) {
+    final String prefix = "Ratis:service=RaftServer,group=" + groupdId + 
",id=";
+    final String registered = jmx.register(mBean, Arrays.asList(
+        () -> prefix + id,
+        () -> prefix + ObjectName.quote(id.toString())));
+    return registered != null;
   }
 
   /**
@@ -218,6 +216,11 @@ public class RaftServerImpl implements RaftServerProtocol,
   void shutdown() {
     lifeCycle.checkStateAndClose(() -> {
       try {
+        jmxAdapter.unregister();
+      } catch (Exception ignored) {
+        LOG.warn("Failed to un-register RaftServer JMX bean for " + getId(), 
ignored);
+      }
+      try {
         shutdownHeartbeatMonitor();
       } catch (Exception ignored) {
         LOG.warn("Failed to shutdown heartbeat monitor for " + getId(), 
ignored);
@@ -971,8 +974,7 @@ public class RaftServerImpl implements RaftServerProtocol,
     }
   }
 
-  private class RaftServerJmxAdapter implements RaftServerMXBean {
-
+  private class RaftServerJmxAdapter extends JmxRegister implements 
RaftServerMXBean {
     @Override
     public String getId() {
       return getState().getSelfId().toString();
@@ -1005,8 +1007,7 @@ public class RaftServerImpl implements RaftServerProtocol,
               leader.getFollowers().stream()
                   .map(RaftPeer::toString)
                   .collect(Collectors.toList()))
-          .orElse(new ArrayList<>());
+          .orElse(Collections.emptyList());
     }
-
   }
 }

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/2a781c22/ratis-server/src/test/java/org/apache/ratis/server/TestRaftServerJmx.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/TestRaftServerJmx.java 
b/ratis-server/src/test/java/org/apache/ratis/server/TestRaftServerJmx.java
deleted file mode 100644
index 7533760..0000000
--- a/ratis-server/src/test/java/org/apache/ratis/server/TestRaftServerJmx.java
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * 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.ratis.server;
-
-import org.apache.log4j.Level;
-import org.apache.ratis.BaseTest;
-import org.apache.ratis.MiniRaftCluster;
-import org.apache.ratis.client.RaftClient;
-import org.apache.ratis.conf.RaftProperties;
-import org.apache.ratis.server.impl.RaftServerImpl;
-import org.apache.ratis.server.simulation.MiniRaftClusterWithSimulatedRpc;
-import org.apache.ratis.util.LogUtils;
-import org.junit.*;
-
-import javax.management.MBeanServer;
-import javax.management.ObjectInstance;
-import javax.management.ObjectName;
-import java.io.IOException;
-import java.lang.management.ManagementFactory;
-import java.util.Set;
-
-import static org.apache.ratis.RaftTestUtil.waitForLeader;
-
-public class TestRaftServerJmx extends BaseTest {
-  static {
-    LogUtils.setLogLevel(RaftServerImpl.LOG, Level.DEBUG);
-    LogUtils.setLogLevel(RaftClient.LOG, Level.DEBUG);
-  }
-
-  public static final int NUM_SERVERS = 5;
-
-  protected static final RaftProperties properties = new RaftProperties();
-
-  private final MiniRaftClusterWithSimulatedRpc cluster;
-
-  public TestRaftServerJmx() throws IOException {
-    cluster = MiniRaftClusterWithSimulatedRpc.FACTORY.newCluster(
-        NUM_SERVERS, getProperties());
-  }
-
-  public RaftProperties getProperties() {
-    return properties;
-  }
-
-  @Before
-  public void setup() throws IOException {
-    Assert.assertNull(getCluster().getLeader());
-    getCluster().start();
-  }
-
-  @After
-  public void tearDown() {
-    final MiniRaftCluster cluster = getCluster();
-    if (cluster != null) {
-      cluster.shutdown();
-    }
-  }
-
-  public MiniRaftClusterWithSimulatedRpc getCluster() {
-    return cluster;
-  }
-
-  @Test
-  public void testJmxBeans() throws Exception {
-    RaftServerImpl leader = waitForLeader(cluster);
-    System.out.println(cluster.getLeader());
-    MBeanServer platformMBeanServer = 
ManagementFactory.getPlatformMBeanServer();
-    Set<ObjectInstance> objectInstances = platformMBeanServer.queryMBeans(new 
ObjectName("Ratis:*"), null);
-    Assert.assertEquals(NUM_SERVERS, objectInstances.size());
-
-    for (ObjectInstance instance : objectInstances) {
-      Object groupId = 
platformMBeanServer.getAttribute(instance.getObjectName(), "GroupId");
-      Assert.assertEquals(cluster.getGroupId().toString(), groupId);
-    }
-  }
-
-}

http://git-wip-us.apache.org/repos/asf/incubator-ratis/blob/2a781c22/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
----------------------------------------------------------------------
diff --git 
a/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
 
b/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
new file mode 100644
index 0000000..d300a27
--- /dev/null
+++ 
b/ratis-server/src/test/java/org/apache/ratis/server/impl/TestRaftServerJmx.java
@@ -0,0 +1,109 @@
+/**
+ * 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.ratis.server.impl;
+
+import org.apache.ratis.BaseTest;
+import org.apache.ratis.conf.RaftProperties;
+import org.apache.ratis.protocol.RaftGroupId;
+import org.apache.ratis.protocol.RaftPeerId;
+import org.apache.ratis.server.RaftServerMXBean;
+import org.apache.ratis.server.simulation.MiniRaftClusterWithSimulatedRpc;
+import org.apache.ratis.util.JmxRegister;
+import org.junit.Assert;
+import org.junit.Test;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.ObjectInstance;
+import javax.management.ObjectName;
+import java.lang.management.ManagementFactory;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.ratis.RaftTestUtil.waitForLeader;
+
+public class TestRaftServerJmx extends BaseTest {
+  @Test
+  public void testJmxBeans() throws Exception {
+    final int NUM_SERVERS = 3;
+    final MiniRaftClusterWithSimulatedRpc cluster
+        = MiniRaftClusterWithSimulatedRpc.FACTORY.newCluster(3, new 
RaftProperties());
+    cluster.start();
+    waitForLeader(cluster);
+
+    MBeanServer platformMBeanServer = 
ManagementFactory.getPlatformMBeanServer();
+    Set<ObjectInstance> objectInstances = platformMBeanServer.queryMBeans(new 
ObjectName("Ratis:*"), null);
+    Assert.assertEquals(NUM_SERVERS, objectInstances.size());
+
+    for (ObjectInstance instance : objectInstances) {
+      Object groupId = 
platformMBeanServer.getAttribute(instance.getObjectName(), "GroupId");
+      Assert.assertEquals(cluster.getGroupId().toString(), groupId);
+    }
+    cluster.shutdown();
+  }
+
+  @Test
+  public void testRegister() throws JMException {
+    {
+      final JmxRegister jmx = new JmxRegister();
+      runUnregister(false, jmx);
+
+      runRegister(true, "abc", jmx);
+      runRegister(false, "abc", jmx);
+      runUnregister(true, jmx);
+      runUnregister(false, jmx);
+
+      runRegister(true, "abc", jmx);
+      runUnregister(true, jmx);
+      runUnregister(false, jmx);
+    }
+
+    {
+      final JmxRegister jmx = new JmxRegister();
+      runRegister(true, "host:1234", jmx);
+      runUnregister(true, jmx);
+      runUnregister(false, jmx);
+    }
+  }
+
+  static void runRegister(boolean expectToSucceed, String name, JmxRegister 
jmx) {
+    final RaftServerMXBean mBean = new RaftServerMXBean() {
+      @Override
+      public String getId() { return null; }
+      @Override
+      public String getLeaderId() { return null; }
+      @Override
+      public long getCurrentTerm() { return 0; }
+      @Override
+      public String getGroupId() { return null; }
+      @Override
+      public String getRole() { return null; }
+      @Override
+      public List<String> getFollowers() { return null; }
+    };
+    final RaftPeerId id = RaftPeerId.valueOf(name);
+    final RaftGroupId groupId = RaftGroupId.randomId();
+    final boolean succeeded = RaftServerImpl.registerMBean(id, groupId, mBean, 
jmx);
+    Assert.assertEquals(expectToSucceed, succeeded);
+  }
+
+  static void runUnregister(boolean expectToSucceed, JmxRegister jmx) throws 
JMException {
+    final boolean succeeded = jmx.unregister();
+    Assert.assertEquals(expectToSucceed, succeeded);
+  }
+}

Reply via email to