@Override
 public void action(List<String> clusters) {
   for (String cluster : clusters) {
     if (!tableWatchers.containsKey(cluster)) {
       WatchChildren clusterWatcher =
           new WatchChildren(_zk, ZookeeperPathConstants.getTablesPath(cluster))
               .watch(new Tables(cluster));
       tableWatchers.put(cluster, clusterWatcher);
       WatchNodeExistance watchNodeExistance =
           new WatchNodeExistance(_zk, ZookeeperPathConstants.getSafemodePath(cluster))
               .watch(new SafeExistance(cluster));
       safeModeWatchers.put(cluster, watchNodeExistance);
     }
   }
   List<String> clustersToCloseAndRemove = new ArrayList<String>(clusters);
   clustersToCloseAndRemove.removeAll(tableWatchers.keySet());
   for (String cluster : clustersToCloseAndRemove) {
     WatchChildren watcher = tableWatchers.remove(cluster);
     if (watcher == null) {
       LOG.error("Error watcher is null [" + cluster + "] ");
     } else {
       watcher.close();
     }
   }
 }
 @Override
 public void action(List<String> tables) {
   synchronized (_tableToClusterCache) {
     Map<String, String> map = _tableToClusterCache.get();
     for (String t : tables) {
       final String table = t;
       String existingCluster = map.get(table);
       if (existingCluster == null) {
         map.put(table, cluster);
         enabledTableWatchers.put(
             table,
             new WatchNodeExistance(
                     _zk, ZookeeperPathConstants.getTableEnabledPath(cluster, table))
                 .watch(
                     new WatchNodeExistance.OnChange() {
                       @Override
                       public void action(Stat stat) {
                         String clusterTableKey = getClusterTableKey(cluster, table);
                         if (stat == null) {
                           _enabledMap.put(clusterTableKey, false);
                         } else {
                           _enabledMap.put(clusterTableKey, true);
                         }
                       }
                     }));
       } else if (!existingCluster.equals(cluster)) {
         LOG.error(
             "Error table [{0}] is being served by more than one cluster [{1},{2}].",
             table, existingCluster, cluster);
       }
     }
   }
 }
 @Override
 public boolean isReadOnly(boolean useCache, String cluster, String table) {
   String key = getClusterTableKey(cluster, table);
   if (useCache) {
     Boolean flag = _readOnlyMap.get(key);
     if (flag != null) {
       return flag;
     }
   }
   LOG.debug("trace isReadOnly");
   String path = ZookeeperPathConstants.getTableReadOnlyPath(cluster, table);
   Boolean flag = null;
   try {
     if (_zk.exists(path, false) == null) {
       flag = false;
       return false;
     }
     flag = true;
     return true;
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   } finally {
     _readOnlyMap.put(key, flag);
   }
 }
 @Override
 public boolean isInSafeMode(boolean useCache, String cluster) {
   if (useCache) {
     Long safeModeTimestamp = _safeModeMap.get(cluster);
     if (safeModeTimestamp == null) {
       return true;
     }
     return safeModeTimestamp < System.currentTimeMillis() ? false : true;
   }
   LOG.debug("trace isInSafeMode");
   try {
     String blurSafemodePath = ZookeeperPathConstants.getSafemodePath(cluster);
     Stat stat = _zk.exists(blurSafemodePath, false);
     if (stat == null) {
       return false;
     }
     byte[] data = _zk.getData(blurSafemodePath, false, stat);
     if (data == null) {
       return false;
     }
     long timestamp = Long.parseLong(new String(data));
     long waitTime = timestamp - System.currentTimeMillis();
     if (waitTime > 0) {
       return true;
     }
     return false;
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public String getCluster(boolean useCache, String table) {
   if (useCache) {
     Map<String, String> map = _tableToClusterCache.get();
     String cluster = map.get(table);
     if (cluster == null) {
       return null;
     } else {
       return cluster;
     }
   }
   LOG.debug("trace getCluster");
   List<String> clusterList = getClusterList();
   for (String cluster : clusterList) {
     try {
       Stat stat = _zk.exists(ZookeeperPathConstants.getTablePath(cluster, table), false);
       if (stat != null) {
         return cluster;
       }
     } catch (KeeperException e) {
       throw new RuntimeException(e);
     } catch (InterruptedException e) {
       throw new RuntimeException(e);
     }
   }
   return null;
 }
 @Override
 public List<String> getOnlineShardServers(boolean useCache, String cluster) {
   if (useCache) {
     synchronized (_onlineShardsNodes) {
       Map<String, List<String>> map = _onlineShardsNodes.get();
       if (map != null) {
         List<String> shards = map.get(cluster);
         if (shards != null) {
           return shards;
         }
       } else {
         _onlineShardsNodes.set(new ConcurrentHashMap<String, List<String>>());
         watchForOnlineShardNodes(cluster);
       }
     }
   }
   LOG.info("trace getOnlineShardServers");
   try {
     return _zk.getChildren(
         ZookeeperPathConstants.getClustersPath() + "/" + cluster + "/online/shard-nodes", false);
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public TableDescriptor getTableDescriptor(boolean useCache, String cluster, String table) {
   if (useCache) {
     TableDescriptor tableDescriptor = _tableDescriptorCache.get(table);
     if (tableDescriptor != null) {
       return tableDescriptor;
     }
   }
   LOG.debug("trace getTableDescriptor");
   TableDescriptor tableDescriptor = new TableDescriptor();
   try {
     if (_zk.exists(ZookeeperPathConstants.getTableEnabledPath(cluster, table), false) == null) {
       tableDescriptor.isEnabled = false;
     } else {
       tableDescriptor.isEnabled = true;
     }
     tableDescriptor.shardCount =
         Integer.parseInt(
             new String(getData(ZookeeperPathConstants.getTableShardCountPath(cluster, table))));
     tableDescriptor.tableUri =
         new String(getData(ZookeeperPathConstants.getTableUriPath(cluster, table)));
     tableDescriptor.compressionClass =
         new String(getData(ZookeeperPathConstants.getTableCompressionCodecPath(cluster, table)));
     tableDescriptor.compressionBlockSize =
         Integer.parseInt(
             new String(
                 getData(
                     ZookeeperPathConstants.getTableCompressionBlockSizePath(cluster, table))));
     tableDescriptor.analyzerDefinition =
         fromBytes(
             getData(ZookeeperPathConstants.getTablePath(cluster, table)),
             AnalyzerDefinition.class);
     tableDescriptor.blockCaching = isBlockCacheEnabled(cluster, table);
     tableDescriptor.blockCachingFileTypes = getBlockCacheFileTypes(cluster, table);
     tableDescriptor.name = table;
     tableDescriptor.columnPreCache =
         fromBytes(
             getData(ZookeeperPathConstants.getTableColumnsToPreCache(cluster, table)),
             ColumnPreCache.class);
     byte[] data = getData(ZookeeperPathConstants.getTableSimilarityPath(cluster, table));
     if (data != null) {
       tableDescriptor.similarityClass = new String(data);
     }
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
   tableDescriptor.cluster = cluster;
   _tableDescriptorCache.put(table, tableDescriptor);
   return tableDescriptor;
 }
 @Override
 public List<String> getTableList(String cluster) {
   LOG.debug("trace getTableList");
   try {
     return _zk.getChildren(ZookeeperPathConstants.getTablesPath(cluster), false);
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 private void watchForOnlineShardNodes(final String cluster) {
   new WatchChildren(_zk, ZookeeperPathConstants.getOnlineShardsPath(cluster))
       .watch(
           new OnChange() {
             @Override
             public void action(List<String> children) {
               Map<String, List<String>> map = _onlineShardsNodes.get();
               map.put(cluster, children);
             }
           });
 }
 @Override
 public List<String> getControllerServerList() {
   LOG.info("trace getControllerServerList");
   try {
     return _zk.getChildren(ZookeeperPathConstants.getOnlineControllersPath(), false);
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public List<String> getShardServerList(String cluster) {
   LOG.debug("trace getShardServerList");
   try {
     return _zk.getChildren(
         ZookeeperPathConstants.getClustersPath() + "/" + cluster + "/shard-nodes", false);
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public boolean isBlockCacheEnabled(String cluster, String table) {
   LOG.debug("trace isBlockCacheEnabled");
   try {
     if (_zk.exists(
             ZookeeperPathConstants.getTableBlockCachingFileTypesPath(cluster, table), false)
         == null) {
       return false;
     }
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
   return true;
 }
 @Override
 public int getShardCount(boolean useCache, String cluster, String table) {
   if (useCache) {
     TableDescriptor tableDescriptor = getTableDescriptor(true, cluster, table);
     return tableDescriptor.shardCount;
   }
   LOG.debug("trace getShardCount");
   try {
     return Integer.parseInt(
         new String(getData(ZookeeperPathConstants.getTableShardCountPath(cluster, table))));
   } catch (NumberFormatException e) {
     throw new RuntimeException(e);
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public void clearLocks(String cluster, String table) {
   LOG.debug("trace clearLocks");
   String lockPath = ZookeeperPathConstants.getLockPath(cluster, table);
   try {
     if (_zk.exists(lockPath, false) == null) {
       return;
     }
     List<String> children = _zk.getChildren(lockPath, false);
     for (String c : children) {
       LOG.warn("Removing lock [{0}] for table [{1}]", c, table);
       _zk.delete(lockPath + "/" + c, -1);
     }
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public void action(Stat stat) {
   if (stat != null) {
     safeModeDataWatchers.put(
         cluster,
         new WatchNodeData(_zk, ZookeeperPathConstants.getSafemodePath(cluster))
             .watch(
                 new WatchNodeData.OnChange() {
                   @Override
                   public void action(byte[] data) {
                     if (data == null) {
                       _safeModeMap.put(cluster, null);
                     } else {
                       _safeModeMap.put(cluster, Long.parseLong(new String(data)));
                     }
                   }
                 }));
   }
 }
 @Override
 public boolean exists(boolean useCache, String cluster, String table) {
   if (useCache) {
     Map<String, String> map = _tableToClusterCache.get();
     if (map.containsKey(table)) {
       return true;
     } else {
       return false;
     }
   }
   LOG.debug("trace exists");
   try {
     if (_zk.exists(ZookeeperPathConstants.getTablePath(cluster, table), false) == null) {
       return false;
     }
     return true;
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 @Override
 public boolean isEnabled(boolean useCache, String cluster, String table) {
   if (useCache) {
     Boolean enabled = _enabledMap.get(getClusterTableKey(cluster, table));
     if (enabled == null) {
       throw new RuntimeException("Table [" + table + "] does not exist.");
     } else {
       return enabled;
     }
   }
   LOG.debug("trace isEnabled");
   String tablePathIsEnabled = ZookeeperPathConstants.getTableEnabledPath(cluster, table);
   try {
     if (_zk.exists(tablePathIsEnabled, false) == null) {
       return false;
     }
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
   return true;
 }
 @Override
 public Set<String> getBlockCacheFileTypes(String cluster, String table) {
   LOG.debug("trace getBlockCacheFileTypes");
   try {
     byte[] data =
         getData(ZookeeperPathConstants.getTableBlockCachingFileTypesPath(cluster, table));
     if (data == null) {
       return null;
     }
     String str = new String(data);
     if (str.isEmpty()) {
       return null;
     }
     Set<String> types = new HashSet<String>(Arrays.asList(str.split(",")));
     if (types.isEmpty()) {
       return null;
     }
     return types;
   } catch (KeeperException e) {
     throw new RuntimeException(e);
   } catch (InterruptedException e) {
     throw new RuntimeException(e);
   }
 }
 private void watchForClusters() {
   clusterWatch =
       new WatchChildren(_zk, ZookeeperPathConstants.getClustersPath()).watch(new Clusters());
 }