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); + } +}
