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.inspector.manager;
19 
20 import java.io.BufferedReader;
21 import java.io.BufferedWriter;
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileReader;
25 import java.io.FileWriter;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collection;
32 import java.util.HashMap;
33 import java.util.LinkedHashMap;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Properties;
37 
38 import org.apache.zookeeper.CreateMode;
39 import org.apache.zookeeper.KeeperException;
40 import org.apache.zookeeper.WatchedEvent;
41 import org.apache.zookeeper.Watcher;
42 import org.apache.zookeeper.ZooKeeper;
43 import org.apache.zookeeper.Watcher.Event.EventType;
44 import org.apache.zookeeper.Watcher.Event.KeeperState;
45 import org.apache.zookeeper.ZooDefs.Ids;
46 import org.apache.zookeeper.ZooDefs.Perms;
47 import org.apache.zookeeper.data.ACL;
48 import org.apache.zookeeper.data.Stat;
49 import org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager;
50 import org.apache.zookeeper.inspector.encryption.DataEncryptionManager;
51 import org.apache.zookeeper.inspector.logger.LoggerFactory;
52 import org.apache.zookeeper.retry.ZooKeeperRetry;
53 
54 /**
55  * A default implementation of {@link ZooInspectorManager} for connecting to
56  * zookeeper instances
57  */
58 public class ZooInspectorManagerImpl implements ZooInspectorManager {
59     private static final String A_VERSION = "ACL Version";
60     private static final String C_TIME = "Creation Time";
61     private static final String C_VERSION = "Children Version";
62     private static final String CZXID = "Creation ID";
63     private static final String DATA_LENGTH = "Data Length";
64     private static final String EPHEMERAL_OWNER = "Ephemeral Owner";
65     private static final String M_TIME = "Last Modified Time";
66     private static final String MZXID = "Modified ID";
67     private static final String NUM_CHILDREN = "Number of Children";
68     private static final String PZXID = "Node ID";
69     private static final String VERSION = "Data Version";
70     private static final String ACL_PERMS = "Permissions";
71     private static final String ACL_SCHEME = "Scheme";
72     private static final String ACL_ID = "Id";
73     private static final String SESSION_STATE = "Session State";
74     private static final String SESSION_ID = "Session ID";
75     /**
76      * The key used for the connect string in the connection properties file
77      */
78     public static final String CONNECT_STRING = "hosts";
79     /**
80      * The key used for the session timeout in the connection properties file
81      */
82     public static final String SESSION_TIMEOUT = "timeout";
83     /**
84      * The key used for the data encryption manager in the connection properties
85      * file
86      */
87     public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager";
88     /**
89      * The key used for the authentication scheme in the connection properties file
90      */
91     public static final String AUTH_SCHEME_KEY = "authScheme";
92     /**
93      * The key used for the authentication data in the connection properties file
94      */
95     public static final String AUTH_DATA_KEY = "authData";
96 
97     private static final String DEFAULT_ENCRYPTION_MANAGER =
98         BasicDataEncryptionManager.class.getName();
99     private static final int DEFAULT_TIMEOUT = 5000;
100     private static final String DEFAULT_HOSTS = "localhost:2181";
101     private static final String DEFAULT_AUTH_SCHEME = "";
102     private static final String DEFAULT_AUTH_VALUE = "";
103 
104     private static final File defaultNodeViewersFile = new File(
105             "./src/main/resources/defaultNodeViewers.cfg");
106     private static final File defaultConnectionFile = new File(
107             "./src/main/resources/defaultConnectionSettings.cfg");
108 
109     private DataEncryptionManager encryptionManager;
110     private String connectString;
111     private int sessionTimeout;
112     private ZooKeeper zooKeeper;
113     private final Map<String, NodeWatcher> watchers = new HashMap<String, NodeWatcher>();
114     protected boolean connected = true;
115     private Properties lastConnectionProps;
116     private String defaultEncryptionManager;
117     private String defaultTimeout;
118     private String defaultHosts;
119     private String defaultAuthScheme;
120     private String defaultAuthValue;
121     private NodesCache nodesCache;
122 
123     /**
124      * @throws IOException
125      *             - thrown if the default connection settings cannot be loaded
126      *
127      */
ZooInspectorManagerImpl()128     public ZooInspectorManagerImpl() throws IOException {
129         loadDefaultConnectionFile();
130     }
131 
132     /*
133      * (non-Javadoc)
134      *
135      * @see
136      * org.apache.zookeeper.inspector.manager.ZooInspectorManager#connect(java
137      * .util.Properties)
138      */
connect(Properties connectionProps)139     public boolean connect(Properties connectionProps) {
140         try {
141             if (this.zooKeeper == null) {
142                 String connectString = connectionProps
143                         .getProperty(CONNECT_STRING);
144                 String sessionTimeout = connectionProps
145                         .getProperty(SESSION_TIMEOUT);
146                 String encryptionManager = connectionProps
147                         .getProperty(DATA_ENCRYPTION_MANAGER);
148                 String authScheme = connectionProps
149                         .getProperty(AUTH_SCHEME_KEY);
150                 String authData = connectionProps
151                         .getProperty(AUTH_DATA_KEY);
152 
153                 if (connectString == null || sessionTimeout == null) {
154                     throw new IllegalArgumentException(
155                             "Both connect string and session timeout are required.");
156                 }
157                 if (encryptionManager == null) {
158                     this.encryptionManager = new BasicDataEncryptionManager();
159                 } else {
160                     Class<?> clazz = Class.forName(encryptionManager);
161 
162                     if (Arrays.asList(clazz.getInterfaces()).contains(
163                             DataEncryptionManager.class)) {
164                         this.encryptionManager = (DataEncryptionManager) Class
165                                 .forName(encryptionManager).newInstance();
166                     } else {
167                         throw new IllegalArgumentException(
168                                 "Data encryption manager must implement DataEncryptionManager interface");
169                     }
170                 }
171                 this.connectString = connectString;
172                 this.sessionTimeout = Integer.valueOf(sessionTimeout);
173                 this.zooKeeper = new ZooKeeperRetry(connectString, Integer
174                         .valueOf(sessionTimeout), new Watcher() {
175 
176                     public void process(WatchedEvent event) {
177                         if (event.getState() == KeeperState.Expired) {
178                             connected = false;
179                         }
180                     }
181                 });
182                 if (authData != null && authData.length() > 0){
183                     this.zooKeeper.addAuthInfo(authScheme, authData.getBytes());
184                 }
185                 ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10);
186                 connected = ((ZooKeeperRetry) this.zooKeeper).testConnection();
187             }
188         } catch (Exception e) {
189             connected = false;
190             e.printStackTrace();
191         }
192         if (!connected){
193         	disconnect();
194         } else {
195             this.nodesCache = new NodesCache(zooKeeper);
196         }
197         return connected;
198     }
199 
200     /*
201      * (non-Javadoc)
202      *
203      * @see
204      * org.apache.zookeeper.inspector.manager.ZooInspectorManager#disconnect()
205      */
disconnect()206     public boolean disconnect() {
207         try {
208             if (this.zooKeeper != null) {
209                 this.zooKeeper.close();
210                 this.zooKeeper = null;
211                 connected = false;
212                 removeWatchers(this.watchers.keySet());
213                 return true;
214             }
215         } catch (Exception e) {
216             LoggerFactory.getLogger().error(
217                     "Error occurred while disconnecting from ZooKeeper server",
218                     e);
219         }
220         return false;
221     }
222 
223     /*
224      * (non-Javadoc)
225      *
226      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
227      * getChildren(java.lang.String)
228      */
getChildren(String nodePath)229     public List<String> getChildren(String nodePath) {
230         if (connected) {
231             return nodesCache.getChildren(nodePath);
232         }
233         return null;
234 
235     }
236 
237     /*
238      * (non-Javadoc)
239      *
240      * @see
241      * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getData
242      * (java.lang.String)
243      */
getData(String nodePath)244     public String getData(String nodePath) {
245         if (connected) {
246             try {
247                 if (nodePath.length() == 0) {
248                     nodePath = "/";
249                 }
250                 Stat s = zooKeeper.exists(nodePath, false);
251                 if (s != null) {
252                     return this.encryptionManager.decryptData(zooKeeper
253                             .getData(nodePath, false, s));
254                 }
255             } catch (Exception e) {
256                 LoggerFactory.getLogger().error(
257                         "Error occurred getting data for node: " + nodePath, e);
258             }
259         }
260         return null;
261     }
262 
263     /*
264      * (non-Javadoc)
265      *
266      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
267      * getNodeChild(java.lang.String, int)
268      */
getNodeChild(String nodePath, int childIndex)269     public String getNodeChild(String nodePath, int childIndex) {
270         if (connected) {
271              return this.nodesCache.getNodeChild(nodePath, childIndex);
272         }
273         return null;
274     }
275 
276     /*
277      * (non-Javadoc)
278      *
279      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
280      * getNodeIndex(java.lang.String)
281      */
getNodeIndex(String nodePath)282     public int getNodeIndex(String nodePath) {
283         if (connected) {
284             int index = nodePath.lastIndexOf("/");
285             if (index == -1
286                     || (!nodePath.equals("/") && nodePath.charAt(nodePath
287                             .length() - 1) == '/')) {
288                 throw new IllegalArgumentException("Invalid node path: "
289                         + nodePath);
290             }
291             String parentPath = nodePath.substring(0, index);
292             String child = nodePath.substring(index + 1);
293             if (parentPath != null && parentPath.length() > 0) {
294                 List<String> children = this.nodesCache.getChildren(parentPath);
295                 if (children != null) {
296                     return children.indexOf(child);
297                 }
298             }
299         }
300         return -1;
301     }
302 
303     /*
304      * (non-Javadoc)
305      *
306      * @see
307      * org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getACLs
308      * (java.lang.String)
309      */
getACLs(String nodePath)310     public List<Map<String, String>> getACLs(String nodePath) {
311         List<Map<String, String>> returnACLs = new ArrayList<Map<String, String>>();
312         if (connected) {
313             try {
314                 if (nodePath.length() == 0) {
315                     nodePath = "/";
316                 }
317                 Stat s = zooKeeper.exists(nodePath, false);
318                 if (s != null) {
319                     List<ACL> acls = zooKeeper.getACL(nodePath, s);
320                     for (ACL acl : acls) {
321                         Map<String, String> aclMap = new LinkedHashMap<String, String>();
322                         aclMap.put(ACL_SCHEME, acl.getId().getScheme());
323                         aclMap.put(ACL_ID, acl.getId().getId());
324                         StringBuilder sb = new StringBuilder();
325                         int perms = acl.getPerms();
326                         boolean addedPerm = false;
327                         if ((perms & Perms.READ) == Perms.READ) {
328                             sb.append("Read");
329                             addedPerm = true;
330                         }
331                         if (addedPerm) {
332                             sb.append(", ");
333                         }
334                         if ((perms & Perms.WRITE) == Perms.WRITE) {
335                             sb.append("Write");
336                             addedPerm = true;
337                         }
338                         if (addedPerm) {
339                             sb.append(", ");
340                         }
341                         if ((perms & Perms.CREATE) == Perms.CREATE) {
342                             sb.append("Create");
343                             addedPerm = true;
344                         }
345                         if (addedPerm) {
346                             sb.append(", ");
347                         }
348                         if ((perms & Perms.DELETE) == Perms.DELETE) {
349                             sb.append("Delete");
350                             addedPerm = true;
351                         }
352                         if (addedPerm) {
353                             sb.append(", ");
354                         }
355                         if ((perms & Perms.ADMIN) == Perms.ADMIN) {
356                             sb.append("Admin");
357                             addedPerm = true;
358                         }
359                         aclMap.put(ACL_PERMS, sb.toString());
360                         returnACLs.add(aclMap);
361                     }
362                 }
363             } catch (InterruptedException e) {
364                 LoggerFactory.getLogger().error(
365                         "Error occurred retrieving ACLs of node: " + nodePath,
366                         e);
367             } catch (KeeperException e) {
368                 LoggerFactory.getLogger().error(
369                         "Error occurred retrieving ACLs of node: " + nodePath,
370                         e);
371             }
372         }
373         return returnACLs;
374     }
375 
376     /*
377      * (non-Javadoc)
378      *
379      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
380      * getNodeMeta(java.lang.String)
381      */
getNodeMeta(String nodePath)382     public Map<String, String> getNodeMeta(String nodePath) {
383         Map<String, String> nodeMeta = new LinkedHashMap<String, String>();
384         if (connected) {
385             try {
386                 if (nodePath.length() == 0) {
387                     nodePath = "/";
388                 }
389                 Stat s = zooKeeper.exists(nodePath, false);
390                 if (s != null) {
391                     nodeMeta.put(A_VERSION, String.valueOf(s.getAversion()));
392                     nodeMeta.put(C_TIME, String.valueOf(s.getCtime()));
393                     nodeMeta.put(C_VERSION, String.valueOf(s.getCversion()));
394                     nodeMeta.put(CZXID, String.valueOf(s.getCzxid()));
395                     nodeMeta
396                             .put(DATA_LENGTH, String.valueOf(s.getDataLength()));
397                     nodeMeta.put(EPHEMERAL_OWNER, String.valueOf(s
398                             .getEphemeralOwner()));
399                     nodeMeta.put(M_TIME, String.valueOf(s.getMtime()));
400                     nodeMeta.put(MZXID, String.valueOf(s.getMzxid()));
401                     nodeMeta.put(NUM_CHILDREN, String.valueOf(s
402                             .getNumChildren()));
403                     nodeMeta.put(PZXID, String.valueOf(s.getPzxid()));
404                     nodeMeta.put(VERSION, String.valueOf(s.getVersion()));
405                 }
406             } catch (Exception e) {
407                 LoggerFactory.getLogger().error(
408                         "Error occurred retrieving meta data for node: "
409                                 + nodePath, e);
410             }
411         }
412         return nodeMeta;
413     }
414 
415     /*
416      * (non-Javadoc)
417      *
418      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
419      * getNumChildren(java.lang.String)
420      */
getNumChildren(String nodePath)421     public int getNumChildren(String nodePath) {
422         if (connected) {
423             try {
424                 Stat s = zooKeeper.exists(nodePath, false);
425                 if (s != null) {
426                     return s.getNumChildren();
427                 }
428             } catch (Exception e) {
429                 LoggerFactory.getLogger().error(
430                         "Error occurred getting the number of children of node: "
431                                 + nodePath, e);
432             }
433         }
434         return -1;
435     }
436 
437     /*
438      * (non-Javadoc)
439      *
440      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
441      * hasChildren(java.lang.String)
442      */
hasChildren(String nodePath)443     public boolean hasChildren(String nodePath) {
444         return getNumChildren(nodePath) > 0;
445     }
446 
447     /*
448      * (non-Javadoc)
449      *
450      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
451      * isAllowsChildren(java.lang.String)
452      */
isAllowsChildren(String nodePath)453     public boolean isAllowsChildren(String nodePath) {
454         if (connected) {
455             try {
456                 Stat s = zooKeeper.exists(nodePath, false);
457                 if (s != null) {
458                     return s.getEphemeralOwner() == 0;
459                 }
460             } catch (Exception e) {
461                 LoggerFactory.getLogger().error(
462                         "Error occurred determining whether node is allowed children: "
463                                 + nodePath, e);
464             }
465         }
466         return false;
467     }
468 
469     /*
470      * (non-Javadoc)
471      *
472      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
473      * getSessionMeta()
474      */
getSessionMeta()475     public Map<String, String> getSessionMeta() {
476         Map<String, String> sessionMeta = new LinkedHashMap<String, String>();
477         try {
478             if (zooKeeper != null) {
479 
480                 sessionMeta.put(SESSION_ID, String.valueOf(zooKeeper
481                         .getSessionId()));
482                 sessionMeta.put(SESSION_STATE, String.valueOf(zooKeeper
483                         .getState().toString()));
484                 sessionMeta.put(CONNECT_STRING, this.connectString);
485                 sessionMeta.put(SESSION_TIMEOUT, String
486                         .valueOf(this.sessionTimeout));
487             }
488         } catch (Exception e) {
489             LoggerFactory.getLogger().error(
490                     "Error occurred retrieving session meta data.", e);
491         }
492         return sessionMeta;
493     }
494 
495     /*
496      * (non-Javadoc)
497      *
498      * @see
499      * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#createNode
500      * (java.lang.String, java.lang.String)
501      */
createNode(String parent, String nodeName)502     public boolean createNode(String parent, String nodeName) {
503         if (connected) {
504             try {
505                 String[] nodeElements = nodeName.split("/");
506                 for (String nodeElement : nodeElements) {
507                     String node = parent + "/" + nodeElement;
508                     Stat s = zooKeeper.exists(node, false);
509                     if (s == null) {
510                         zooKeeper.create(node, this.encryptionManager
511                                 .encryptData(null), Ids.OPEN_ACL_UNSAFE,
512                                 CreateMode.PERSISTENT);
513                         parent = node;
514                     }
515                 }
516                 return true;
517             } catch (Exception e) {
518                 LoggerFactory.getLogger().error(
519                         "Error occurred creating node: " + parent + "/"
520                                 + nodeName, e);
521             }
522         }
523         return false;
524     }
525 
526     /*
527      * (non-Javadoc)
528      *
529      * @see
530      * org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#deleteNode
531      * (java.lang.String)
532      */
deleteNode(String nodePath)533     public boolean deleteNode(String nodePath) {
534         if (connected) {
535             try {
536                 Stat s = zooKeeper.exists(nodePath, false);
537                 if (s != null) {
538                     List<String> children = zooKeeper.getChildren(nodePath,
539                             false);
540                     for (String child : children) {
541                         String node = nodePath + "/" + child;
542                         deleteNode(node);
543                     }
544                     zooKeeper.delete(nodePath, -1);
545                 }
546                 return true;
547             } catch (Exception e) {
548                 LoggerFactory.getLogger().error(
549                         "Error occurred deleting node: " + nodePath, e);
550             }
551         }
552         return false;
553     }
554 
555     /*
556      * (non-Javadoc)
557      *
558      * @see
559      * org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager#setData
560      * (java.lang.String, java.lang.String)
561      */
setData(String nodePath, String data)562     public boolean setData(String nodePath, String data) {
563         if (connected) {
564             try {
565                 zooKeeper.setData(nodePath, this.encryptionManager
566                         .encryptData(data), -1);
567                 return true;
568             } catch (Exception e) {
569                 LoggerFactory.getLogger().error(
570                         "Error occurred setting data for node: " + nodePath, e);
571             }
572         }
573         return false;
574     }
575 
576     /*
577      * (non-Javadoc)
578      *
579      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
580      * getConnectionPropertiesTemplate()
581      */
getConnectionPropertiesTemplate()582     public Pair<Map<String, List<String>>, Map<String, String>> getConnectionPropertiesTemplate() {
583         Map<String, List<String>> template = new LinkedHashMap<String, List<String>>();
584         template.put(CONNECT_STRING, Arrays
585                 .asList(new String[] { defaultHosts }));
586         template.put(SESSION_TIMEOUT, Arrays
587                 .asList(new String[] { defaultTimeout }));
588         template.put(DATA_ENCRYPTION_MANAGER, Arrays
589                 .asList(new String[] { defaultEncryptionManager }));
590         template.put(AUTH_SCHEME_KEY, Arrays
591                 .asList(new String[] { defaultAuthScheme }));
592         template.put(AUTH_DATA_KEY, Arrays
593                 .asList(new String[] { defaultAuthValue }));
594         Map<String, String> labels = new LinkedHashMap<String, String>();
595         labels.put(CONNECT_STRING, "Connect String");
596         labels.put(SESSION_TIMEOUT, "Session Timeout");
597         labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager");
598         labels.put(AUTH_SCHEME_KEY, "Authentication Scheme");
599         labels.put(AUTH_DATA_KEY, "Authentication Data");
600         return new Pair<Map<String, List<String>>, Map<String, String>>(
601                 template, labels);
602     }
603 
604     /*
605      * (non-Javadoc)
606      *
607      * @see
608      * org.apache.zookeeper.inspector.manager.ZooInspectorManager#addWatchers
609      * (java.util.Collection,
610      * org.apache.zookeeper.inspector.manager.NodeListener)
611      */
addWatchers(Collection<String> selectedNodes, NodeListener nodeListener)612     public void addWatchers(Collection<String> selectedNodes,
613             NodeListener nodeListener) {
614         // add watcher for each node and add node to collection of
615         // watched nodes
616         if (connected) {
617             for (String node : selectedNodes) {
618                 if (!watchers.containsKey(node)) {
619                     try {
620                         watchers.put(node, new NodeWatcher(node, nodeListener,
621                                 zooKeeper));
622                     } catch (Exception e) {
623                         LoggerFactory.getLogger().error(
624                                 "Error occurred adding node watcher for node: "
625                                         + node, e);
626                     }
627                 }
628             }
629         }
630     }
631 
632     /*
633      * (non-Javadoc)
634      *
635      * @see
636      * org.apache.zookeeper.inspector.manager.ZooInspectorManager#removeWatchers
637      * (java.util.Collection)
638      */
removeWatchers(Collection<String> selectedNodes)639     public void removeWatchers(Collection<String> selectedNodes) {
640         // remove watcher for each node and remove node from
641         // collection of watched nodes
642         if (connected) {
643             for (String node : selectedNodes) {
644                 if (watchers.containsKey(node)) {
645                     NodeWatcher watcher = watchers.remove(node);
646                     if (watcher != null) {
647                         watcher.stop();
648                     }
649                 }
650             }
651         }
652     }
653 
654     /**
655      * A Watcher which will re-add itself every time an event is fired
656      *
657      */
658     public class NodeWatcher implements Watcher {
659 
660         private final String nodePath;
661         private final NodeListener nodeListener;
662         private final ZooKeeper zookeeper;
663         private boolean closed = false;
664 
665         /**
666          * @param nodePath
667          *            - the path to the node to watch
668          * @param nodeListener
669          *            the {@link NodeListener} for this node
670          * @param zookeeper
671          *            - a {@link ZooKeeper} to use to access zookeeper
672          * @throws InterruptedException
673          * @throws KeeperException
674          */
NodeWatcher(String nodePath, NodeListener nodeListener, ZooKeeper zookeeper)675         public NodeWatcher(String nodePath, NodeListener nodeListener,
676                 ZooKeeper zookeeper) throws KeeperException,
677                 InterruptedException {
678             this.nodePath = nodePath;
679             this.nodeListener = nodeListener;
680             this.zookeeper = zookeeper;
681             Stat s = zooKeeper.exists(nodePath, this);
682             if (s != null) {
683                 zookeeper.getChildren(nodePath, this);
684             }
685         }
686 
process(WatchedEvent event)687         public void process(WatchedEvent event) {
688             if (!closed) {
689                 try {
690                     if (event.getType() != EventType.NodeDeleted) {
691 
692                         Stat s = zooKeeper.exists(nodePath, this);
693                         if (s != null) {
694                             zookeeper.getChildren(nodePath, this);
695                         }
696                     }
697                 } catch (Exception e) {
698                     LoggerFactory.getLogger().error(
699                             "Error occurred re-adding node watcherfor node "
700                                     + nodePath, e);
701                 }
702                 nodeListener.processEvent(event.getPath(), event.getType()
703                         .name(), null);
704             }
705         }
706 
707         /**
708 		 *
709 		 */
stop()710         public void stop() {
711             this.closed = true;
712         }
713 
714     }
715 
716     /*
717      * (non-Javadoc)
718      *
719      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
720      * loadNodeViewersFile(java.io.File)
721      */
loadNodeViewersFile(File selectedFile)722     public List<String> loadNodeViewersFile(File selectedFile)
723             throws IOException {
724         List<String> result = new ArrayList<String>();
725 
726         try(BufferedReader reader = getReaderForFile(selectedFile)) {
727             if(reader == null) {
728                 return result;
729             }
730 
731             String line = "";
732             while (line != null) {
733                 line = reader.readLine();
734                 if(line != null) {
735                     line = line.trim();
736                     if (!line.isEmpty() && !line.startsWith("#")) {
737                         result.add(line);
738                     }
739                 }
740             }
741         }
742 
743         return result;
744     }
745 
loadDefaultConnectionFile()746     private void loadDefaultConnectionFile() throws IOException {
747         Properties props = new Properties();
748 
749         try(BufferedReader reader = getReaderForFile(defaultConnectionFile)) {
750             //If reader is null, it's OK.  Default values will get set below.
751             if(reader != null) {
752                 props.load(reader);
753             }
754         }
755 
756         defaultEncryptionManager = props.getProperty(DATA_ENCRYPTION_MANAGER) == null ?
757             DEFAULT_ENCRYPTION_MANAGER : props.getProperty(DATA_ENCRYPTION_MANAGER);
758         defaultTimeout = props.getProperty(SESSION_TIMEOUT) == null ?
759             Integer.toString(DEFAULT_TIMEOUT) : props.getProperty(SESSION_TIMEOUT);
760         defaultHosts = props.getProperty(CONNECT_STRING) == null ?
761             DEFAULT_HOSTS : props.getProperty(CONNECT_STRING);
762         defaultAuthScheme = props.getProperty(AUTH_SCHEME_KEY) == null ?
763             DEFAULT_AUTH_SCHEME : props.getProperty(AUTH_SCHEME_KEY);
764         defaultAuthValue = props.getProperty(AUTH_DATA_KEY) == null ?
765             DEFAULT_AUTH_VALUE : props.getProperty(AUTH_DATA_KEY);
766     }
767 
768     /*
769      * (non-Javadoc)
770      *
771      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
772      * saveDefaultConnectionFile(java.util.Properties)
773      */
saveDefaultConnectionFile(Properties props)774     public void saveDefaultConnectionFile(Properties props) throws IOException {
775         File defaultDir = defaultConnectionFile.getParentFile();
776         if (!defaultDir.exists()) {
777             if (!defaultDir.mkdirs()) {
778                 throw new IOException(
779                         "Failed to create configuration directory: "
780                                 + defaultDir.getAbsolutePath());
781             }
782         }
783         if (!defaultConnectionFile.exists()) {
784             if (!defaultConnectionFile.createNewFile()) {
785                 throw new IOException(
786                         "Failed to create default connection file: "
787                                 + defaultConnectionFile.getAbsolutePath());
788             }
789         }
790         FileWriter writer = new FileWriter(defaultConnectionFile);
791         try {
792             props.store(writer, "Default connection for ZooInspector");
793         } finally {
794             writer.close();
795         }
796     }
797 
798     /*
799      * (non-Javadoc)
800      *
801      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
802      * saveNodeViewersFile(java.io.File, java.util.List)
803      */
saveNodeViewersFile(File selectedFile, List<String> nodeViewersClassNames)804     public void saveNodeViewersFile(File selectedFile,
805             List<String> nodeViewersClassNames) throws IOException {
806         if (!selectedFile.exists()) {
807             if (!selectedFile.createNewFile()) {
808                 throw new IOException(
809                         "Failed to create node viewers configuration file: "
810                                 + selectedFile.getAbsolutePath());
811             }
812         }
813         FileWriter writer = new FileWriter(selectedFile);
814         try {
815             BufferedWriter buff = new BufferedWriter(writer);
816             try {
817                 for (String nodeViewersClassName : nodeViewersClassNames) {
818                     buff.append(nodeViewersClassName);
819                     buff.append("\n");
820                 }
821             } finally {
822                 buff.flush();
823                 buff.close();
824             }
825         } finally {
826             writer.close();
827         }
828     }
829 
830     /*
831      * (non-Javadoc)
832      *
833      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
834      * setDefaultNodeViewerConfiguration(java.io.File, java.util.List)
835      */
setDefaultNodeViewerConfiguration( List<String> nodeViewersClassNames)836     public void setDefaultNodeViewerConfiguration(
837             List<String> nodeViewersClassNames) throws IOException {
838         File defaultDir = defaultNodeViewersFile.getParentFile();
839         if (!defaultDir.exists()) {
840             if (!defaultDir.mkdirs()) {
841                 throw new IOException(
842                         "Failed to create configuration directory: "
843                                 + defaultDir.getAbsolutePath());
844             }
845         }
846         saveNodeViewersFile(defaultNodeViewersFile, nodeViewersClassNames);
847     }
848 
getDefaultNodeViewerConfiguration()849     public List<String> getDefaultNodeViewerConfiguration() throws IOException {
850         List<String> defaultNodeViewers = loadNodeViewersFile(defaultNodeViewersFile);
851         if (defaultNodeViewers.isEmpty()) {
852             LoggerFactory.getLogger().warn("List of default node viewers is empty");
853         }
854         return defaultNodeViewers;
855     }
856 
857     /*
858      * (non-Javadoc)
859      *
860      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
861      * getLastConnectionProps()
862      */
getLastConnectionProps()863     public Properties getLastConnectionProps() {
864         return this.lastConnectionProps;
865     }
866 
867     /*
868      * (non-Javadoc)
869      *
870      * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
871      * setLastConnectionProps(java.util.Properties)
872      */
setLastConnectionProps(Properties connectionProps)873     public void setLastConnectionProps(Properties connectionProps) {
874         this.lastConnectionProps = connectionProps;
875     }
876 
getReaderForFile(File file)877     private static BufferedReader getReaderForFile(File file) {
878         //check the filesystem first
879         if (file.exists()) {
880             try {
881                 return new BufferedReader(new FileReader(file));
882             } catch (FileNotFoundException e) {
883                 return null;
884             }
885         }
886 
887         //fall back to checking the CLASSPATH with only the filename
888         //(for cases where the file exists in src/main/resources)
889         InputStream classpathStream = ZooInspectorManagerImpl.class.getClassLoader()
890             .getResourceAsStream(file.getName());
891         if (classpathStream != null) {
892             return new BufferedReader(new InputStreamReader(classpathStream));
893         }
894 
895         //couldn't find the file anywhere
896         return null;
897     }
898 }
899