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