org.apache.hadoop.hbase.crosssite.CrossSiteZNodes.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.crosssite.CrossSiteZNodes.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 org.apache.hadoop.hbase.crosssite;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableNotFoundException;
import org.apache.hadoop.hbase.crosssite.locator.ClusterLocator;
import org.apache.hadoop.hbase.crosssite.locator.ClusterLocatorRPCObject;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.Pair;
import org.apache.hadoop.hbase.zookeeper.ZKUtil;
import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
import org.apache.zookeeper.KeeperException;

/**
 * The operations in the cross site table are included.
 */
public class CrossSiteZNodes {

    private static final String EPHEMERAL = "-ephemeral";
    private static final String CLUSTER_EPHEMERAL = "-cluster-ephemeral";
    private static final Log LOG = LogFactory.getLog(CrossSiteZNodes.class);
    public final static String CROSS_SITE_ZNODE_NAME = "CrossSite";
    public final static String CLUSTERS_ZNODE_NAME = "clusters";
    public final static String TABLES_ZNODE_NAME = "tables";
    public final static String CLUSTER_ADDRESS_ZNODE_NAME = "address";
    public final static String CLUSTER_PEERS_ZNODE_NAME = "peers";
    public final static String TABLE_STATE_ZNODE_NAME = "state";
    public final static String TABLE_DESC_ZNODE_NAME = "desc";
    public final static String TABLE_PROPOSED_DESC_ZNODE_NAME = "proposed_desc";
    public final static String TABLE_SPLITKEYS_ZNODE_NAME = "splitkeys";
    public final static String CLUSTER_LOCATOR_ZNODE_NAME = "locator";
    public final static String HIERARCHY_ZNODE_NAME = "hierarchy";

    private final ZooKeeperWatcher zkw;
    private String crossSiteZNode;
    private String clustersZNode;
    private String tablesZNode;
    private String hierarchyZNode;

    /** The state of the Cross Site Table **/
    // When adding new states later, make sure to add towards the end without disturbing the ordinal.
    public static enum TableState {
        ENABLED, DISABLED, ENABLING, DISABLING, DELETING, MODIFYING, ADDINGCOLUMN, DELETINGCOLUMN, MODIFYINGCOLUMN;
    };

    /**
     * 
     * @param zkw
     * @param createBaseZNode
     *          If true, the base znodes(CrossSite, clusters, tables and hierarchy) will be created if
     *          they're not present.
     * @throws KeeperException
     */
    public CrossSiteZNodes(ZooKeeperWatcher zkw, boolean createBaseZNode) throws KeeperException {
        this.zkw = zkw;
        this.crossSiteZNode = ZKUtil.joinZNode(zkw.baseZNode, CROSS_SITE_ZNODE_NAME);
        this.clustersZNode = ZKUtil.joinZNode(crossSiteZNode, CLUSTERS_ZNODE_NAME);
        this.tablesZNode = ZKUtil.joinZNode(crossSiteZNode, TABLES_ZNODE_NAME);
        this.hierarchyZNode = ZKUtil.joinZNode(crossSiteZNode, HIERARCHY_ZNODE_NAME);
        if (createBaseZNode) {
            if (ZKUtil.checkExists(zkw, crossSiteZNode) == -1) {
                ZKUtil.createWithParents(zkw, crossSiteZNode);
            }
            if (ZKUtil.checkExists(zkw, clustersZNode) == -1) {
                ZKUtil.createWithParents(zkw, clustersZNode);
            }
            if (ZKUtil.checkExists(zkw, tablesZNode) == -1) {
                ZKUtil.createWithParents(zkw, tablesZNode);
            }
            if (ZKUtil.checkExists(zkw, hierarchyZNode) == -1) {
                ZKUtil.createWithParents(zkw, hierarchyZNode);
            }
        }
    }

    /**
     * The constructor. The base znodes(CrossSite, clusters, tables and hierarchy) will be created if
     * they're not present.
     * 
     * @param zkw
     * @throws KeeperException
     */
    public CrossSiteZNodes(ZooKeeperWatcher zkw) throws KeeperException {
        this(zkw, true);
    }

    /**
     * @return the root znode for the cross site tables.
     */
    public String getCrossSiteZNode() {
        return crossSiteZNode;
    }

    /**
     * @return the root znode for clusters.
     */
    public String getClustersZNode() {
        return clustersZNode;
    }

    /**
     * Gets the znode path of the cross site tables.
     * 
     * @return
     */
    public String getTablesZNode() {
        return tablesZNode;
    }

    /**
     * Gets the znode path of the hierarchy.
     * 
     * @return
     */
    public String getHierarchyZNode() {
        return hierarchyZNode;
    }

    /**
     * Creates the znode for the cluster.
     * 
     * @param clusterInfo
     * @throws KeeperException
     */
    public void createClusterZNode(ClusterInfo clusterInfo) throws KeeperException {
        ZKUtil.createWithParents(zkw, getClusterZNode(clusterInfo.getName()));
        ZKUtil.createWithParents(zkw, getClusterAddressZNode(clusterInfo.getName()),
                Bytes.toBytes(clusterInfo.getAddress()));
        String peersZNode = getClusterPeersZNode(clusterInfo.getName());
        ZKUtil.createWithParents(zkw, peersZNode);
        if (clusterInfo.getPeers() != null) {
            for (ClusterInfo peer : clusterInfo.getPeers()) {
                ZKUtil.createWithParents(zkw, ZKUtil.joinZNode(peersZNode, peer.getName()),
                        Bytes.toBytes(peer.getAddress()));
            }
        }
    }

    /**
     * Creates the znode for the cluster.
     * 
     * @param name
     * @param address
     * @throws KeeperException
     */
    public void createClusterZNode(String name, String address) throws KeeperException {
        ZKUtil.createWithParents(zkw, getClusterZNode(name));
        ZKUtil.createWithParents(zkw, getClusterAddressZNode(name), Bytes.toBytes(address));
    }

    /**
     * Creates the peer.
     * 
     * @param clusterName
     * @param peer
     * @throws KeeperException
     */
    public void createPeer(String clusterName, Pair<String, String> peer) throws KeeperException {
        String peersZNode = getClusterPeersZNode(clusterName);
        ZKUtil.createWithParents(zkw, ZKUtil.joinZNode(peersZNode, peer.getFirst()),
                Bytes.toBytes(peer.getSecond()));
    }

    /**
     * Deletes all the children nodes of the cluster but itself.
     * 
     * @param clusterName
     * @throws KeeperException
     */
    public void deletePeers(String clusterName) throws KeeperException {
        ZKUtil.deleteChildrenRecursively(zkw, getClusterPeersZNode(clusterName));
    }

    /**
     * Deletes the peers and all their children.
     * 
     * @param clusterName
     * @param peers
     * @throws KeeperException
     */
    public void deletePeers(String clusterName, String[] peers) throws KeeperException {
        String peersZNode = getClusterPeersZNode(clusterName);
        if (peers != null && peers.length > 0) {
            for (String peer : peers) {
                ZKUtil.deleteNodeRecursively(zkw, ZKUtil.joinZNode(peersZNode, peer));
            }
        } else {
            this.deletePeers(clusterName);
        }
    }

    /**
     * Deletes the znode of the cluster.
     * 
     * @param clusterName
     * @throws KeeperException
     */
    public void deleteClusterZNode(String clusterName) throws KeeperException {
        ZKUtil.deleteNodeRecursively(zkw, getClusterZNode(clusterName));
    }

    /**
     * Creates the hierarchy znode.
     * 
     * @throws KeeperException
     */
    public void createHierarchyZNode() throws KeeperException {
        ZKUtil.createWithParents(zkw, hierarchyZNode);
    }

    /**
     * Creates the hierarchy.
     * 
     * <pre>
     * Parent | --child1 | --child2
     * </pre>
     * 
     * @param parent
     * @param children
     * @throws KeeperException
     */
    public void createHierarchy(String parent, String[] children) throws KeeperException {
        String parentZNode = ZKUtil.joinZNode(hierarchyZNode, parent);
        ZKUtil.createWithParents(zkw, parentZNode);
        for (String child : children) {
            ZKUtil.createWithParents(zkw, ZKUtil.joinZNode(parentZNode, child));
        }
    }

    /**
     * Deletes the hierarchy under the parent including itself.
     * 
     * @param parent
     * @throws KeeperException
     */
    public void deleteHierarchy(String parent) throws KeeperException {
        String parentZNode = ZKUtil.joinZNode(hierarchyZNode, parent);
        List<String> children = ZKUtil.listChildrenNoWatch(zkw, parentZNode);
        ZKUtil.deleteNodeRecursively(zkw, ZKUtil.joinZNode(hierarchyZNode, parent));
        if (children != null) {
            for (String child : children) {
                // since the child may be the parent itself, we need to add this check to avoid a infinite
                // loop.
                if (!child.equals(parent)) {
                    deleteHierarchy(child);
                }
            }
        }
    }

    /**
     * Deletes the hierarchy of the children, and delete the hierarchy between the parent and
     * children.
     * 
     * @param parent
     * @param children
     * @throws KeeperException
     */
    public void deleteHierarchy(String parent, String[] children) throws KeeperException {
        if (children != null && children.length > 0) {
            String parentZNode = ZKUtil.joinZNode(hierarchyZNode, parent);
            for (String child : children) {
                ZKUtil.deleteNode(zkw, ZKUtil.joinZNode(parentZNode, child));
                // since the child may be the parent itself, we need to add this check to avoid a infinite
                // loop.
                if (!child.equals(parent)) {
                    deleteHierarchy(child);
                }
            }
        } else {
            deleteHierarchy(parent);
        }
    }

    /**
     * Lists the addresses of the clusters.
     * 
     * @return
     * @throws KeeperException
     */
    public Map<String, String> listClusterAddresses() throws KeeperException {
        Map<String, String> clusterMap = new TreeMap<String, String>();
        List<String> clusters = ZKUtil.listChildrenNoWatch(this.zkw, this.clustersZNode);
        if (clusters != null) {
            for (String cluster : clusters) {
                clusterMap.put(cluster, Bytes.toString(ZKUtil.getData(this.zkw, getClusterAddressZNode(cluster))));
            }
        }
        return clusterMap;
    }

    /**
     * Lists the cluster information.
     * 
     * @return
     * @throws KeeperException
     */
    public Map<String, ClusterInfo> listClusterInfos() throws KeeperException {
        Map<String, ClusterInfo> clusterMap = new TreeMap<String, ClusterInfo>();
        List<String> clusters = ZKUtil.listChildrenNoWatch(this.zkw, this.clustersZNode);
        if (clusters != null) {
            for (String cluster : clusters) {
                String address = Bytes.toString(ZKUtil.getData(this.zkw, getClusterAddressZNode(cluster)));
                List<ClusterInfo> peers = getPeerClusters(cluster);
                Set<ClusterInfo> peerSet = new TreeSet<ClusterInfo>();
                if (peers != null) {
                    peerSet.addAll(peers);
                }
                ClusterInfo ci = new ClusterInfo(cluster, address, peerSet);
                clusterMap.put(cluster, ci);
            }
        }
        return clusterMap;
    }

    /**
     * Returns the clusterInfo for the given clusterName
     * @param clusterName
     * @return
     * @throws KeeperException
     */
    public ClusterInfo getClusterInfo(String clusterName) throws KeeperException {
        List<String> clusters = ZKUtil.listChildrenNoWatch(this.zkw, this.clustersZNode);
        if (clusters != null) {
            for (String cluster : clusters) {
                if (cluster.equals(clusterName)) {
                    String address = Bytes.toString(ZKUtil.getData(this.zkw, getClusterAddressZNode(cluster)));
                    if (address != null) {
                        List<ClusterInfo> peers = getPeerClusters(cluster);
                        Set<ClusterInfo> peerSet = new TreeSet<ClusterInfo>();
                        if (peers != null) {
                            peerSet.addAll(peers);
                        }
                        ClusterInfo ci = new ClusterInfo(cluster, address, peerSet);
                        return ci;
                    } else {
                        return null;
                    }
                }
            }
        }
        return null;
    }

    /**
     * Lists the peers of the cluster.
     * 
     * @param clusterName
     * @return
     * @throws KeeperException
     */
    public List<String> listClusterPeers(String clusterName) throws KeeperException {
        List<String> emptyResult = Collections.emptyList();
        List<String> result = ZKUtil.listChildrenNoWatch(this.zkw, getClusterPeersZNode(clusterName));
        return result == null ? emptyResult : result;
    }

    /**
     * Gets the znode path of the cluster address.
     * 
     * @param clusterName
     * @return
     */
    public String getClusterAddressZNode(String clusterName) {
        return ZKUtil.joinZNode(getClusterZNode(clusterName), CLUSTER_ADDRESS_ZNODE_NAME);
    }

    /**
     * Gets the znode path of the cluster peers.
     * 
     * @param clusterName
     * @return
     */
    public String getClusterPeersZNode(String clusterName) {
        return ZKUtil.joinZNode(getClusterZNode(clusterName), CLUSTER_PEERS_ZNODE_NAME);
    }

    /**
     * Gets the peer znode path of cluster.
     * 
     * @param clusterName
     * @param peer
     * @return
     */
    public String getClusterPeerZNode(String clusterName, String peer) {
        return ZKUtil.joinZNode(getClusterPeersZNode(clusterName), peer);
    }

    /**
     * Gets the znode path of the cluster.
     * 
     * @param clusterName
     * @return
     */
    public String getClusterZNode(String clusterName) {
        return ZKUtil.joinZNode(this.clustersZNode, clusterName);
    }

    /**
     * Gets all the peer clusters for the given cluster. Will return null when no peer available for
     * the given cluster
     * 
     * @param clusterName
     * @return List of peer clusters
     * @throws KeeperException
     */
    public List<ClusterInfo> getPeerClusters(String clusterName) throws KeeperException {
        List<String> peers = ZKUtil.listChildrenNoWatch(zkw, getClusterPeersZNode(clusterName));
        List<ClusterInfo> cis = Collections.emptyList();
        if (peers != null) {
            cis = new ArrayList<ClusterInfo>();
            for (String peer : peers) {
                byte[] data = ZKUtil.getData(zkw, getClusterPeerZNode(clusterName, peer));
                if (data != null) {
                    ClusterInfo ci = new ClusterInfo(peer, Bytes.toString(data), (ClusterInfo[]) null);
                    cis.add(ci);
                }
            }
        }
        return cis;
    }

    /**
     * Gets the peers of the cluster.
     * 
     * @param clusterName
     * @param peers
     * @return
     * @throws KeeperException
     */
    public List<ClusterInfo> getClusterPeers(String clusterName, String[] peers) throws KeeperException {
        if (peers != null && peers.length > 0) {
            List<ClusterInfo> cis = new ArrayList<ClusterInfo>();
            for (String peer : peers) {
                byte[] data = ZKUtil.getData(zkw, getClusterPeerZNode(clusterName, peer));
                if (data != null) {
                    ClusterInfo ci = new ClusterInfo(peer, Bytes.toString(data), (ClusterInfo[]) null);
                    cis.add(ci);
                }
            }
            return cis;
        } else {
            return this.getPeerClusters(clusterName);
        }
    }

    /**
     * Sets the table state in zookeeper.
     * 
     * @param tableName
     * @param state
     * @throws KeeperException
     */
    public void setTableState(String tableName, TableState state) throws KeeperException {
        ZKUtil.setData(this.zkw, getTableStateZNode(tableName), Bytes.toBytes(state.toString()));
    }

    /**
     * @param tableName
     * @return The state of the table stored in zookeeper.
     * @throws KeeperException
     * @throws TableNotFoundException
     */
    public TableState getTableState(String tableName) throws KeeperException, TableNotFoundException {
        byte[] data = ZKUtil.getData(this.zkw, getTableStateZNode(tableName));
        if (data == null) {
            throw new TableNotFoundException(tableName);
        }
        String str = Bytes.toString(data);
        try {
            return TableState.valueOf(str);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(str);
        }
    }

    /**
     * @param tableName
     * @return The state of the table stored in zookeeper.
     * @throws KeeperException
     */
    public TableState getTableStateAllowNull(String tableName) throws KeeperException {
        byte[] data = ZKUtil.getData(this.zkw, getTableStateZNode(tableName));
        if (data == null) {
            return null;
        }
        String str = Bytes.toString(data);
        try {
            return TableState.valueOf(str);
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(str);
        }
    }

    /**
     * Finds all the state of the cross site tables.
     * 
     * @return
     * @throws KeeperException
     * @throws TableNotFoundException
     */
    public Map<String, TableState> listTableStates() throws KeeperException, TableNotFoundException {
        Map<String, TableState> states = Collections.emptyMap();
        List<String> tableNames = this.listTables();
        if (!tableNames.isEmpty()) {
            states = new HashMap<String, TableState>();
            for (String tableName : tableNames) {
                states.put(tableName, getTableState(tableName));
            }
        }
        return states;
    }

    /**
     * Check whether table exists or not
     * 
     * @param tableName
     * @return true if table exists.
     * @throws KeeperException
     */
    public boolean isTableStateExist(String tableName) throws KeeperException {
        try {
            this.getTableState(tableName);
        } catch (TableNotFoundException e) {
            return false;
        }
        return true;
    }

    /**
     * Gets the znode path of the table state.
     * 
     * @param tableName
     * @return
     */
    public String getTableStateZNode(String tableName) {
        return ZKUtil.joinZNode(getTableZNode(tableName), TABLE_STATE_ZNODE_NAME);
    }

    /**
     * Locks the table by creating an ephemeral znode for a table.
     * 
     * @param tableName
     * @return
     * @throws KeeperException
     */
    public boolean lockTable(String tableName) throws KeeperException {
        boolean locked = ZKUtil.createEphemeralNodeAndWatch(zkw,
                ZKUtil.joinZNode(crossSiteZNode, tableName + EPHEMERAL), null);
        if (LOG.isDebugEnabled()) {
            LOG.debug(locked ? "Locked" : "Could not lock" + " the table " + tableName);
        }
        return locked;
    }

    /**
     * Unlocks the table by deleting the ephemeral znode.
     * 
     * @param tableName
     */
    public void unlockTable(String tableName) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Unlocking the table " + tableName);
        }
        try {
            ZKUtil.deleteNode(zkw, ZKUtil.joinZNode(crossSiteZNode, tableName + EPHEMERAL));
        } catch (KeeperException e) {
            LOG.warn("Fail to unlock the table " + tableName, e);
        }
    }

    /**
     * Locks the table by creating an ephemeral znode for a cluster.
     * 
     * @param clusterName
     * @return
     * @throws KeeperException
     */
    public boolean lockCluster(String clusterName) throws KeeperException {
        boolean locked = ZKUtil.createEphemeralNodeAndWatch(zkw,
                ZKUtil.joinZNode(crossSiteZNode, clusterName + CLUSTER_EPHEMERAL), null);
        if (LOG.isDebugEnabled()) {
            LOG.debug(locked ? "Locked" : "Could not lock" + " the cluster " + clusterName);
        }
        return locked;
    }

    /**
     * Unlocks the cluster by deleting the ephemeral znode.
     * 
     * @param clusterName
     */
    public void unlockCluster(String clusterName) {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Unlocking the cluster " + clusterName);
        }
        try {
            ZKUtil.deleteNode(zkw, ZKUtil.joinZNode(crossSiteZNode, clusterName + CLUSTER_EPHEMERAL));
        } catch (KeeperException e) {
            LOG.warn("Fail to unlock the cluster " + clusterName, e);
        }
    }

    /**
     * Gets the znode path of a table.
     * 
     * @param tableName
     * @return
     */
    public String getTableZNode(String tableName) {
        return ZKUtil.joinZNode(this.tablesZNode, tableName);
    }

    /**
     * Gets the znode path of the table descriptor.
     * 
     * @param tableName
     * @return
     */
    public String getTableDescZNode(String tableName) {
        return ZKUtil.joinZNode(getTableZNode(tableName), TABLE_DESC_ZNODE_NAME);
    }

    /**
     * Gets the znode path of the split keys.
     * 
     * @param tableName
     * @return
     */
    public String getTableSplitKeysZNode(String tableName) {
        return ZKUtil.joinZNode(getTableZNode(tableName), TABLE_SPLITKEYS_ZNODE_NAME);
    }

    /**
     * @param tableName
     * @return Initial split keys with which given table was created. Null when the table was created
     *         with out specifying any splits.
     * @throws KeeperException
     * @throws IOException
     */
    public byte[][] getTableSplitKeys(String tableName) throws KeeperException, IOException {
        byte[] data = ZKUtil.getData(zkw, getTableSplitKeysZNode(tableName));
        return CrossSiteUtil.readSplitKeys(data);
    }

    /**
     * Gets the znode path of the cluster locator.
     * 
     * @param tableName
     * @return
     */
    public String getClusterLocatorZNode(String tableName) {
        return ZKUtil.joinZNode(getTableZNode(tableName), CLUSTER_LOCATOR_ZNODE_NAME);
    }

    /**
     * Creates the znode of a table.
     * 
     * @param tableName
     * @throws KeeperException
     */
    public void createTableZNode(String tableName) throws KeeperException {
        ZKUtil.createWithParents(this.zkw, getTableZNode(tableName));
    }

    /**
     * Deletes the znode of a table.
     * 
     * @param tableName
     * @throws KeeperException
     */
    public void deleteTableZNode(String tableName) throws KeeperException {
        ZKUtil.deleteNodeRecursively(this.zkw, getTableZNode(tableName));
    }

    /**
     * Modify the znode data of the table descriptor.
     * 
     * @param tableName
     * @param desc
     * @throws KeeperException
     * @throws IOException
     */
    public void modifyTableDesc(String tableName, HTableDescriptor desc) throws KeeperException, IOException {
        // Do table existence check
        getTableState(tableName);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        DataOutput out = new DataOutputStream(stream);
        desc.write(out);
        ZKUtil.setData(this.zkw, getTableDescZNode(tableName), stream.toByteArray());
        stream.close();
    }

    /**
     * Write the znode data of the new proposed table descriptor.
     * 
     * @param tableName
     * @param desc
     * @throws KeeperException
     * @throws IOException
     */
    public void writeProposedTableDesc(String tableName, HTableDescriptor desc)
            throws KeeperException, IOException {
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        DataOutput out = new DataOutputStream(stream);
        desc.write(out);
        // The node should be created here
        ZKUtil.createSetData(this.zkw, ZKUtil.joinZNode(getTableZNode(tableName), TABLE_PROPOSED_DESC_ZNODE_NAME),
                stream.toByteArray());
        stream.close();
    }

    /**
     * Deletes the proposed HTD node from zk.
     * 
     * @param tableName
     * @throws KeeperException
     * @throws IOException
     */
    public void deleteProposedTableDesc(String tableName) throws KeeperException, IOException {
        ZKUtil.deleteNode(zkw, ZKUtil.joinZNode(getTableZNode(tableName), TABLE_PROPOSED_DESC_ZNODE_NAME));
    }

    /**
     * Gets proposed HTD written to zk.
     * 
     * @param tableName
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public HTableDescriptor getProposedTableDesc(String tableName) throws KeeperException, IOException {
        byte[] data = ZKUtil.getData(zkw,
                ZKUtil.joinZNode(getTableZNode(tableName), TABLE_PROPOSED_DESC_ZNODE_NAME));
        if (data == null)
            return null;
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
        DataInput in = new DataInputStream(stream);
        HTableDescriptor htd = new HTableDescriptor();
        htd.readFields(in);
        stream.close();
        return htd;
    }

    /**
     * Gets the table descriptor for the table descriptor znode.
     * 
     * @param tableName
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public HTableDescriptor getTableDesc(String tableName) throws KeeperException, IOException {
        // Do table existence check
        getTableState(tableName);
        byte[] data = ZKUtil.getData(this.zkw, getTableDescZNode(tableName));
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
        DataInput in = new DataInputStream(stream);
        HTableDescriptor htd = new HTableDescriptor();
        htd.readFields(in);
        stream.close();
        return htd;
    }

    /**
     * Gets the table descriptor for the table descriptor znode.
     * 
     * @param tableName
     * @return null if the table is not existent
     * @throws KeeperException
     * @throws IOException
     */
    public HTableDescriptor getTableDescAllowNull(String tableName) throws KeeperException, IOException {
        // Do table existence check
        byte[] stateData = ZKUtil.getData(this.zkw, getTableStateZNode(tableName));
        if (stateData == null) {
            return null;
        }
        byte[] data = ZKUtil.getData(this.zkw, getTableDescZNode(tableName));
        ByteArrayInputStream stream = new ByteArrayInputStream(data);
        DataInput in = new DataInputStream(stream);
        HTableDescriptor htd = new HTableDescriptor();
        htd.readFields(in);
        stream.close();
        return htd;
    }

    /**
     * Lists the descriptors of all the cross site tables.
     * 
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public HTableDescriptor[] listTableDescs() throws KeeperException, IOException {
        return listTableDescs((Pattern) null);
    }

    /**
     * Lists the descriptors of all the cross site tables with the pattern.
     * 
     * @param pattern
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public HTableDescriptor[] listTableDescs(Pattern pattern) throws KeeperException, IOException {
        List<HTableDescriptor> htds = Collections.emptyList();
        List<String> tableNames = listTables();
        if (tableNames != null && !tableNames.isEmpty()) {
            htds = new ArrayList<HTableDescriptor>();
            for (String tableName : tableNames) {
                if (pattern == null || pattern.matcher(tableName).matches()) {
                    byte[] data = ZKUtil.getData(this.zkw, getTableDescZNode(tableName));
                    if (data != null) {
                        ByteArrayInputStream stream = new ByteArrayInputStream(data);
                        DataInput in = new DataInputStream(stream);
                        HTableDescriptor htd = new HTableDescriptor();
                        htd.readFields(in);
                        stream.close();
                        htds.add(htd);
                    }
                }
            }
        }
        return htds.toArray(new HTableDescriptor[0]);
    }

    /**
     * Lists the descriptors of the cross site tables.
     * 
     * @param tableNames
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public HTableDescriptor[] listTableDescs(List<String> tableNames) throws KeeperException, IOException {
        List<HTableDescriptor> htds = Collections.emptyList();
        if (tableNames != null && !tableNames.isEmpty()) {
            htds = new ArrayList<HTableDescriptor>();
            for (String tableName : tableNames) {
                byte[] data = ZKUtil.getData(this.zkw, getTableDescZNode(tableName));
                if (data != null) {
                    ByteArrayInputStream stream = new ByteArrayInputStream(data);
                    DataInput in = new DataInputStream(stream);
                    HTableDescriptor htd = new HTableDescriptor();
                    htd.readFields(in);
                    stream.close();
                    htds.add(htd);
                }
            }
        }
        return htds.toArray(new HTableDescriptor[0]);
    }

    /**
     * Lists all the cross site tables.
     * 
     * @return
     * @throws KeeperException
     */
    public List<String> listTables() throws KeeperException {
        List<String> emptyResult = Collections.emptyList();
        List<String> result = ZKUtil.listChildrenNoWatch(zkw, tablesZNode);
        return result == null ? emptyResult : result;
    }

    /**
     * Gets all the cross site tables.
     * 
     * @return an array of the table names.
     * @throws KeeperException
     */
    public String[] getTableNames() throws KeeperException {
        List<String> results = Collections.emptyList();
        List<String> tableNames = ZKUtil.listChildrenNoWatch(zkw, this.tablesZNode);
        if (tableNames != null && !tableNames.isEmpty()) {
            results = new ArrayList<String>();
            for (String tableName : tableNames) {
                byte[] data = ZKUtil.getData(this.zkw, getTableDescZNode(tableName));
                if (data != null) {
                    results.add(tableName);
                }
            }
        }
        return results.toArray(new String[0]);
    }

    /**
     * Gets the table names of all the cross site tables with the pattern.
     * 
     * @param pattern
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public String[] getTableNames(Pattern pattern) throws KeeperException, IOException {
        List<String> results = Collections.emptyList();
        List<String> tableNames = ZKUtil.listChildrenNoWatch(zkw, this.tablesZNode);
        if (tableNames != null && !tableNames.isEmpty()) {
            results = new ArrayList<String>();
            for (String tableName : tableNames) {
                if (pattern.matcher(tableName).matches()) {
                    byte[] data = ZKUtil.getData(this.zkw, getTableDescZNode(tableName));
                    if (data != null) {
                        results.add(tableName);
                    }
                }
            }
        }
        return results.toArray(new String[0]);
    }

    /**
     * Gets the znode data of the cluster locator.
     * 
     * @param tableName
     * @return
     * @throws KeeperException
     * @throws IOException
     */
    public byte[] getClusterLocatorData(String tableName) throws KeeperException, IOException {
        // Do table existence check
        getTableState(tableName);
        return ZKUtil.getData(this.zkw, getClusterLocatorZNode(tableName));
    }

    /**
     * Gets a cluster locator.
     * 
     * @param tableName
     * @return
     * @throws IOException
     * @throws KeeperException
     */
    public ClusterLocator getClusterLocator(String tableName) throws IOException, KeeperException {
        byte[] clusterLocatorData = getClusterLocatorData(tableName);
        ByteArrayInputStream stream = null;
        try {
            stream = new ByteArrayInputStream(clusterLocatorData);
            DataInput in = new DataInputStream(stream);
            ClusterLocatorRPCObject locator = new ClusterLocatorRPCObject();
            locator.readFields(in);
            return locator.getClusterLocator();
        } finally {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    LOG.warn("Fail to close the stream of reading cluster locator", e);
                }
            }
        }
    }

    /**
     * Checks whether the cluster is existent.
     * 
     * @param clusterName
     * @return
     * @throws KeeperException
     */
    public boolean clusterExists(String clusterName) throws KeeperException {
        return ZKUtil.checkExists(this.zkw, getClusterZNode(clusterName)) >= 0;
    }

    /**
     * Gets the cluster address.
     * 
     * @param clusterName
     * @return
     * @throws KeeperException
     */
    public String getClusterAddress(String clusterName) throws KeeperException {
        if (!clusterExists(clusterName)) {
            throw new IllegalArgumentException("The cluster " + clusterName + " is not found");
        }
        byte[] data = ZKUtil.getData(this.zkw, getClusterAddressZNode(clusterName));
        return Bytes.toString(data);
    }

    /**
     * Lists the clusters.
     * 
     * @return
     * @throws KeeperException
     */
    public List<String> listClusters() throws KeeperException {
        List<String> empty = Collections.emptyList();
        List<String> result = ZKUtil.listChildrenNoWatch(this.zkw, this.clustersZNode);
        return result == null ? empty : result;
    }

    /**
     * Gets the map of the hierarchy.
     * 
     * @return
     * @throws KeeperException
     */
    public Map<String, Set<String>> getHierarchyMap() throws KeeperException {
        Map<String, Set<String>> hierarchy = new TreeMap<String, Set<String>>();
        List<String> parents = ZKUtil.listChildrenNoWatch(this.zkw, this.hierarchyZNode);
        if (parents != null) {
            for (String parent : parents) {
                List<String> children = ZKUtil.listChildrenNoWatch(this.zkw,
                        ZKUtil.joinZNode(this.hierarchyZNode, parent));
                if (children != null) {
                    TreeSet<String> childrenSet = new TreeSet<String>();
                    childrenSet.addAll(children);
                    hierarchy.put(parent, childrenSet);
                }
            }
        }
        return hierarchy;
    }

    /**
     * Checks whether the table is existent.
     * 
     * @param tableName
     * @return true if this table is existent.
     * @throws KeeperException
     */
    public boolean isTableExists(String tableName) throws KeeperException {
        return (ZKUtil.checkExists(zkw, getTableZNode(tableName)) >= 0);
    }

    /**
     * Checks whether the peer is existent.
     * 
     * @param peer
     * @return
     * @throws KeeperException
     */
    public boolean isPeerExists(String clusterName, String peer) throws KeeperException {
        return (ZKUtil.checkExists(zkw, ZKUtil.joinZNode(getClusterPeersZNode(clusterName), peer)) >= 0);
    }
}