1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.zookeeper.server.embedded;
19 
20 import java.lang.management.ManagementFactory;
21 import java.lang.reflect.UndeclaredThrowableException;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Set;
27 import javax.management.InstanceNotFoundException;
28 import javax.management.MBeanServer;
29 import javax.management.MBeanServerInvocationHandler;
30 import javax.management.ObjectInstance;
31 import javax.management.ObjectName;
32 import org.apache.zookeeper.common.StringUtils;
33 import org.apache.zookeeper.server.ConnectionMXBean;
34 import org.apache.zookeeper.server.ZooKeeperServerBean;
35 import org.apache.zookeeper.server.quorum.LocalPeerMXBean;
36 import org.apache.zookeeper.server.quorum.QuorumBean;
37 import org.apache.zookeeper.server.quorum.QuorumMXBean;
38 import org.apache.zookeeper.server.quorum.RemotePeerMXBean;
39 
40 public final class ZookeeperServeInfo {
41 
42     private static final MBeanServer localServer = ManagementFactory.getPlatformMBeanServer();
43 
ZookeeperServeInfo()44     private ZookeeperServeInfo() {
45     }
46 
47     public static class PeerInfo {
48 
49         private final String name;
50         private final String quorumAddress;
51         private final String state;
52         private final boolean leader;
53 
PeerInfo(String name, String quorumAddress, String state, boolean leader)54         public PeerInfo(String name, String quorumAddress, String state, boolean leader) {
55             this.name = name;
56             this.quorumAddress = quorumAddress;
57             this.state = state;
58             this.leader = leader;
59         }
60 
getName()61         public String getName() {
62             return name;
63         }
64 
getQuorumAddress()65         public String getQuorumAddress() {
66             return quorumAddress;
67         }
68 
getState()69         public String getState() {
70             return state;
71         }
72 
isLeader()73         public boolean isLeader() {
74             return leader;
75         }
76 
77         @Override
toString()78         public String toString() {
79             return "PeerInfo{" + "name=" + name + ", leader=" + leader + ", quorumAddress=" + quorumAddress
80                     + ", state=" + state + '}';
81         }
82     }
83 
84     public static class ConnectionInfo {
85 
86         private final String sourceip;
87         private final String sessionid;
88         private final String lastoperation;
89         private final String lastResponseTime;
90         private final String avgLatency;
91         private final String lastLatency;
92         private final String nodes;
93 
ConnectionInfo(String sourceip, String sessionid, String lastoperation, String lastResponseTime, String avgLatency, String lastLatency, String nodes)94         public ConnectionInfo(String sourceip, String sessionid, String lastoperation, String lastResponseTime,
95                               String avgLatency, String lastLatency, String nodes) {
96             this.sourceip = sourceip;
97             this.sessionid = sessionid;
98             this.lastoperation = lastoperation;
99             this.lastResponseTime = lastResponseTime;
100             this.avgLatency = avgLatency;
101             this.lastLatency = lastLatency;
102             this.nodes = nodes;
103         }
104 
getLastLatency()105         public String getLastLatency() {
106             return lastLatency;
107         }
108 
getSourceip()109         public String getSourceip() {
110             return sourceip;
111         }
112 
getSessionid()113         public String getSessionid() {
114             return sessionid;
115         }
116 
getLastoperation()117         public String getLastoperation() {
118             return lastoperation;
119         }
120 
getLastResponseTime()121         public String getLastResponseTime() {
122             return lastResponseTime;
123         }
124 
getAvgLatency()125         public String getAvgLatency() {
126             return avgLatency;
127         }
128 
getNodes()129         public String getNodes() {
130             return nodes;
131         }
132 
133         @Override
toString()134         public String toString() {
135             return "ConnectionInfo{" + "sourceip=" + sourceip + ", sessionid=" + sessionid + ", lastoperation="
136                     + lastoperation + ", lastResponseTime=" + lastResponseTime + ", avgLatency=" + avgLatency
137                     + ", nodes=" + nodes + '}';
138         }
139     }
140 
141     public static class ServerInfo {
142 
143         private final List<ConnectionInfo> connections = new ArrayList<>();
144         private boolean leader;
145         private boolean standaloneMode;
146         public List<PeerInfo> peers = new ArrayList<>();
147 
isStandaloneMode()148         public boolean isStandaloneMode() {
149             return standaloneMode;
150         }
151 
getConnections()152         public List<ConnectionInfo> getConnections() {
153             return connections;
154         }
155 
isLeader()156         public boolean isLeader() {
157             return leader;
158         }
159 
getPeers()160         public List<PeerInfo> getPeers() {
161             return Collections.unmodifiableList(peers);
162         }
163 
addPeer(PeerInfo peer)164         public void addPeer(PeerInfo peer) {
165             peers.add(peer);
166         }
167 
168         @Override
toString()169         public String toString() {
170             return "ServerInfo{" + "connections=" + connections + ", leader=" + leader + ", standaloneMode="
171                     + standaloneMode + ", peers=" + peers + '}';
172         }
173 
174     }
175 
getStatus()176     public static ServerInfo getStatus() throws Exception {
177         return getStatus("*");
178     }
179 
getStatus(String beanName)180     public static ServerInfo getStatus(String beanName) throws Exception {
181 
182         ServerInfo info = new ServerInfo();
183         boolean standalonemode = false;
184         // org.apache.ZooKeeperService:name0=ReplicatedServer_id1,name1=replica.1,name2=Follower,name3=Connections,
185         // name4=10.168.10.119,name5=0x13e83353764005a
186         // org.apache.ZooKeeperService:name0=ReplicatedServer_id2,name1=replica.2,name2=Leader
187         if (StringUtils.isBlank(beanName)) {
188             beanName = "*";
189         }
190         ObjectName objectName = new ObjectName("org.apache.ZooKeeperService:name0=" + beanName);
191         Set<ObjectInstance> first_level_beans = localServer.queryMBeans(objectName, null);
192         if (first_level_beans.isEmpty()) {
193             throw new IllegalStateException("No ZooKeeper server found in this JVM with name " + objectName);
194         }
195         String myName = "";
196         for (ObjectInstance o : first_level_beans) {
197             if (o.getClassName().equalsIgnoreCase(ZooKeeperServerBean.class.getName())) {
198                 standalonemode = true;
199                 info.leader = true;
200                 info.addPeer(new PeerInfo("local", "local", "STANDALONE", true));
201             } else if (o.getClassName().equalsIgnoreCase(QuorumBean.class.getName())) {
202                 standalonemode = false;
203                 try {
204                     QuorumMXBean quorum = MBeanServerInvocationHandler.newProxyInstance(localServer, o.getObjectName(),
205                             QuorumMXBean.class, false);
206                     myName = quorum.getName();
207                 } catch (UndeclaredThrowableException err) {
208                     if (err.getCause() instanceof javax.management.InstanceNotFoundException) {
209                         // maybe server not yet started or already stopped ?
210                     } else {
211                         throw err;
212                     }
213                 }
214             }
215         }
216         info.standaloneMode = standalonemode;
217         if (standalonemode) {
218             Set<ObjectInstance> connectionsbeans = localServer.queryMBeans(new ObjectName(
219                     "org.apache.ZooKeeperService:name0=*,name1=Connections,name2=*,name3=*"), null);
220             for (ObjectInstance conbean : connectionsbeans) {
221                 ConnectionMXBean cc = MBeanServerInvocationHandler.
222                         newProxyInstance(localServer, conbean.getObjectName(), ConnectionMXBean.class, false);
223                 try {
224                     String nodes = "";
225                     if (cc.getEphemeralNodes() != null) {
226                         nodes = Arrays.asList(cc.getEphemeralNodes()) + "";
227                     }
228                     info.connections.add(new ConnectionInfo(cc.getSourceIP(), cc.getSessionId(), cc.getLastOperation(),
229                             cc.getLastResponseTime(), cc.getAvgLatency() + "", cc.getLastLatency() + "", nodes));
230                 } catch (Exception ex) {
231                     if (ex instanceof InstanceNotFoundException && ex.getCause() instanceof InstanceNotFoundException) {
232                         // SKIP
233                     } else {
234                         throw ex;
235                     }
236                 }
237             }
238         } else {
239             if (myName.isEmpty()) {
240                 throw new IllegalStateException(
241                         "Cannot find local JMX name for current node, in quorum mode, scanned " + first_level_beans);
242             }
243             boolean leader = false;
244             Set<ObjectInstance> replicas = localServer.queryMBeans(new ObjectName(
245                     "org.apache.ZooKeeperService:name0=" + myName + ",name1=*"), null);
246             for (ObjectInstance o : replicas) {
247                 if (o.getClassName().toLowerCase().contains("local")) {
248                     LocalPeerMXBean local = MBeanServerInvocationHandler.
249                             newProxyInstance(localServer, o.getObjectName(), LocalPeerMXBean.class, false);
250                     info.addPeer(new PeerInfo(local.getName(), local.getQuorumAddress(), local.getState() + "",
251                             local.isLeader()));
252 
253                     ObjectName asfollowername = new ObjectName(o.getObjectName() + ",name2=Follower");
254                     ObjectName asleadername = new ObjectName(o.getObjectName() + ",name2=Leader");
255                     boolean isleader = localServer.isRegistered(asleadername);
256                     Set<ObjectInstance> connectionsbeans = null;
257                     if (isleader) {
258                         leader = true;
259                         ObjectName asleaderconnections = new ObjectName(
260                                 asleadername + ",name3=Connections,name4=*,name5=*");
261                         connectionsbeans = localServer.queryMBeans(asleaderconnections, null);
262                     } else {
263                         leader = false;
264                         ObjectName asfollowernameconnections = new ObjectName(
265                                 asfollowername + ",name3=Connections,name4=*,name5=*");
266                         connectionsbeans = localServer.queryMBeans(asfollowernameconnections, null);
267                     }
268 
269                     for (ObjectInstance conbean : connectionsbeans) {
270                         ConnectionMXBean cc = MBeanServerInvocationHandler.newProxyInstance(localServer,
271                                 conbean.getObjectName(), ConnectionMXBean.class, false);
272                         try {
273                             String nodes = "";
274                             if (cc.getEphemeralNodes() != null) {
275                                 nodes = Arrays.asList(cc.getEphemeralNodes()) + "";
276                             }
277                             info.connections.add(new ConnectionInfo(cc.getSourceIP(), cc.getSessionId(), cc.
278                                     getLastOperation(), cc.getLastResponseTime(), cc.getAvgLatency() + "", cc.
279                                     getLastLatency() + "", nodes));
280                         } catch (Exception ex) {
281                             if (ex instanceof InstanceNotFoundException && ex.getCause() instanceof InstanceNotFoundException) {
282                                 // SKIP
283                             } else {
284                                 throw ex;
285                             }
286                         }
287                     }
288                 } else {
289                     RemotePeerMXBean remote = MBeanServerInvocationHandler.newProxyInstance(localServer, o.
290                             getObjectName(), RemotePeerMXBean.class, false);
291                     info.addPeer(new PeerInfo(remote.getName(), remote.getQuorumAddress(),
292                             "REMOTE", remote.isLeader()));
293                 }
294 
295             }
296             info.leader = leader;
297         }
298         return info;
299     }
300 }
301