com.zookeeper.web.inspector.manager.ZooInspectorManagerImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.zookeeper.web.inspector.manager.ZooInspectorManagerImpl.java

Source

/**
 * 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 com.zookeeper.web.inspector.manager;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.KeeperException.NoNodeException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.Watcher.Event.EventType;
import org.apache.zookeeper.Watcher.Event.KeeperState;
import org.apache.zookeeper.ZooDefs.Ids;
import org.apache.zookeeper.ZooDefs.Perms;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.ZooKeeper.States;
import org.apache.zookeeper.data.ACL;
import org.apache.zookeeper.data.Stat;
import org.springframework.stereotype.Component;

import com.zookeeper.retry.ZooKeeperRetry;
import com.zookeeper.web.inspector.encryption.BasicDataEncryptionManager;
import com.zookeeper.web.inspector.encryption.DataEncryptionManager;
import com.zookeeper.web.inspector.logger.LoggerFactory;
import com.zookeeper.web.inspector.manager.ZooInspectorManagerCache.Item;

/**
 * A default implementation of {@link ZooInspectorManager} for connecting to zookeeper
 * instances
 */
@Component
public class ZooInspectorManagerImpl implements ZooInspectorManager {
    private static final String A_VERSION = "ACL Version";
    private static final String C_TIME = "Creation Time";
    private static final String C_VERSION = "Children Version";
    private static final String CZXID = "Creation ID";
    private static final String DATA_LENGTH = "Data Length";
    private static final String EPHEMERAL_OWNER = "Ephemeral Owner";
    private static final String M_TIME = "Last Modified Time";
    private static final String MZXID = "Modified ID";
    private static final String NUM_CHILDREN = "Number of Children";
    private static final String PZXID = "Node ID";
    private static final String VERSION = "Data Version";
    private static final String ACL_PERMS = "Permissions";
    private static final String ACL_SCHEME = "Scheme";
    private static final String ACL_ID = "Id";
    private static final String SESSION_STATE = "Session State";
    private static final String SESSION_ID = "Session ID";
    /**
     * The key used for the connect string in the connection properties file
     */
    public static final String CONNECT_STRING = "hosts";
    /**
     * The key used for the session timeout in the connection properties file
     */
    public static final String SESSION_TIMEOUT = "timeout";
    /**
     * The key used for the data encryption manager in the connection properties file
     */
    public static final String DATA_ENCRYPTION_MANAGER = "encryptionManager";

    //  private static final String homeDir = System.getProperty("user.home");
    //  private static final File defaultNodeViewersFile =
    //      new File(homeDir + "/.zooinspector/defaultNodeVeiwers.cfg");
    //  private static final File defaultConnectionFile =
    //      new File(homeDir + "/.zooinspector/defaultConnectionSettings.cfg");

    //  private static final File defaultNodeViewersFile =
    //      new File("./config/defaultNodeVeiwers.cfg");
    //  private static final File defaultConnectionFile =
    //      new File("./config/defaultConnectionSettings.cfg");

    private DataEncryptionManager encryptionManager;
    private String connectString;
    private int sessionTimeout;
    private ZooKeeper zooKeeper;
    // private
    final Map<String, NodeWatcher> watchers = new HashMap<String, NodeWatcher>();
    protected boolean connected = true;
    //  private Properties lastConnectionProps;
    //  private String defaultEncryptionManager;
    //  private String defaultTimeout;
    //  private String defaultHosts;
    //  private List<String> defaultHostsList;
    //  private final int defaultHostsListSize = 10;

    // zk cache that updates when:
    // - refresh button is clicked
    // - treeExpansion event fired
    // - selectPth event fired
    ZooInspectorManagerCache cache;

    /**
     * @throws IOException
     *           - thrown if the default connection settings cannot be loaded
     *
     */
    public ZooInspectorManagerImpl() throws IOException {
        //    loadDefaultConnectionFile();s
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager#connect(java
     * .util.Properties)
     */
    @Override
    public boolean connect(Properties connectionProps) {
        connected = false;
        try {
            if (this.zooKeeper == null) {
                String connectString = connectionProps.getProperty(CONNECT_STRING);
                String sessionTimeout = connectionProps.getProperty(SESSION_TIMEOUT);
                String encryptionManager = connectionProps.getProperty(DATA_ENCRYPTION_MANAGER);
                if (connectString == null || sessionTimeout == null) {
                    throw new IllegalArgumentException("Both connect string and session timeout are required.");
                }
                if (encryptionManager == null) {
                    this.encryptionManager = new BasicDataEncryptionManager();
                } else {
                    Class<?> clazz = Class.forName(encryptionManager);

                    if (Arrays.asList(clazz.getInterfaces()).contains(DataEncryptionManager.class)) {
                        this.encryptionManager = (DataEncryptionManager) Class.forName(encryptionManager)
                                .newInstance();
                    } else {
                        throw new IllegalArgumentException(
                                "Data encryption manager must implement DataEncryptionManager interface");
                    }
                }
                this.connectString = connectString;
                this.sessionTimeout = Integer.valueOf(sessionTimeout);

                //        long start = System.currentTimeMillis();
                //        System.out.println("[START] connecting...");
                this.zooKeeper = new ZooKeeperRetry(connectString, Integer.valueOf(sessionTimeout), new Watcher() {

                    @Override
                    public void process(WatchedEvent event) {
                        if (event.getState() == KeeperState.Expired) {
                            connected = false;
                        }
                    }
                });
                ((ZooKeeperRetry) this.zooKeeper).setRetryLimit(10);
                //        System.out.println("[START] connected took: " + (System.currentTimeMillis() - start));

                connected = ((ZooKeeperRetry) this.zooKeeper).testConnection();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        // connected = false;

        // do initial cache refresh on all childs of "/"
        if (connected == true) {
            cache = new ZooInspectorManagerCache(this);
            try {
                cache.refresh(Arrays.asList("/"), 1);
            } catch (KeeperException e) {
                // TODO Auto-generated catch block
                disconnect();
                e.printStackTrace();
            }
        } else {
            disconnect();
        }

        return connected;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager#disconnect()
     */
    @Override
    public boolean disconnect() {
        try {
            if (this.zooKeeper != null) {
                this.zooKeeper.close();
                this.zooKeeper = null;
                connected = false;
                removeWatchers(this.watchers.keySet());
                return true;
            }
        } catch (Exception e) {
            LoggerFactory.getLogger().error("Error occurred while disconnecting from ZooKeeper server", e);
        }
        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * getChildren(java.lang.String)
     */
    @Override
    public List<String> getChildren(String nodePath) {
        // System.out.println("ZooInspectorManagerImpl.getChildren(), nodePath: " + nodePath);
        if (connected) {
            // try {
            //
            // return zooKeeper.getChildren(nodePath, false);
            // } catch (Exception e) {
            // LoggerFactory.getLogger().error(
            // "Error occurred retrieving children of node: "
            // + nodePath, e);
            // }
            return cache.getChildren(nodePath);
        }
        return null;

    }

    Item getChildrenAndStat(String nodePath) throws KeeperException {
        // System.out.println("getChildrenAndStat(), path: " + nodePath);
        if (zooKeeper.getState() != States.CONNECTED) {
            throw KeeperException.create(KeeperException.Code.CONNECTIONLOSS, nodePath);
        }

        try {
            Stat stat = new Stat();
            List<String> childs = zooKeeper.getChildren(nodePath, false, stat);
            return new Item(childs, stat);
        } catch (NoNodeException e) {
            // OK to return null
        } catch (KeeperException e) {
            throw e;
        } catch (Exception e) {
            LoggerFactory.getLogger().error("Error occurred retrieving children of node: " + nodePath, e);
        }
        return null;
    }

    // experiments show that multi-thread sync-read is faster than single-thread async-read
    // let's try it
    ExecutorService service = Executors.newFixedThreadPool(40);

    Map<String, Item> getChildren(List<String> paths) {
        int n = paths.size();
        if (n > 0) {
            // final List<Item> ret = new ArrayList<Item>();
            final Map<String, Item> ret = new ConcurrentHashMap<String, ZooInspectorManagerCache.Item>();
            final CountDownLatch cntDown = new CountDownLatch(n);

            for (final String path : paths) {
                service.submit(new Callable<String>() {

                    @Override
                    public String call() throws Exception {
                        Stat stat = null;
                        List<String> childs = null;
                        try {
                            stat = new Stat();
                            childs = zooKeeper.getChildren(path, false, stat);
                        } catch (Exception e) {
                            // System.out.println("exception: " + e);
                        } finally {
                            // ret.add(new Item(childs, stat));
                            ret.put(path, new Item(childs, stat));
                            cntDown.countDown();
                        }
                        return null;
                    }
                });
            }

            try {
                cntDown.await();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return ret;
        }

        return Collections.emptyMap();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getData
     * (java.lang.String)
     */
    @Override
    public String getData(String nodePath) {
        if (connected) {
            try {
                if (nodePath.length() == 0) {
                    nodePath = "/";
                }
                Stat s = zooKeeper.exists(nodePath, false);
                if (s != null) {
                    return this.encryptionManager.decryptData(zooKeeper.getData(nodePath, false, s));
                }
            } catch (Exception e) {
                LoggerFactory.getLogger().error("Error occurred getting data for node: " + nodePath, e);
            }
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * getNodeChild(java.lang.String, int)
     */
    @Override
    public String getNodeChild(String nodePath, int childIndex) {
        long start = System.currentTimeMillis();
        if (connected) {
            // try {
            // Stat s = zooKeeper.exists(nodePath, false);
            // if (s != null) {
            //
            // String string = this.zooKeeper.getChildren(nodePath, false).get(
            // childIndex);
            // long end = System.currentTimeMillis();
            // System.out.println("getNodeChild(), path: " + nodePath + ", childIndex: " +
            // childIndex + ", " + (end - start));
            // return string;
            // }
            // } catch (Exception e) {
            // LoggerFactory.getLogger().error(
            // "Error occurred retrieving child " + childIndex
            // + " of node: " + nodePath, e);
            // }
            String child = cache.getNodeChild(nodePath, childIndex);
            long end = System.currentTimeMillis();
            // System.out.println("getNodeChild(), path: " + nodePath + ", childIndex: " +
            // childIndex + ", " + (end - start));
            return child;
        }
        return null;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * getNodeIndex(java.lang.String)
     */
    @Override
    public int getNodeIndex(String nodePath) {
        if (connected) {
            int index = nodePath.lastIndexOf("/");
            if (index == -1 || (!nodePath.equals("/") && nodePath.charAt(nodePath.length() - 1) == '/')) {
                throw new IllegalArgumentException("Invalid node path: " + nodePath);
            }
            String parentPath = nodePath.substring(0, index);
            String child = nodePath.substring(index + 1);
            if (parentPath != null && parentPath.length() > 0) {
                List<String> children = this.getChildren(parentPath);
                if (children != null) {
                    return children.indexOf(child);
                }
            }
        }
        return -1;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#getACLs
     * (java.lang.String)
     */
    @Override
    public List<Map<String, String>> getACLs(String nodePath) {
        List<Map<String, String>> returnACLs = new ArrayList<Map<String, String>>();
        if (connected) {
            try {
                if (nodePath.length() == 0) {
                    nodePath = "/";
                }
                Stat s = zooKeeper.exists(nodePath, false);
                if (s != null) {
                    List<ACL> acls = zooKeeper.getACL(nodePath, s);
                    for (ACL acl : acls) {
                        Map<String, String> aclMap = new LinkedHashMap<String, String>();
                        aclMap.put(ACL_SCHEME, acl.getId().getScheme());
                        aclMap.put(ACL_ID, acl.getId().getId());
                        StringBuilder sb = new StringBuilder();
                        int perms = acl.getPerms();
                        boolean addedPerm = false;
                        if ((perms & Perms.READ) == Perms.READ) {
                            sb.append("Read");
                            addedPerm = true;
                        }
                        if (addedPerm) {
                            sb.append(", ");
                        }
                        if ((perms & Perms.WRITE) == Perms.WRITE) {
                            sb.append("Write");
                            addedPerm = true;
                        }
                        if (addedPerm) {
                            sb.append(", ");
                        }
                        if ((perms & Perms.CREATE) == Perms.CREATE) {
                            sb.append("Create");
                            addedPerm = true;
                        }
                        if (addedPerm) {
                            sb.append(", ");
                        }
                        if ((perms & Perms.DELETE) == Perms.DELETE) {
                            sb.append("Delete");
                            addedPerm = true;
                        }
                        if (addedPerm) {
                            sb.append(", ");
                        }
                        if ((perms & Perms.ADMIN) == Perms.ADMIN) {
                            sb.append("Admin");
                            addedPerm = true;
                        }
                        aclMap.put(ACL_PERMS, sb.toString());
                        returnACLs.add(aclMap);
                    }
                }
            } catch (InterruptedException e) {
                LoggerFactory.getLogger().error("Error occurred retrieving ACLs of node: " + nodePath, e);
            } catch (KeeperException e) {
                LoggerFactory.getLogger().error("Error occurred retrieving ACLs of node: " + nodePath, e);
            }
        }
        return returnACLs;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * getNodeMeta(java.lang.String)
     */
    @Override
    public Map<String, String> getNodeMeta(String nodePath) {
        Map<String, String> nodeMeta = new LinkedHashMap<String, String>();
        if (connected) {
            try {
                if (nodePath.length() == 0) {
                    nodePath = "/";
                }
                Stat s = zooKeeper.exists(nodePath, false);
                if (s != null) {
                    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS z");
                    nodeMeta.put(A_VERSION, String.valueOf(s.getAversion()));
                    // nodeMeta.put(C_TIME, String.valueOf(s.getCtime()));
                    nodeMeta.put(C_TIME, format.format(new Date(s.getCtime())));
                    nodeMeta.put(C_VERSION, String.valueOf(s.getCversion()));
                    nodeMeta.put(CZXID, "0x" + Long.toHexString(s.getCzxid()));
                    nodeMeta.put(DATA_LENGTH, String.valueOf(s.getDataLength()));
                    nodeMeta.put(EPHEMERAL_OWNER, "0x" + Long.toHexString(s.getEphemeralOwner()));
                    // nodeMeta.put(M_TIME, String.valueOf(s.getMtime()));
                    nodeMeta.put(M_TIME, format.format(new Date(s.getMtime())));
                    nodeMeta.put(MZXID, "0x" + Long.toHexString(s.getMzxid()));
                    nodeMeta.put(NUM_CHILDREN, String.valueOf(s.getNumChildren()));
                    nodeMeta.put(PZXID, "0x" + Long.toHexString(s.getPzxid()));
                    nodeMeta.put(VERSION, String.valueOf(s.getVersion()));
                }
            } catch (Exception e) {
                LoggerFactory.getLogger().error("Error occurred retrieving meta data for node: " + nodePath, e);
            }
        }
        return nodeMeta;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * getNumChildren(java.lang.String)
     */
    @Override
    public int getNumChildren(String nodePath) {
        long start = System.currentTimeMillis();
        if (connected) {
            // try {
            // Stat s = zooKeeper.exists(nodePath, false);
            // if (s != null) {
            // int numChildren = s.getNumChildren();
            // long end = System.currentTimeMillis();
            // System.out.println("getNumbChilds(), nodePath: " + nodePath + ", " +
            // (end-start));
            // return numChildren;
            // }
            // } catch (Exception e) {
            // LoggerFactory.getLogger().error(
            // "Error occurred getting the number of children of node: "
            // + nodePath, e);
            // }
            int numChildren = cache.getNumChildren(nodePath);
            long end = System.currentTimeMillis();
            // System.out.println("getNumbChilds(), nodePath: " + nodePath + ", " +
            // (end-start));
            return numChildren;
        }
        return -1;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * hasChildren(java.lang.String)
     */
    @Override
    public boolean hasChildren(String nodePath) {
        return getNumChildren(nodePath) > 0;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * isAllowsChildren(java.lang.String)
     */
    @Override
    public boolean isAllowsChildren(String nodePath) {
        if (connected) {
            try {
                Stat s = zooKeeper.exists(nodePath, false);
                if (s != null) {
                    return s.getEphemeralOwner() == 0;
                }
            } catch (Exception e) {
                LoggerFactory.getLogger()
                        .error("Error occurred determining whether node is allowed children: " + nodePath, e);
            }
        }
        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorReadOnlyManager#
     * getSessionMeta()
     */
    @Override
    public Map<String, String> getSessionMeta() {
        Map<String, String> sessionMeta = new LinkedHashMap<String, String>();
        try {
            if (zooKeeper != null) {

                sessionMeta.put(SESSION_ID, String.valueOf(zooKeeper.getSessionId()));
                sessionMeta.put(SESSION_STATE, String.valueOf(zooKeeper.getState().toString()));
                sessionMeta.put(CONNECT_STRING, this.connectString);
                sessionMeta.put(SESSION_TIMEOUT, String.valueOf(this.sessionTimeout));
            }
        } catch (Exception e) {
            LoggerFactory.getLogger().error("Error occurred retrieving session meta data.", e);
        }
        return sessionMeta;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#createNode
     * (java.lang.String, java.lang.String)
     */
    @Override
    public boolean createNode(String parent, String nodeName) {
        if (zooKeeper.getState() == States.CONNECTED) {
            try {
                String[] nodeElements = nodeName.split("/");
                for (String nodeElement : nodeElements) {
                    String node = parent + "/" + nodeElement;
                    Stat s = zooKeeper.exists(node, false);
                    if (s == null) {
                        zooKeeper.create(node, this.encryptionManager.encryptData(null), Ids.OPEN_ACL_UNSAFE,
                                CreateMode.PERSISTENT);
                        parent = node;
                    }
                }
                return true;
            } catch (Exception e) {
                LoggerFactory.getLogger().error("Error occurred creating node: " + parent + "/" + nodeName, e);
            }
        }
        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorNodeTreeManager#deleteNode
     * (java.lang.String)
     */
    @Override
    public boolean deleteNode(String nodePath) {
        if (zooKeeper.getState() == States.CONNECTED) {
            try {
                // Stat s = zooKeeper.exists(nodePath, false);
                // if (s != null) {
                List<String> children = zooKeeper.getChildren(nodePath, false);
                if (children != null) {
                    for (String child : children) {
                        String node = nodePath + "/" + child;
                        deleteNode(node);
                    }
                }
                zooKeeper.delete(nodePath, -1);
                // }
                return true;
            } catch (Exception e) {
                LoggerFactory.getLogger().error("Error occurred deleting node: " + nodePath, e);
            }
        }
        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorNodeManager#setData
     * (java.lang.String, java.lang.String)
     */
    @Override
    public boolean setData(String nodePath, String data) {
        if (connected) {
            try {
                zooKeeper.setData(nodePath, this.encryptionManager.encryptData(data), -1);
                return true;
            } catch (Exception e) {
                LoggerFactory.getLogger().error("Error occurred setting data for node: " + nodePath, e);
            }
        }
        return false;
    }

    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * getConnectionPropertiesTemplate()
    //   */
    //  @Override
    //  public Pair<Map<String, List<String>>, Map<String, String>> getConnectionPropertiesTemplate()
    //  {
    //    Map<String, List<String>> template = new LinkedHashMap<String, List<String>>();
    //    // template.put(CONNECT_STRING, Arrays.asList(new String[] { defaultHosts }));
    //    template.put(CONNECT_STRING, defaultHostsList);
    //    template.put(SESSION_TIMEOUT, Arrays.asList(new String[] { defaultTimeout }));
    //    template.put(DATA_ENCRYPTION_MANAGER,
    //                 Arrays.asList(new String[] { defaultEncryptionManager }));
    //    Map<String, String> labels = new LinkedHashMap<String, String>();
    //    labels.put(CONNECT_STRING, "Connect String");
    //    labels.put(SESSION_TIMEOUT, "Session Timeout");
    //    labels.put(DATA_ENCRYPTION_MANAGER, "Data Encryption Manager");
    //    return new Pair<Map<String, List<String>>, Map<String, String>>(template, labels);
    //  }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager#addWatchers
     * (java.util.Collection, org.apache.zookeeper.inspector.manager.NodeListener)
     */
    @Override
    public void addWatchers(Collection<String> selectedNodes, NodeListener nodeListener) {
        // add watcher for each node and add node to collection of
        // watched nodes
        if (connected) {
            for (String node : selectedNodes) {
                if (!watchers.containsKey(node)) {
                    try {
                        watchers.put(node, new NodeWatcher(node, nodeListener, zooKeeper));
                    } catch (Exception e) {
                        LoggerFactory.getLogger().error("Error occured adding node watcher for node: " + node, e);
                    }
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.apache.zookeeper.inspector.manager.ZooInspectorManager#removeWatchers
     * (java.util.Collection)
     */
    @Override
    public void removeWatchers(Collection<String> selectedNodes) {
        // remove watcher for each node and remove node from
        // collection of watched nodes
        if (connected) {
            for (String node : selectedNodes) {
                if (watchers.containsKey(node)) {
                    NodeWatcher watcher = watchers.remove(node);
                    if (watcher != null) {
                        watcher.stop();
                    }
                }
            }
        }
    }

    /**
     * A Watcher which will re-add itself every time an event is fired
     *
     */
    public class NodeWatcher implements Watcher {

        private final String nodePath;
        private final NodeListener nodeListener;
        private final ZooKeeper zookeeper;
        private boolean closed = false;

        // cache stat and children when event fires
        private final Stat stat = new Stat();
        private List<String> childs;

        /**
         * @param nodePath
         *          - the path to the node to watch
         * @param nodeListener
         *          the {@link NodeListener} for this node
         * @param zookeeper
         *          - a {@link ZooKeeper} to use to access zookeeper
         * @throws InterruptedException
         * @throws KeeperException
         */
        public NodeWatcher(String nodePath, NodeListener nodeListener, ZooKeeper zookeeper)
                throws KeeperException, InterruptedException {
            this.nodePath = nodePath;
            this.nodeListener = nodeListener;
            this.zookeeper = zookeeper;
            Stat s = zooKeeper.exists(nodePath, this);
            if (s != null) {
                childs = zookeeper.getChildren(nodePath, this, stat);
            }
        }

        @Override
        public void process(WatchedEvent event) {
            if (!closed) {
                try {
                    if (event.getType() != EventType.NodeDeleted) {

                        Stat s = zooKeeper.exists(nodePath, this);
                        if (s != null) {
                            childs = zookeeper.getChildren(nodePath, this, s);
                        }
                    }
                } catch (Exception e) {
                    LoggerFactory.getLogger().error("Error occured re-adding node watcherfor node " + nodePath, e);
                }

                if (nodeListener != null) {
                    nodeListener.processEvent(event.getPath(), event.getType().name(), null);
                }
            }
        }

        /**
           *
           */
        public void stop() {
            this.closed = true;
        }

        public List<String> getChilds() {
            return childs;
        }
    }

    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * loadNodeViewersFile(java.io.File)
    //   */
    //  @Override
    //  public List<String> loadNodeViewersFile(File selectedFile) throws IOException
    //  {
    //    // TODO read from src/main/resources/defaultNodeVeiwers.cfg
    //    List<String> result = new ArrayList<String>();
    //    if (defaultNodeViewersFile.exists())
    //    {
    //      FileReader reader = new FileReader(selectedFile);
    //      try
    //      {
    //        BufferedReader buff = new BufferedReader(reader);
    //        try
    //        {
    //          while (buff.ready())
    //          {
    //            String line = buff.readLine();
    //            if (line != null && line.length() > 0)
    //            {
    //              result.add(line);
    //            }
    //          }
    //        }
    //        finally
    //        {
    //          buff.close();
    //        }
    //      }
    //      finally
    //      {
    //        reader.close();
    //      }
    //    } else {
    //      result.add("org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerData");
    //      result.add("org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerMetaData");
    //      result.add("org.apache.zookeeper.inspector.gui.nodeviewer.NodeViewerACL");
    //    }
    //    return result;
    //  }
    //
    //  private void loadDefaultConnectionFile() throws IOException
    //  {
    //    if (defaultConnectionFile.exists())
    //    {
    //      Properties props = new Properties();
    //
    //      FileReader reader = new FileReader(defaultConnectionFile);
    //      try
    //      {
    //        props.load(reader);
    //      }
    //      finally
    //      {
    //        reader.close();
    //      }
    //      defaultEncryptionManager =
    //          props.getProperty(DATA_ENCRYPTION_MANAGER) == null
    //              ? "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager"
    //              : props.getProperty(DATA_ENCRYPTION_MANAGER);
    //      defaultTimeout =
    //          props.getProperty(SESSION_TIMEOUT) == null ? "30000"
    //              : props.getProperty(SESSION_TIMEOUT);
    //      defaultHosts =
    //          props.getProperty(CONNECT_STRING) == null ? "localhost:2181"
    //              : props.getProperty(CONNECT_STRING);
    //    }
    //    else
    //    {
    //      defaultEncryptionManager =
    //          "org.apache.zookeeper.inspector.encryption.BasicDataEncryptionManager";
    //      defaultTimeout = "30000";
    //      defaultHosts = "localhost:2181";
    //    }
    //
    //    defaultHostsList = new ArrayList<String>(Arrays.asList(defaultHosts.trim().split("\\s+")));
    //    System.out.println("defaultHostsList: " + defaultHostsList);
    ////    System.out.println("end");
    //  }
    //
    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * saveDefaultConnectionFile(java.util.Properties)
    //   */
    //  @Override
    //  public void saveDefaultConnectionFile(Properties props) throws IOException
    //  {
    //    File defaultDir = defaultConnectionFile.getParentFile();
    //    if (!defaultDir.exists())
    //    {
    //      if (!defaultDir.mkdirs())
    //      {
    //        throw new IOException("Failed to create configuration directory: "
    //            + defaultDir.getAbsolutePath());
    //      }
    //    }
    //    if (!defaultConnectionFile.exists())
    //    {
    //      if (!defaultConnectionFile.createNewFile())
    //      {
    //        throw new IOException("Failed to create default connection file: "
    //            + defaultConnectionFile.getAbsolutePath());
    //      }
    //    }
    //    FileWriter writer = new FileWriter(defaultConnectionFile);
    //    try
    //    {
    //      props.store(writer, "Default connection for ZooInspector");
    //    }
    //    finally
    //    {
    //      writer.close();
    //    }
    //  }
    //
    //  @Override
    //  public void updateDefaultConnectionFile(Properties connectionProps) throws IOException
    //  {
    //    Properties properties = new Properties();
    //
    //    String connStr = connectionProps.getProperty(CONNECT_STRING);
    //    defaultHostsList.remove(connStr);
    //    while (defaultHostsList.size() > defaultHostsListSize) {
    //      defaultHostsList.remove(defaultHostsList.size()-1);
    //    }
    //
    //    defaultHostsList.add(0, connStr);
    //
    //    StringBuilder sb = new StringBuilder();
    //    for (int i = 0; i < defaultHostsList.size(); i++) {
    //      String str = defaultHostsList.get(i);
    //      if (i > 0) {
    //        sb.append(" ");
    //      }
    //      sb.append(str);
    //    }
    //    System.out.println("updateDefaultConnectionFile#connectString: " + sb.toString());
    //    properties.setProperty(CONNECT_STRING, sb.toString());
    //    properties.setProperty(SESSION_TIMEOUT, defaultTimeout);
    //    properties.getProperty(DATA_ENCRYPTION_MANAGER, defaultEncryptionManager);
    //    saveDefaultConnectionFile(properties);
    //  }
    //
    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * saveNodeViewersFile(java.io.File, java.util.List)
    //   */
    //  @Override
    //  public void saveNodeViewersFile(File selectedFile, List<String> nodeViewersClassNames) throws IOException
    //  {
    //    if (!selectedFile.exists())
    //    {
    //      if (!selectedFile.createNewFile())
    //      {
    //        throw new IOException("Failed to create node viewers configuration file: "
    //            + selectedFile.getAbsolutePath());
    //      }
    //    }
    //    FileWriter writer = new FileWriter(selectedFile);
    //    try
    //    {
    //      BufferedWriter buff = new BufferedWriter(writer);
    //      try
    //      {
    //        for (String nodeViewersClassName : nodeViewersClassNames)
    //        {
    //          buff.append(nodeViewersClassName);
    //          buff.append("\n");
    //        }
    //      }
    //      finally
    //      {
    //        buff.flush();
    //        buff.close();
    //      }
    //    }
    //    finally
    //    {
    //      writer.close();
    //    }
    //  }
    //
    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * setDefaultNodeViewerConfiguration(java.io.File, java.util.List)
    //   */
    //  @Override
    //  public void setDefaultNodeViewerConfiguration(List<String> nodeViewersClassNames) throws IOException
    //  {
    //    File defaultDir = defaultNodeViewersFile.getParentFile();
    //    if (!defaultDir.exists())
    //    {
    //      if (!defaultDir.mkdirs())
    //      {
    //        throw new IOException("Failed to create configuration directory: "
    //            + defaultDir.getAbsolutePath());
    //      }
    //    }
    //    saveNodeViewersFile(defaultNodeViewersFile, nodeViewersClassNames);
    //  }
    //
    //  @Override
    //  public List<String> getDefaultNodeViewerConfiguration() throws IOException
    //  {
    //    return loadNodeViewersFile(defaultNodeViewersFile);
    //  }
    //
    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * getLastConnectionProps()
    //   */
    //  @Override
    //  public Properties getLastConnectionProps()
    //  {
    //    return this.lastConnectionProps;
    //  }
    //
    //  /*
    //   * (non-Javadoc)
    //   *
    //   * @seeorg.apache.zookeeper.inspector.manager.ZooInspectorManager#
    //   * setLastConnectionProps(java.util.Properties)
    //   */
    //  @Override
    //  public void setLastConnectionProps(Properties connectionProps)
    //  {
    //    this.lastConnectionProps = connectionProps;
    //  }

    @Override
    public ZooInspectorManagerCache getCache() {
        return cache;
    }

    @Override
    public States getZookeeperStates() {
        if (zooKeeper == null) {
            return null;
        }

        return zooKeeper.getState();
    }

}