You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by st...@apache.org on 2010/09/01 01:51:50 UTC

svn commit: r991397 [2/15] - in /hbase/trunk: ./ bin/ conf/ src/main/java/org/apache/hadoop/hbase/ src/main/java/org/apache/hadoop/hbase/avro/ src/main/java/org/apache/hadoop/hbase/catalog/ src/main/java/org/apache/hadoop/hbase/client/ src/main/java/or...

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/CatalogTracker.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,395 @@
+/**
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.catalog;
+
+import java.io.IOException;
+import java.net.ConnectException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.HServerInfo;
+import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
+import org.apache.hadoop.hbase.NotServingRegionException;
+import org.apache.hadoop.hbase.client.RetriesExhaustedException;
+import org.apache.hadoop.hbase.client.ServerConnection;
+import org.apache.hadoop.hbase.ipc.HRegionInterface;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.hadoop.hbase.zookeeper.MetaNodeTracker;
+import org.apache.hadoop.hbase.zookeeper.RootRegionTracker;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
+
+/**
+ * Tracks the availability of the catalog tables <code>-ROOT-</code> and
+ * <code>.META.</code>.
+ * <p>
+ * This class is "read-only" in that the locations of the catalog tables cannot
+ * be explicitly set.  Instead, ZooKeeper is used to learn of the availability
+ * and location of ROOT.  ROOT is used to learn of the location of META.  If not
+ * available in ROOT, ZooKeeper is used to monitor for a new location of META.
+ */
+public class CatalogTracker {
+  private static final Log LOG = LogFactory.getLog(CatalogTracker.class);
+
+  private final ServerConnection connection;
+
+  private final ZooKeeperWatcher zookeeper;
+
+  private final RootRegionTracker rootRegionTracker;
+
+  private final MetaNodeTracker metaNodeTracker;
+
+  private final AtomicBoolean metaAvailable = new AtomicBoolean(false);
+  private HServerAddress metaLocation;
+
+  private final int defaultTimeout;
+
+  public static final byte [] ROOT_REGION =
+    HRegionInfo.ROOT_REGIONINFO.getRegionName();
+
+  public static final byte [] META_REGION =
+    HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
+
+  /**
+   * Constructs the catalog tracker.  Find current state of catalog tables and
+   * begin active tracking by executing {@link #start()}.
+   * @param zk
+   * @param connection server connection
+   * @param abortable if fatal exception
+   * @throws IOException 
+   */
+  public CatalogTracker(final ZooKeeperWatcher zk,
+      final ServerConnection connection, final Abortable abortable,
+      final int defaultTimeout)
+  throws IOException {
+    this.zookeeper = zk;
+    this.connection = connection;
+    this.rootRegionTracker = new RootRegionTracker(zookeeper, abortable);
+    this.metaNodeTracker = new MetaNodeTracker(zookeeper, this);
+    this.defaultTimeout = defaultTimeout;
+  }
+
+  /**
+   * Starts the catalog tracker.
+   * <p>
+   * Determines current availability of catalog tables and ensures all further
+   * transitions of either region is tracked.
+   * @throws IOException
+   * @throws InterruptedException 
+   */
+  public void start() throws IOException, InterruptedException {
+    // Register listeners with zk
+    zookeeper.registerListener(rootRegionTracker);
+    zookeeper.registerListener(metaNodeTracker);
+    // Start root tracking
+    rootRegionTracker.start();
+    // Determine meta assignment; may not work because root and meta not yet
+    // deployed.
+    getMetaServerConnection(true);
+  }
+
+  /**
+   * Gets the current location for <code>-ROOT-</code> or null if location is
+   * not currently available.
+   * @return location of root, null if not available
+   * @throws InterruptedException 
+   */
+  public HServerAddress getRootLocation() throws InterruptedException {
+    return this.rootRegionTracker.getRootRegionLocation();
+  }
+
+  /**
+   * @return Location of meta or null if not yet available.
+   */
+  public HServerAddress getMetaLocation() {
+    return this.metaLocation;
+  }
+
+  /**
+   * Waits indefinitely for availability of <code>-ROOT-</code>.  Used during
+   * cluster startup.
+   * @throws InterruptedException if interrupted while waiting
+   */
+  public void waitForRoot()
+  throws InterruptedException {
+    rootRegionTracker.getRootRegionLocation();
+  }
+
+  /**
+   * Gets the current location for <code>-ROOT-</code> if available and waits
+   * for up to the specified timeout if not immediately available.  Returns null
+   * if the timeout elapses before root is available.
+   * @param timeout maximum time to wait for root availability, in milliseconds
+   * @return location of root
+   * @throws InterruptedException if interrupted while waiting
+   * @throws NotAllMetaRegionsOnlineException if root not available before
+   *                                          timeout
+   */
+  public HServerAddress waitForRoot(final long timeout)
+  throws InterruptedException, NotAllMetaRegionsOnlineException {
+    HServerAddress address = rootRegionTracker.waitRootRegionLocation(timeout);
+    if (address == null) {
+      throw new NotAllMetaRegionsOnlineException("Timed out; " + timeout + "ms");
+    }
+    return address;
+  }
+
+  /**
+   * Gets a connection to the server hosting root, as reported by ZooKeeper,
+   * waiting up to the specified timeout for availability.
+   * @see #waitForRoot(long) for additional information
+   * @return connection to server hosting root
+   * @throws InterruptedException
+   * @throws NotAllMetaRegionsOnlineException if timed out waiting
+   * @throws IOException
+   */
+  public HRegionInterface waitForRootServerConnection(long timeout)
+  throws InterruptedException, NotAllMetaRegionsOnlineException, IOException {
+    return getCachedConnection(waitForRoot(timeout));
+  }
+
+  /**
+   * Gets a connection to the server hosting root, as reported by ZooKeeper,
+   * waiting for the default timeout specified on instantiation.
+   * @see #waitForRoot(long) for additional information
+   * @return connection to server hosting root
+   * @throws NotAllMetaRegionsOnlineException if timed out waiting
+   * @throws IOException
+   */
+  public HRegionInterface waitForRootServerConnectionDefault()
+  throws NotAllMetaRegionsOnlineException, IOException {
+    try {
+      return getCachedConnection(waitForRoot(defaultTimeout));
+    } catch (InterruptedException e) {
+      throw new NotAllMetaRegionsOnlineException("Interrupted");
+    }
+  }
+
+  /**
+   * Gets a connection to the server hosting root, as reported by ZooKeeper,
+   * if available.  Returns null if no location is immediately available.
+   * @return connection to server hosting root, null if not available
+   * @throws IOException
+   * @throws InterruptedException 
+   */
+  private HRegionInterface getRootServerConnection()
+  throws IOException, InterruptedException {
+    HServerAddress address = rootRegionTracker.getRootRegionLocation();
+    if (address == null) {
+      return null;
+    }
+    return getCachedConnection(address);
+  }
+
+  /**
+   * Gets a connection to the server currently hosting <code>.META.</code> or
+   * null if location is not currently available.
+   * <p>
+   * If a location is known, a connection to the cached location is returned.
+   * If refresh is true, the cached connection is verified first before
+   * returning.  If the connection is not valid, it is reset and rechecked.
+   * <p>
+   * If no location for meta is currently known, method checks ROOT for a new
+   * location, verifies META is currently there, and returns a cached connection
+   * to the server hosting META.
+   *
+   * @return connection to server hosting meta, null if location not available
+   * @throws IOException
+   * @throws InterruptedException 
+   */
+  private HRegionInterface getMetaServerConnection(boolean refresh)
+  throws IOException, InterruptedException {
+    synchronized(metaAvailable) {
+      if(metaAvailable.get()) {
+        HRegionInterface current = getCachedConnection(metaLocation);
+        if(!refresh) {
+          return current;
+        }
+        if(verifyRegionLocation(current, META_REGION)) {
+          return current;
+        }
+        resetMetaLocation();
+      }
+      HRegionInterface rootConnection = getRootServerConnection();
+      if(rootConnection == null) {
+        return null;
+      }
+      HServerAddress newLocation = MetaReader.readMetaLocation(rootConnection);
+      if(newLocation == null) {
+        return null;
+      }
+      HRegionInterface newConnection = getCachedConnection(newLocation);
+      if(verifyRegionLocation(newConnection, META_REGION)) {
+        setMetaLocation(newLocation);
+        return newConnection;
+      }
+      return null;
+    }
+  }
+
+  /**
+   * Waits indefinitely for availability of <code>.META.</code>.  Used during
+   * cluster startup.
+   * @throws InterruptedException if interrupted while waiting
+   */
+  public void waitForMeta() throws InterruptedException {
+    synchronized(metaAvailable) {
+      while(!metaAvailable.get()) {
+        metaAvailable.wait();
+      }
+    }
+  }
+
+  /**
+   * Gets the current location for <code>.META.</code> if available and waits
+   * for up to the specified timeout if not immediately available.  Throws an
+   * exception if timed out waiting.
+   * @param timeout maximum time to wait for meta availability, in milliseconds
+   * @return location of meta
+   * @throws InterruptedException if interrupted while waiting
+   * @throws IOException unexpected exception connecting to meta server
+   * @throws NotAllMetaRegionsOnlineException if meta not available before
+   *                                          timeout
+   */
+  public HServerAddress waitForMeta(long timeout)
+  throws InterruptedException, IOException, NotAllMetaRegionsOnlineException {
+    long stop = System.currentTimeMillis() + timeout;
+    synchronized(metaAvailable) {
+      if(getMetaServerConnection(true) != null) {
+        return metaLocation;
+      }
+      while(!metaAvailable.get() &&
+          (timeout == 0 || System.currentTimeMillis() < stop)) {
+        metaAvailable.wait(timeout);
+      }
+      if(getMetaServerConnection(true) == null) {
+        throw new NotAllMetaRegionsOnlineException(
+            "Timed out (" + timeout + "ms");
+      }
+      return metaLocation;
+    }
+  }
+
+  /**
+   * Gets a connection to the server hosting meta, as reported by ZooKeeper,
+   * waiting up to the specified timeout for availability.
+   * @see #waitForMeta(long) for additional information
+   * @return connection to server hosting meta
+   * @throws InterruptedException
+   * @throws NotAllMetaRegionsOnlineException if timed out waiting
+   * @throws IOException
+   */
+  public HRegionInterface waitForMetaServerConnection(long timeout)
+  throws InterruptedException, NotAllMetaRegionsOnlineException, IOException {
+    return getCachedConnection(waitForMeta(timeout));
+  }
+
+  /**
+   * Gets a connection to the server hosting meta, as reported by ZooKeeper,
+   * waiting up to the specified timeout for availability.
+   * @see #waitForMeta(long) for additional information
+   * @return connection to server hosting meta
+   * @throws NotAllMetaRegionsOnlineException if timed out or interrupted
+   * @throws IOException
+   */
+  public HRegionInterface waitForMetaServerConnectionDefault()
+  throws NotAllMetaRegionsOnlineException, IOException {
+    try {
+      return getCachedConnection(waitForMeta(defaultTimeout));
+    } catch (InterruptedException e) {
+      throw new NotAllMetaRegionsOnlineException("Interrupted");
+    }
+  }
+
+  private void resetMetaLocation() {
+    LOG.info("Current cached META location is not valid, resetting");
+    metaAvailable.set(false);
+    metaLocation = null;
+  }
+
+  private void setMetaLocation(HServerAddress metaLocation) {
+    LOG.info("Found new META location, " + metaLocation);
+    metaAvailable.set(true);
+    this.metaLocation = metaLocation;
+    // no synchronization because these are private and already under lock
+    metaAvailable.notifyAll();
+  }
+
+  private HRegionInterface getCachedConnection(HServerAddress address)
+  throws IOException {
+    HRegionInterface protocol = null;
+    try {
+      protocol = connection.getHRegionConnection(address, false);
+    } catch (RetriesExhaustedException e) {
+      if (e.getCause() != null && e.getCause() instanceof ConnectException) {
+        // Catch this; presume it means the cached connection has gone bad.
+      } else {
+        throw e;
+      }
+    }
+    return protocol;
+  }
+
+  private boolean verifyRegionLocation(HRegionInterface metaServer,
+      byte [] regionName) {
+    try {
+      return metaServer.getRegionInfo(regionName) != null;
+    } catch (NotServingRegionException e) {
+      return false;
+    }
+  }
+
+  /**
+   * Check if <code>hsi</code> was carrying <code>-ROOT-</code> or
+   * <code>.META.</code> and if so, clear out old locations.
+   * @param hsi Server that has crashed/shutdown.
+   * @throws InterruptedException
+   * @throws KeeperException
+   * @return Pair of booleans; if this server was carrying root, then first
+   * boolean is set, if server was carrying meta, then second boolean set.
+   */
+  public Pair<Boolean, Boolean> processServerShutdown(final HServerInfo hsi)
+  throws InterruptedException, KeeperException {
+    Pair<Boolean, Boolean> result = new Pair<Boolean, Boolean>(false, false);
+    HServerAddress rootHsa = getRootLocation();
+    if (rootHsa == null) {
+      LOG.info("-ROOT- is not assigned; continuing");
+    } else if (hsi.getServerAddress().equals(rootHsa)) {
+      result.setFirst(true);
+      LOG.info(hsi.getServerName() + " carrying -ROOT-; deleting " +
+        "-ROOT- location from meta");
+      RootLocationEditor.deleteRootLocation(this.zookeeper);
+    }
+    HServerAddress metaHsa = getMetaLocation();
+    if (metaHsa == null) {
+      LOG.info(".META. is not assigned; continuing");
+    } else if (hsi.getServerAddress().equals(metaHsa)) {
+      LOG.info(hsi.getServerName() + " carrying .META.; unsetting " +
+        ".META. location");
+      result.setSecond(true);
+      resetMetaLocation();
+    }
+    return result;
+  }
+}
\ No newline at end of file

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaEditor.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,215 @@
+/**
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.catalog;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HServerInfo;
+import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
+import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.ipc.HRegionInterface;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Writables;
+
+/**
+ * Writes region and assignment information to <code>.META.</code>.
+ * <p>
+ * Uses the {@link CatalogTracker} to obtain locations and connections to
+ * catalogs.
+ */
+public class MetaEditor {
+  private static final Log LOG = LogFactory.getLog(MetaEditor.class);
+
+  /**
+   * Adds a META row for the specified new region.
+   * @param info region information
+   * @throws IOException if problem connecting or updating meta
+   */
+  public static void addRegionToMeta(CatalogTracker catalogTracker,
+      HRegionInfo regionInfo)
+  throws IOException {
+    Put put = new Put(regionInfo.getRegionName());
+    put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
+        Writables.getBytes(regionInfo));
+    catalogTracker.waitForMetaServerConnectionDefault().put(
+        CatalogTracker.META_REGION, put);
+    LOG.info("Added region " + regionInfo.getRegionNameAsString() + " to META");
+  }
+
+  /**
+   * Offline parent in meta.
+   * Used when splitting.
+   * @param catalogTracker
+   * @param parent
+   * @param a Split daughter region A
+   * @param b Split daughter region B
+   * @throws NotAllMetaRegionsOnlineException
+   * @throws IOException
+   */
+  public static void offlineParentInMeta(CatalogTracker catalogTracker,
+      HRegionInfo parent, final HRegionInfo a, final HRegionInfo b)
+  throws NotAllMetaRegionsOnlineException, IOException {
+    HRegionInfo copyOfParent = new HRegionInfo(parent);
+    copyOfParent.setOffline(true);
+    copyOfParent.setSplit(true);
+    Put put = new Put(copyOfParent.getRegionName());
+    addRegionInfo(put, copyOfParent);
+    put.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
+        HConstants.EMPTY_BYTE_ARRAY);
+    put.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
+        HConstants.EMPTY_BYTE_ARRAY);
+    put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER,
+      Writables.getBytes(a));
+    put.add(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER,
+      Writables.getBytes(b));
+    catalogTracker.waitForMetaServerConnectionDefault().put(CatalogTracker.META_REGION, put);
+    LOG.info("Offlined parent region " + parent.getRegionNameAsString() +
+      " in META");
+  }
+
+  public static void addDaughter(final CatalogTracker catalogTracker,
+      final HRegionInfo regionInfo, final HServerInfo serverInfo)
+  throws NotAllMetaRegionsOnlineException, IOException {
+    HRegionInterface server = catalogTracker.waitForMetaServerConnectionDefault();
+    byte [] catalogRegionName = CatalogTracker.META_REGION;
+    Put put = new Put(regionInfo.getRegionName());
+    addRegionInfo(put, regionInfo);
+    if (serverInfo != null) addLocation(put, serverInfo);
+    server.put(catalogRegionName, put);
+    LOG.info("Added daughter " + regionInfo.getRegionNameAsString() +
+      " in region " + Bytes.toString(catalogRegionName) + " with " +
+      "server=" + serverInfo.getHostnamePort() + ", " +
+      "startcode=" + serverInfo.getStartCode());
+  }
+
+  /**
+   * Updates the location of the specified META region in ROOT to be the
+   * specified server hostname and startcode.
+   * <p>
+   * Uses passed catalog tracker to get a connection to the server hosting
+   * ROOT and makes edits to that region.
+   *
+   * @param catalogTracker catalog tracker
+   * @param regionInfo region to update location of
+   * @param serverInfo server the region is located on
+   * @throws IOException
+   */
+  public static void updateMetaLocation(CatalogTracker catalogTracker,
+      HRegionInfo regionInfo, HServerInfo serverInfo)
+  throws IOException {
+    HRegionInterface server = catalogTracker.waitForRootServerConnectionDefault();
+    updateLocation(server, CatalogTracker.ROOT_REGION, regionInfo, serverInfo);
+  }
+
+  /**
+   * Updates the location of the specified region in META to be the specified
+   * server hostname and startcode.
+   * <p>
+   * Uses passed catalog tracker to get a connection to the server hosting
+   * META and makes edits to that region.
+   *
+   * @param catalogTracker catalog tracker
+   * @param regionInfo region to update location of
+   * @param serverInfo server the region is located on
+   * @throws IOException
+   */
+  public static void updateRegionLocation(CatalogTracker catalogTracker,
+      HRegionInfo regionInfo, HServerInfo serverInfo)
+  throws IOException {
+    updateLocation(catalogTracker.waitForMetaServerConnectionDefault(),
+        CatalogTracker.META_REGION, regionInfo, serverInfo);
+  }
+
+  /**
+   * Updates the location of the specified region to be the specified server.
+   * <p>
+   * Connects to the specified server which should be hosting the specified
+   * catalog region name to perform the edit.
+   *
+   * @param server connection to server hosting catalog region
+   * @param catalogRegionName name of catalog region being updated
+   * @param regionInfo region to update location of
+   * @param serverInfo server the region is located on
+   * @throws IOException
+   */
+  public static void updateLocation(HRegionInterface server,
+      byte [] catalogRegionName, HRegionInfo regionInfo, HServerInfo serverInfo)
+  throws IOException {
+    Put put = new Put(regionInfo.getRegionName());
+    addLocation(put, serverInfo);
+    server.put(catalogRegionName, put);
+    LOG.info("Updated row " + regionInfo.getRegionNameAsString() +
+      " in region " + Bytes.toString(catalogRegionName) + " with " +
+      "server=" + serverInfo.getHostnamePort() + ", " +
+      "startcode=" + serverInfo.getStartCode());
+  }
+
+  /**
+   * Deletes the specified region from META.
+   * @param catalogTracker
+   * @param regionInfo region to be deleted from META
+   * @throws IOException
+   */
+  public static void deleteRegion(CatalogTracker catalogTracker,
+      HRegionInfo regionInfo)
+  throws IOException {
+    Delete delete = new Delete(regionInfo.getRegionName());
+    catalogTracker.waitForMetaServerConnectionDefault().delete(
+        CatalogTracker.META_REGION, delete);
+
+    LOG.info("Deleted region " + regionInfo.getRegionNameAsString() + " from META");
+  }
+
+  /**
+   * Updates the region information for the specified region in META.
+   * @param catalogTracker
+   * @param regionInfo region to be updated in META
+   * @throws IOException
+   */
+  public static void updateRegionInfo(CatalogTracker catalogTracker,
+      HRegionInfo regionInfo)
+  throws IOException {
+    Put put = new Put(regionInfo.getRegionName());
+    addRegionInfo(put, regionInfo);
+    catalogTracker.waitForMetaServerConnectionDefault().put(
+        CatalogTracker.META_REGION, put);
+    LOG.info("Updated region " + regionInfo.getRegionNameAsString() + " in META");
+  }
+
+  private static Put addRegionInfo(final Put p, final HRegionInfo hri)
+  throws IOException {
+    p.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
+        Writables.getBytes(hri));
+    return p;
+  }
+
+  private static Put addLocation(final Put p, final HServerInfo hsi) {
+    p.add(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
+      Bytes.toBytes(hsi.getHostnamePort()));
+    p.add(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
+      Bytes.toBytes(hsi.getStartCode()));
+    return p;
+  }
+}
\ No newline at end of file

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/MetaReader.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,309 @@
+/**
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.catalog;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+import java.util.NavigableSet;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.HServerInfo;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.ipc.HRegionInterface;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.Pair;
+import org.apache.hadoop.hbase.util.Writables;
+
+/**
+ * Reads region and assignment information from <code>.META.</code>.
+ * <p>
+ * Uses the {@link CatalogTracker} to obtain locations and connections to
+ * catalogs.
+ */
+public class MetaReader {
+  /**
+   * Performs a full scan of <code>.META.</code>.
+   * <p>
+   * Returns a map of every region to it's currently assigned server, according
+   * to META.  If the region does not have an assignment it will have a null
+   * value in the map.
+   *
+   * @return map of regions to their currently assigned server
+   * @throws IOException
+   */
+  public static Map<HRegionInfo,HServerAddress> fullScan(
+      CatalogTracker catalogTracker)
+  throws IOException {
+    HRegionInterface metaServer =
+      catalogTracker.waitForMetaServerConnectionDefault();
+    Map<HRegionInfo,HServerAddress> allRegions =
+      new TreeMap<HRegionInfo,HServerAddress>();
+    Scan scan = new Scan();
+    scan.addFamily(HConstants.CATALOG_FAMILY);
+    long scannerid = metaServer.openScanner(
+        HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
+    try {
+      Result data;
+      while((data = metaServer.next(scannerid)) != null) {
+        if (!data.isEmpty()) {
+          Pair<HRegionInfo,HServerAddress> region =
+            metaRowToRegionPair(data);
+          allRegions.put(region.getFirst(), region.getSecond());
+        }
+      }
+    } finally {
+      metaServer.close(scannerid);
+    }
+    return allRegions;
+  }
+
+  /**
+   * Reads the location of META from ROOT.
+   * @param metaServer connection to server hosting ROOT
+   * @return location of META in ROOT, null if not available
+   * @throws IOException
+   */
+  public static HServerAddress readMetaLocation(HRegionInterface metaServer)
+  throws IOException {
+    return readLocation(metaServer, CatalogTracker.ROOT_REGION,
+        CatalogTracker.META_REGION);
+  }
+
+  /**
+   * Reads the location of the specified region from META.
+   * @param catalogTracker
+   * @param regionName region to read location of
+   * @return location of region in META, null if not available
+   * @throws IOException
+   */
+  public static HServerAddress readRegionLocation(CatalogTracker catalogTracker,
+      byte [] regionName)
+  throws IOException {
+    return readLocation(catalogTracker.waitForMetaServerConnectionDefault(),
+        CatalogTracker.META_REGION, regionName);
+  }
+
+  private static HServerAddress readLocation(HRegionInterface metaServer,
+      byte [] catalogRegionName, byte [] regionName)
+  throws IOException {
+    Result r = null;
+    try {
+      r = metaServer.get(catalogRegionName,
+        new Get(regionName).addColumn(HConstants.CATALOG_FAMILY,
+        HConstants.SERVER_QUALIFIER));
+    } catch (java.net.ConnectException e) {
+      if (e.getMessage() != null &&
+          e.getMessage().contains("Connection refused")) {
+        // Treat this exception + message as unavailable catalog table. Catch it
+        // and fall through to return a null
+      } else {
+        throw e;
+      }
+    } catch (IOException e) {
+      if (e.getCause() != null && e.getCause() instanceof IOException &&
+          e.getCause().getMessage() != null &&
+          e.getCause().getMessage().contains("Connection reset by peer")) {
+        // Treat this exception + message as unavailable catalog table. Catch it
+        // and fall through to return a null
+      } else {
+        throw e;
+      }
+    }
+    if (r == null || r.isEmpty()) {
+      return null;
+    }
+    byte [] value = r.getValue(HConstants.CATALOG_FAMILY,
+      HConstants.SERVER_QUALIFIER);
+    return new HServerAddress(Bytes.toString(value));
+  }
+
+  /**
+   * Gets the region info and assignment for the specified region from META.
+   * @param catalogTracker
+   * @param regionName
+   * @return region info and assignment from META, null if not available
+   * @throws IOException
+   */
+  public static Pair<HRegionInfo, HServerAddress> getRegion(
+      CatalogTracker catalogTracker, byte [] regionName)
+  throws IOException {
+    Get get = new Get(regionName);
+    get.addFamily(HConstants.CATALOG_FAMILY);
+    Result r = catalogTracker.waitForMetaServerConnectionDefault().get(
+        CatalogTracker.META_REGION, get);
+    if(r == null || r.isEmpty()) {
+      return null;
+    }
+    return metaRowToRegionPair(r);
+  }
+
+  public static Pair<HRegionInfo, HServerAddress> metaRowToRegionPair(
+      Result data) throws IOException {
+    HRegionInfo info = Writables.getHRegionInfo(
+      data.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER));
+    final byte[] value = data.getValue(HConstants.CATALOG_FAMILY,
+      HConstants.SERVER_QUALIFIER);
+    if (value != null && value.length > 0) {
+      HServerAddress server = new HServerAddress(Bytes.toString(value));
+      return new Pair<HRegionInfo,HServerAddress>(info, server);
+    } else {
+      return new Pair<HRegionInfo, HServerAddress>(info, null);
+    }
+  }
+
+  /**
+   * Checks if the specified table exists.  Looks at the META table hosted on
+   * the specified server.
+   * @param metaServer server hosting meta
+   * @param tableName table to check
+   * @return true if the table exists in meta, false if not
+   * @throws IOException
+   */
+  public static boolean tableExists(CatalogTracker catalogTracker,
+      String tableName)
+  throws IOException {
+    HRegionInterface metaServer =
+      catalogTracker.waitForMetaServerConnectionDefault();
+    byte[] firstRowInTable = Bytes.toBytes(tableName + ",,");
+    Scan scan = new Scan(firstRowInTable);
+    scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
+    long scannerid = metaServer.openScanner(
+        HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
+    try {
+      Result data = metaServer.next(scannerid);
+      if (data != null && data.size() > 0) {
+        HRegionInfo info = Writables.getHRegionInfo(
+          data.getValue(HConstants.CATALOG_FAMILY,
+              HConstants.REGIONINFO_QUALIFIER));
+        if (info.getTableDesc().getNameAsString().equals(tableName)) {
+          // A region for this table already exists. Ergo table exists.
+          return true;
+        }
+      }
+      return false;
+    } finally {
+      metaServer.close(scannerid);
+    }
+  }
+
+  /**
+   * Gets all of the regions of the specified table from META.
+   * @param catalogTracker
+   * @param tableName
+   * @return
+   * @throws IOException
+   */
+  public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
+      byte [] tableName)
+  throws IOException {
+    HRegionInterface metaServer =
+      catalogTracker.waitForMetaServerConnectionDefault();
+    List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
+    String tableString = Bytes.toString(tableName);
+    byte[] firstRowInTable = Bytes.toBytes(tableString + ",,");
+    Scan scan = new Scan(firstRowInTable);
+    scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
+    long scannerid = metaServer.openScanner(
+        HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
+    try {
+      Result data;
+      while((data = metaServer.next(scannerid)) != null) {
+        if (data != null && data.size() > 0) {
+          HRegionInfo info = Writables.getHRegionInfo(
+              data.getValue(HConstants.CATALOG_FAMILY,
+                  HConstants.REGIONINFO_QUALIFIER));
+          if (info.getTableDesc().getNameAsString().equals(tableString)) {
+            regions.add(info);
+          } else {
+            break;
+          }
+        }
+      }
+      return regions;
+    } finally {
+      metaServer.close(scannerid);
+    }
+  }
+
+  public static List<Pair<HRegionInfo, HServerAddress>>
+  getTableRegionsAndLocations(CatalogTracker catalogTracker, String tableName)
+  throws IOException {
+    HRegionInterface metaServer =
+      catalogTracker.waitForMetaServerConnectionDefault();
+    List<Pair<HRegionInfo, HServerAddress>> regions =
+      new ArrayList<Pair<HRegionInfo, HServerAddress>>();
+    byte[] firstRowInTable = Bytes.toBytes(tableName + ",,");
+    Scan scan = new Scan(firstRowInTable);
+    scan.addFamily(HConstants.CATALOG_FAMILY);
+    long scannerid = metaServer.openScanner(
+        HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
+    try {
+      Result data;
+      while((data = metaServer.next(scannerid)) != null) {
+        if (data != null && data.size() > 0) {
+          Pair<HRegionInfo, HServerAddress> region = metaRowToRegionPair(data);
+          if (region.getFirst().getTableDesc().getNameAsString().equals(
+              tableName)) {
+            regions.add(region);
+          } else {
+            break;
+          }
+        }
+      }
+      return regions;
+    } finally {
+      metaServer.close(scannerid);
+    }
+  }
+
+  public static NavigableMap<HRegionInfo, Result>
+  getServerRegions(CatalogTracker catalogTracker, final HServerInfo hsi)
+  throws IOException {
+    HRegionInterface metaServer =
+      catalogTracker.waitForMetaServerConnectionDefault();
+    NavigableMap<HRegionInfo, Result> hris = new TreeMap<HRegionInfo, Result>();
+    Scan scan = new Scan();
+    scan.addFamily(HConstants.CATALOG_FAMILY);
+    long scannerid = metaServer.openScanner(
+        HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
+    try {
+      Result result;
+      while((result = metaServer.next(scannerid)) != null) {
+        if (result != null && result.size() > 0) {
+          Pair<HRegionInfo, HServerAddress> pair = metaRowToRegionPair(result);
+          if (!pair.getSecond().equals(hsi.getServerAddress())) continue;
+          hris.put(pair.getFirst(), result);
+        }
+      }
+      return hris;
+    } finally {
+      metaServer.close(scannerid);
+    }
+  }
+}
\ No newline at end of file

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/RootLocationEditor.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/RootLocationEditor.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/RootLocationEditor.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/catalog/RootLocationEditor.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,72 @@
+/**
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.catalog;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.zookeeper.ZKUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
+import org.apache.zookeeper.KeeperException;
+
+/**
+ * Makes changes to the location of <code>-ROOT-</code> in ZooKeeper.
+ */
+public class RootLocationEditor {
+  private static final Log LOG = LogFactory.getLog(RootLocationEditor.class);
+
+  /**
+   * Deletes the location of <code>-ROOT-</code> in ZooKeeper.
+   * @param zookeeper zookeeper reference
+   * @throws KeeperException unexpected zookeeper exception
+   */
+  public static void deleteRootLocation(ZooKeeperWatcher zookeeper)
+  throws KeeperException {
+    LOG.info("Unsetting ROOT region location in ZooKeeper");
+    try {
+      // Just delete the node.  Don't need any watches, only we will create it.
+      ZKUtil.deleteNode(zookeeper, zookeeper.rootServerZNode);
+    } catch(KeeperException.NoNodeException nne) {
+      // Has already been deleted
+    }
+  }
+
+  /**
+   * Sets the location of <code>-ROOT-</code> in ZooKeeper to the
+   * specified server address.
+   * @param zookeeper zookeeper reference
+   * @param location server address hosting root
+   * @throws KeeperException unexpected zookeeper exception
+   */
+  public static void setRootLocation(ZooKeeperWatcher zookeeper,
+      HServerAddress location)
+  throws KeeperException {
+    LOG.info("Setting ROOT region location in ZooKeeper as " + location);
+    try {
+      ZKUtil.createAndWatch(zookeeper, zookeeper.rootServerZNode,
+        Bytes.toBytes(location.toString()));
+    } catch(KeeperException.NodeExistsException nee) {
+      LOG.debug("ROOT region location already existed, updated location");
+      ZKUtil.setData(zookeeper, zookeeper.rootServerZNode,
+          Bytes.toBytes(location.toString()));
+    }
+  }
+}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Action.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Action.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Action.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Action.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,100 @@
+/**
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * 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.client;
+
+import java.io.DataInput;
+import java.io.DataOutput;
+import java.io.IOException;
+
+import org.apache.hadoop.hbase.io.HbaseObjectWritable;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.io.Writable;
+
+/*
+ * A Get, Put or Delete associated with it's region.  Used internally by  
+ * {@link HTable::batch} to associate the action with it's region and maintain 
+ * the index from the original request. 
+ */
+public class Action implements Writable, Comparable {
+
+  private byte[] regionName;
+  private Row action;
+  private int originalIndex;
+  private Result result;
+
+  public Action() {
+    super();
+  }
+
+  public Action(byte[] regionName, Row action, int originalIndex) {
+    super();
+    this.regionName = regionName;
+    this.action = action;
+    this.originalIndex = originalIndex;
+  }
+
+  public byte[] getRegionName() {
+    return regionName;
+  }
+
+  public void setRegionName(byte[] regionName) {
+    this.regionName = regionName;
+  }
+
+  public Result getResult() {
+    return result;
+  }
+
+  public void setResult(Result result) {
+    this.result = result;
+  }
+
+  public Row getAction() {
+    return action;
+  }
+
+  public int getOriginalIndex() {
+    return originalIndex;
+  }
+
+  @Override
+  public int compareTo(Object o) {
+    return action.compareTo(((Action) o).getAction());
+  }
+
+  // ///////////////////////////////////////////////////////////////////////////
+  // Writable
+  // ///////////////////////////////////////////////////////////////////////////
+
+  public void write(final DataOutput out) throws IOException {
+    Bytes.writeByteArray(out, regionName);
+    HbaseObjectWritable.writeObject(out, action, Row.class, null);
+    out.writeInt(originalIndex);
+    HbaseObjectWritable.writeObject(out, result, Result.class, null);
+  }
+
+  public void readFields(final DataInput in) throws IOException {
+    this.regionName = Bytes.readByteArray(in);
+    this.action = (Row) HbaseObjectWritable.readObject(in, null);
+    this.originalIndex = in.readInt();
+    this.result = (Result) HbaseObjectWritable.readObject(in, null);
+  }
+
+}

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Get.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Get.java?rev=991397&r1=991396&r2=991397&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Get.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/Get.java Tue Aug 31 23:51:44 2010
@@ -60,7 +60,7 @@ import java.util.TreeSet;
  * <p>
  * To add a filter, execute {@link #setFilter(Filter) setFilter}.
  */
-public class Get implements Writable, Row, Comparable<Row> {
+public class Get implements Writable {
   private static final byte GET_VERSION = (byte)1;
 
   private byte [] row = null;
@@ -325,11 +325,6 @@ public class Get implements Writable, Ro
     return sb.toString();
   }
 
-  //Row
-  public int compareTo(Row other) {
-    return Bytes.compareTo(this.getRow(), other.getRow());
-  }
-  
   //Writable
   public void readFields(final DataInput in)
   throws IOException {

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java?rev=991397&r1=991396&r2=991397&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseAdmin.java Tue Aug 31 23:51:44 2010
@@ -19,35 +19,39 @@
  */
 package org.apache.hadoop.hbase.client;
 
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.NavigableMap;
+
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Abortable;
 import org.apache.hadoop.hbase.ClusterStatus;
 import org.apache.hadoop.hbase.HBaseConfiguration;
 import org.apache.hadoop.hbase.HColumnDescriptor;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.HServerAddress;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.MasterNotRunningException;
 import org.apache.hadoop.hbase.RegionException;
 import org.apache.hadoop.hbase.RemoteExceptionHandler;
 import org.apache.hadoop.hbase.TableExistsException;
-import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
+import org.apache.hadoop.hbase.UnknownRegionException;
+import org.apache.hadoop.hbase.ZooKeeperConnectionException;
+import org.apache.hadoop.hbase.catalog.CatalogTracker;
+import org.apache.hadoop.hbase.catalog.MetaReader;
 import org.apache.hadoop.hbase.ipc.HMasterInterface;
 import org.apache.hadoop.hbase.ipc.HRegionInterface;
 import org.apache.hadoop.hbase.util.Bytes;
-import org.apache.hadoop.hbase.util.MetaUtils;
+import org.apache.hadoop.hbase.util.Pair;
 import org.apache.hadoop.hbase.util.Writables;
-import org.apache.hadoop.io.BooleanWritable;
-import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.ipc.RemoteException;
 
-import java.io.IOException;
-import java.util.Arrays;
-import java.util.Map;
-import java.util.NavigableMap;
-
 /**
  * Provides an interface to manage HBase database table metadata + general 
  * administrative functions.  Use HBaseAdmin to create, drop, list, enable and 
@@ -55,27 +59,56 @@ import java.util.NavigableMap;
  * 
  * See {@link HTable} to add, update, and delete data from an individual table.
  */
-public class HBaseAdmin {
+public class HBaseAdmin implements Abortable {
   private final Log LOG = LogFactory.getLog(this.getClass().getName());
 //  private final HConnection connection;
   final HConnection connection;
   private volatile Configuration conf;
   private final long pause;
   private final int numRetries;
-  private volatile HMasterInterface master;
+  /**
+   * Lazily instantiated.  Use {@link #getCatalogTracker()} to ensure you get
+   * an instance rather than a null.
+   */
+  private CatalogTracker catalogTracker = null;
 
   /**
    * Constructor
    *
    * @param conf Configuration object
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
-  public HBaseAdmin(Configuration conf) throws MasterNotRunningException {
+  public HBaseAdmin(Configuration conf)
+  throws MasterNotRunningException, ZooKeeperConnectionException {
     this.connection = HConnectionManager.getConnection(conf);
     this.conf = conf;
     this.pause = conf.getLong("hbase.client.pause", 30 * 1000);
     this.numRetries = conf.getInt("hbase.client.retries.number", 5);
-    this.master = connection.getMaster();
+    this.connection.getMaster();
+  }
+
+  private synchronized CatalogTracker getCatalogTracker()
+  throws ZooKeeperConnectionException, IOException {
+    if (this.catalogTracker == null) {
+      this.catalogTracker = new CatalogTracker(this.connection.getZooKeeperWatcher(),
+        ServerConnectionManager.getConnection(conf), this,
+        this.conf.getInt("hbase.admin.catalog.timeout", 10 * 1000));
+      try {
+        this.catalogTracker.start();
+      } catch (InterruptedException e) {
+        // Let it out as an IOE for now until we redo all so tolerate IEs
+        Thread.currentThread().interrupt();
+        throw new IOException("Interrupted", e);
+      }
+    }
+    return this.catalogTracker;
+  }
+
+  @Override
+  public void abort(String why, Throwable e) {
+    // Currently does nothing but throw the passed message and exception
+    throw new RuntimeException(why, e);
   }
 
   /** @return HConnection used by this object. */
@@ -84,15 +117,21 @@ public class HBaseAdmin {
   }
 
   /**
+   * Get a connection to the currently set master.
    * @return proxy connection to master server for this instance
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
-  public HMasterInterface getMaster() throws MasterNotRunningException{
+  public HMasterInterface getMaster()
+  throws MasterNotRunningException, ZooKeeperConnectionException {
     return this.connection.getMaster();
   }
 
-  /** @return - true if the master server is running */
-  public boolean isMasterRunning() {
+  /** @return - true if the master server is running
+   * @throws ZooKeeperConnectionException
+   * @throws MasterNotRunningException */
+  public boolean isMasterRunning()
+  throws MasterNotRunningException, ZooKeeperConnectionException {
     return this.connection.isMasterRunning();
   }
 
@@ -100,9 +139,10 @@ public class HBaseAdmin {
    * @param tableName Table to check.
    * @return True if table exists already.
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
   public boolean tableExists(final String tableName)
-  throws MasterNotRunningException {
+  throws MasterNotRunningException, ZooKeeperConnectionException {
     return tableExists(Bytes.toBytes(tableName));
   }
 
@@ -110,12 +150,11 @@ public class HBaseAdmin {
    * @param tableName Table to check.
    * @return True if table exists already.
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
   public boolean tableExists(final byte [] tableName)
-  throws MasterNotRunningException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
+  throws MasterNotRunningException, ZooKeeperConnectionException {
+    connection.isMasterRunning();
     return connection.tableExists(tableName);
   }
 
@@ -147,8 +186,9 @@ public class HBaseAdmin {
 
   private long getPauseTime(int tries) {
     int triesCount = tries;
-    if (triesCount >= HConstants.RETRY_BACKOFF.length)
+    if (triesCount >= HConstants.RETRY_BACKOFF.length) {
       triesCount = HConstants.RETRY_BACKOFF.length - 1;
+    }
     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
   }
 
@@ -257,7 +297,7 @@ public class HBaseAdmin {
       try {
         Thread.sleep(getPauseTime(tries));
       } catch (InterruptedException e) {
-        // continue
+        // Just continue; ignore the interruption.
       }
     }
   }
@@ -277,12 +317,9 @@ public class HBaseAdmin {
    */
   public void createTableAsync(HTableDescriptor desc, byte [][] splitKeys)
   throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
     HTableDescriptor.isLegalTableName(desc.getName());
     try {
-      this.master.createTable(desc, splitKeys);
+      getMaster().createTable(desc, splitKeys);
     } catch (RemoteException e) {
       throw RemoteExceptionHandler.decodeRemoteException(e);
     }
@@ -307,13 +344,11 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public void deleteTable(final byte [] tableName) throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
+    isMasterRunning();
     HTableDescriptor.isLegalTableName(tableName);
     HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName);
     try {
-      this.master.deleteTable(tableName);
+      getMaster().deleteTable(tableName);
     } catch (RemoteException e) {
       throw RemoteExceptionHandler.decodeRemoteException(e);
     }
@@ -401,21 +436,20 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public void enableTable(final byte [] tableName) throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
+    isMasterRunning();
 
     // Wait until all regions are enabled
     boolean enabled = false;
     for (int tries = 0; tries < this.numRetries; tries++) {
-
       try {
-        this.master.enableTable(tableName);
+        getMaster().enableTable(tableName);
       } catch (RemoteException e) {
         throw RemoteExceptionHandler.decodeRemoteException(e);
       }
       enabled = isTableEnabled(tableName);
-      if (enabled) break;
+      if (enabled) {
+        break;
+      }
       long sleep = getPauseTime(tries);
       if (LOG.isDebugEnabled()) {
         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
@@ -431,9 +465,10 @@ public class HBaseAdmin {
           Bytes.toString(tableName));
       }
     }
-    if (!enabled)
+    if (!enabled) {
       throw new IOException("Unable to enable table " +
         Bytes.toString(tableName));
+    }
     LOG.info("Enabled table " + Bytes.toString(tableName));
   }
 
@@ -458,20 +493,20 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public void disableTable(final byte [] tableName) throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
+    isMasterRunning();
 
     // Wait until all regions are disabled
     boolean disabled = false;
     for (int tries = 0; tries < this.numRetries; tries++) {
       try {
-        this.master.disableTable(tableName);
+        getMaster().disableTable(tableName);
       } catch (RemoteException e) {
         throw RemoteExceptionHandler.decodeRemoteException(e);
       }
       disabled = isTableDisabled(tableName);
-      if (disabled) break;
+      if (disabled) {
+        break;
+      }
       if (LOG.isDebugEnabled()) {
         LOG.debug("Sleep. Waiting for all regions to be disabled from " +
           Bytes.toString(tableName));
@@ -560,12 +595,9 @@ public class HBaseAdmin {
    */
   public void addColumn(final byte [] tableName, HColumnDescriptor column)
   throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
     HTableDescriptor.isLegalTableName(tableName);
     try {
-      this.master.addColumn(tableName, column);
+      getMaster().addColumn(tableName, column);
     } catch (RemoteException e) {
       throw RemoteExceptionHandler.decodeRemoteException(e);
     }
@@ -594,12 +626,8 @@ public class HBaseAdmin {
    */
   public void deleteColumn(final byte [] tableName, final byte [] columnName)
   throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
-    HTableDescriptor.isLegalTableName(tableName);
     try {
-      this.master.deleteColumn(tableName, columnName);
+      getMaster().deleteColumn(tableName, columnName);
     } catch (RemoteException e) {
       throw RemoteExceptionHandler.decodeRemoteException(e);
     }
@@ -613,12 +641,25 @@ public class HBaseAdmin {
    * @param columnName name of column to be modified
    * @param descriptor new column descriptor to use
    * @throws IOException if a remote or network exception occurs
+   * @deprecated The <code>columnName</code> is redundant. Use {@link #addColumn(String, HColumnDescriptor)}
    */
   public void modifyColumn(final String tableName, final String columnName,
       HColumnDescriptor descriptor)
   throws IOException {
-    modifyColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName),
-      descriptor);
+    modifyColumn(tableName,  descriptor);
+  }
+
+  /**
+   * Modify an existing column family on a table.
+   * Asynchronous operation.
+   *
+   * @param tableName name of table
+   * @param descriptor new column descriptor to use
+   * @throws IOException if a remote or network exception occurs
+   */
+  public void modifyColumn(final String tableName, HColumnDescriptor descriptor)
+  throws IOException {
+    modifyColumn(Bytes.toBytes(tableName), descriptor);
   }
 
   /**
@@ -629,56 +670,71 @@ public class HBaseAdmin {
    * @param columnName name of column to be modified
    * @param descriptor new column descriptor to use
    * @throws IOException if a remote or network exception occurs
+   * @deprecated The <code>columnName</code> is redundant. Use {@link #modifyColumn(byte[], HColumnDescriptor)}
    */
   public void modifyColumn(final byte [] tableName, final byte [] columnName,
     HColumnDescriptor descriptor)
   throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
-    HTableDescriptor.isLegalTableName(tableName);
+    modifyColumn(tableName, descriptor);
+  }
+
+  /**
+   * Modify an existing column family on a table.
+   * Asynchronous operation.
+   *
+   * @param tableName name of table
+   * @param descriptor new column descriptor to use
+   * @throws IOException if a remote or network exception occurs
+   */
+  public void modifyColumn(final byte [] tableName, HColumnDescriptor descriptor)
+  throws IOException {
     try {
-      this.master.modifyColumn(tableName, columnName, descriptor);
-    } catch (RemoteException e) {
-      throw RemoteExceptionHandler.decodeRemoteException(e);
+      getMaster().modifyColumn(tableName, descriptor);
+    } catch (RemoteException re) {
+      // Convert RE exceptions in here; client shouldn't have to deal with them,
+      // at least w/ the type of exceptions that come out of this method:
+      // TableNotFoundException, etc.
+      throw RemoteExceptionHandler.decodeRemoteException(re);
     }
   }
 
   /**
    * Close a region. For expert-admins.
-   * Asynchronous operation.
-   *
    * @param regionname region name to close
-   * @param args Optional server name.  Otherwise, we'll send close to the
-   * server registered in .META.
+   * @param hostAndPort If supplied, we'll use this location rather than
+   * the one currently in <code>.META.</code>
    * @throws IOException if a remote or network exception occurs
    */
-  public void closeRegion(final String regionname, final Object... args)
+  public void closeRegion(final String regionname, final String hostAndPort)
   throws IOException {
-    closeRegion(Bytes.toBytes(regionname), args);
+    closeRegion(Bytes.toBytes(regionname), hostAndPort);
   }
 
   /**
    * Close a region.  For expert-admins.
-   * Asynchronous operation.
-   *
    * @param regionname region name to close
-   * @param args Optional server name.  Otherwise, we'll send close to the
-   * server registered in .META.
+   * @param hostAndPort If supplied, we'll use this location rather than
+   * the one currently in <code>.META.</code>
    * @throws IOException if a remote or network exception occurs
    */
-  public void closeRegion(final byte [] regionname, final Object... args)
+  public void closeRegion(final byte [] regionname, final String hostAndPort)
   throws IOException {
-    // Be careful. Must match the handler over in HMaster at MODIFY_CLOSE_REGION
-    int len = (args == null)? 0: args.length;
-    int xtraArgsCount = 1;
-    Object [] newargs = new Object[len + xtraArgsCount];
-    newargs[0] = regionname;
-    if(args != null) {
-      System.arraycopy(args, 0, newargs, xtraArgsCount, len);
+    if (hostAndPort != null) {
+      HServerAddress hsa = new HServerAddress(hostAndPort);
+      Pair<HRegionInfo, HServerAddress> pair =
+        MetaReader.getRegion(getCatalogTracker(), regionname);
+      closeRegion(hsa, pair.getFirst());
+    } else {
+      Pair<HRegionInfo, HServerAddress> pair =
+        MetaReader.getRegion(getCatalogTracker(), regionname);
+      closeRegion(pair.getSecond(), pair.getFirst());
     }
-    modifyTable(HConstants.META_TABLE_NAME, HConstants.Modify.CLOSE_REGION,
-      newargs);
+  }
+
+  private void closeRegion(final HServerAddress hsa, final HRegionInfo hri)
+  throws IOException {
+    HRegionInterface rs = this.connection.getHRegionConnection(hsa);
+    rs.closeRegion(hri);
   }
 
   /**
@@ -700,7 +756,25 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public void flush(final byte [] tableNameOrRegionName) throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_FLUSH);
+    boolean isRegionName = isRegionName(tableNameOrRegionName);
+    if (isRegionName) {
+      Pair<HRegionInfo, HServerAddress> pair =
+        MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName);
+      flush(pair.getSecond(), pair.getFirst());
+    } else {
+      List<Pair<HRegionInfo, HServerAddress>> pairs =
+        MetaReader.getTableRegionsAndLocations(getCatalogTracker(),
+          Bytes.toString(tableNameOrRegionName));
+      for (Pair<HRegionInfo, HServerAddress> pair: pairs) {
+        flush(pair.getSecond(), pair.getFirst());
+      }
+    }
+  }
+
+  private void flush(final HServerAddress hsa, final HRegionInfo hri)
+  throws IOException {
+    HRegionInterface rs = this.connection.getHRegionConnection(hsa);
+    rs.flushRegion(hri);
   }
 
   /**
@@ -722,7 +796,7 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public void compact(final byte [] tableNameOrRegionName) throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_COMPACT);
+    compact(tableNameOrRegionName, false);
   }
 
   /**
@@ -746,7 +820,63 @@ public class HBaseAdmin {
    */
   public void majorCompact(final byte [] tableNameOrRegionName)
   throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_MAJOR_COMPACT);
+    compact(tableNameOrRegionName, true);
+  }
+
+  /**
+   * Compact a table or an individual region.
+   * Asynchronous operation.
+   *
+   * @param tableNameOrRegionName table or region to compact
+   * @param major True if we are to do a major compaction.
+   * @throws IOException if a remote or network exception occurs
+   */
+  private void compact(final byte [] tableNameOrRegionName, final boolean major)
+  throws IOException {
+    if (isRegionName(tableNameOrRegionName)) {
+      Pair<HRegionInfo, HServerAddress> pair =
+        MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName);
+      compact(pair.getSecond(), pair.getFirst(), major);
+    } else {
+      List<Pair<HRegionInfo, HServerAddress>> pairs =
+        MetaReader.getTableRegionsAndLocations(getCatalogTracker(),
+          Bytes.toString(tableNameOrRegionName));
+      for (Pair<HRegionInfo, HServerAddress> pair: pairs) {
+        compact(pair.getSecond(), pair.getFirst(), major);
+      }
+    }
+  }
+
+  private void compact(final HServerAddress hsa, final HRegionInfo hri,
+      final boolean major)
+  throws IOException {
+    HRegionInterface rs = this.connection.getHRegionConnection(hsa);
+    rs.compactRegion(hri, major);
+  }
+
+  /**
+   * Move the region <code>r</code> to <code>dest</code>.
+   * @param encodedRegionName The encoded region name.
+   * @param destServerName The servername of the destination regionserver
+   * @throws UnknownRegionException Thrown if we can't find a region named
+   * <code>encodedRegionName</code>
+   * @throws ZooKeeperConnectionException 
+   * @throws MasterNotRunningException 
+   */
+  public void move(final byte [] encodedRegionName, final byte [] destServerName)
+  throws UnknownRegionException, MasterNotRunningException, ZooKeeperConnectionException {
+    getMaster().move(encodedRegionName, destServerName);
+  }
+
+  /**
+   * @param b If true, enable balancer. If false, disable balancer.
+   * @return Previous balancer value
+   * @throws ZooKeeperConnectionException 
+   * @throws MasterNotRunningException 
+   */
+  public boolean balance(final boolean b)
+  throws MasterNotRunningException, ZooKeeperConnectionException {
+    return getMaster().balance(b);
   }
 
   /**
@@ -768,32 +898,31 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public void split(final byte [] tableNameOrRegionName) throws IOException {
-    modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_SPLIT);
+    if (isRegionName(tableNameOrRegionName)) {
+      // Its a possible region name.
+      Pair<HRegionInfo, HServerAddress> pair =
+        MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName);
+      split(pair.getSecond(), pair.getFirst());
+    } else {
+      List<Pair<HRegionInfo, HServerAddress>> pairs =
+        MetaReader.getTableRegionsAndLocations(getCatalogTracker(),
+          Bytes.toString(tableNameOrRegionName));
+      for (Pair<HRegionInfo, HServerAddress> pair: pairs) {
+        split(pair.getSecond(), pair.getFirst());
+      }
+    }
   }
 
-  /*
-   * Call modifyTable using passed tableName or region name String.  If no
-   * such table, presume we have been passed a region name.
-   * @param tableNameOrRegionName
-   * @param op
-   * @throws IOException
-   */
-  private void modifyTable(final byte [] tableNameOrRegionName,
-      final HConstants.Modify op)
+  private void split(final HServerAddress hsa, final HRegionInfo hri)
   throws IOException {
-    if (tableNameOrRegionName == null) {
-      throw new IllegalArgumentException("Pass a table name or region name");
-    }
-    byte [] tableName = tableExists(tableNameOrRegionName)?
-      tableNameOrRegionName: null;
-    byte [] regionName = tableName == null? tableNameOrRegionName: null;
-    Object [] args = regionName == null? null: new byte [][] {regionName};
-    modifyTable(tableName == null? null: tableName, op, args);
+    HRegionInterface rs = this.connection.getHRegionConnection(hsa);
+    rs.splitRegion(hri);
   }
 
   /**
    * Modify an existing table, more IRB friendly version.
-   * Asynchronous operation.
+   * Asynchronous operation.  This means that it may be a while before your
+   * schema change is updated across all of the table.
    *
    * @param tableName name of table.
    * @param htd modified description of the table
@@ -801,107 +930,57 @@ public class HBaseAdmin {
    */
   public void modifyTable(final byte [] tableName, HTableDescriptor htd)
   throws IOException {
-    modifyTable(tableName, HConstants.Modify.TABLE_SET_HTD, htd);
+    try {
+      getMaster().modifyTable(tableName, htd);
+    } catch (RemoteException re) {
+      // Convert RE exceptions in here; client shouldn't have to deal with them,
+      // at least w/ the type of exceptions that come out of this method:
+      // TableNotFoundException, etc.
+      throw RemoteExceptionHandler.decodeRemoteException(re);
+    }
   }
 
   /**
-   * Modify an existing table.
-   * Asynchronous operation.
-   *
-   * @param tableName name of table.  May be null if we are operating on a
-   * region.
-   * @param op table modification operation
-   * @param args operation specific arguments
-   * @throws IOException if a remote or network exception occurs
+   * @param tableNameOrRegionName Name of a table or name of a region.
+   * @return True if <code>tableNameOrRegionName</code> is *possibly* a region
+   * name else false if a verified tablename (we call {@link #tableExists(byte[])};
+   * else we throw an exception.
+   * @throws ZooKeeperConnectionException 
+   * @throws MasterNotRunningException 
    */
-  public void modifyTable(final byte [] tableName, HConstants.Modify op,
-      Object... args)
-      throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
-    // Let pass if its a catalog table.  Used by admins.
-    if (tableName != null && !MetaUtils.isMetaTableName(tableName)) {
-      // This will throw exception
-      HTableDescriptor.isLegalTableName(tableName);
+  private boolean isRegionName(final byte [] tableNameOrRegionName)
+  throws MasterNotRunningException, ZooKeeperConnectionException {
+    if (tableNameOrRegionName == null) {
+      throw new IllegalArgumentException("Pass a table name or region name");
     }
-    Writable[] arr = null;
-    try {
-      switch (op) {
-      case TABLE_SET_HTD:
-        if (args == null || args.length < 1 ||
-            !(args[0] instanceof HTableDescriptor)) {
-          throw new IllegalArgumentException("SET_HTD requires a HTableDescriptor");
-        }
-        arr = new Writable[1];
-        arr[0] = (HTableDescriptor)args[0];
-        this.master.modifyTable(tableName, op, arr);
-        break;
-
-      case TABLE_COMPACT:
-      case TABLE_SPLIT:
-      case TABLE_MAJOR_COMPACT:
-      case TABLE_FLUSH:
-        if (args != null && args.length > 0) {
-          arr = new Writable[1];
-          if (args[0] instanceof byte[]) {
-            arr[0] = new ImmutableBytesWritable((byte[])args[0]);
-          } else if (args[0] instanceof ImmutableBytesWritable) {
-            arr[0] = (ImmutableBytesWritable)args[0];
-          } else if (args[0] instanceof String) {
-            arr[0] = new ImmutableBytesWritable(Bytes.toBytes((String)args[0]));
-          } else {
-            throw new IllegalArgumentException("Requires byte[], String, or" +
-              "ImmutableBytesWritable");
-          }
-        }
-        this.master.modifyTable(tableName, op, arr);
-        break;
-
-      case CLOSE_REGION:
-        if (args == null || args.length < 1) {
-          throw new IllegalArgumentException("Requires at least a region name");
-        }
-        arr = new Writable[args.length];
-        for (int i = 0; i < args.length; i++) {
-          if (args[i] instanceof byte[]) {
-            arr[i] = new ImmutableBytesWritable((byte[])args[i]);
-          } else if (args[i] instanceof ImmutableBytesWritable) {
-            arr[i] = (ImmutableBytesWritable)args[i];
-          } else if (args[i] instanceof String) {
-            arr[i] = new ImmutableBytesWritable(Bytes.toBytes((String)args[i]));
-          } else if (args[i] instanceof Boolean) {
-            arr[i] = new BooleanWritable((Boolean) args[i]);
-          } else {
-            throw new IllegalArgumentException("Requires byte [] or " +
-              "ImmutableBytesWritable, not " + args[i]);
-          }
-        }
-        this.master.modifyTable(tableName, op, arr);
-        break;
+    return !tableExists(tableNameOrRegionName);
+  }
 
-      default:
-        throw new IOException("unknown modifyTable op " + op);
-      }
+  /**
+   * Shuts down the HBase cluster
+   * @throws IOException if a remote or network exception occurs
+   */
+  public synchronized void shutdown() throws IOException {
+    isMasterRunning();
+    try {
+      getMaster().shutdown();
     } catch (RemoteException e) {
       throw RemoteExceptionHandler.decodeRemoteException(e);
     }
   }
 
   /**
-   * Shuts down the HBase instance
+   * Shuts down the current HBase master only.
+   * Does not shutdown the cluster.
+   * @see #shutdown()
    * @throws IOException if a remote or network exception occurs
    */
-  public synchronized void shutdown() throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
+  public synchronized void stopMaster() throws IOException {
+    isMasterRunning();
     try {
-      this.master.shutdown();
+      getMaster().stopMaster();
     } catch (RemoteException e) {
       throw RemoteExceptionHandler.decodeRemoteException(e);
-    } finally {
-      this.master = null;
     }
   }
 
@@ -910,10 +989,7 @@ public class HBaseAdmin {
    * @throws IOException if a remote or network exception occurs
    */
   public ClusterStatus getClusterStatus() throws IOException {
-    if (this.master == null) {
-      throw new MasterNotRunningException("master has been shut down");
-    }
-    return this.master.getClusterStatus();
+    return getMaster().getClusterStatus();
   }
 
   private HRegionLocation getFirstMetaServerForTable(final byte [] tableName)
@@ -926,12 +1002,13 @@ public class HBaseAdmin {
    * Check to see if HBase is running. Throw an exception if not.
    *
    * @param conf system configuration
-   * @throws MasterNotRunningException if a remote or network exception occurs
+   * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
   public static void checkHBaseAvailable(Configuration conf)
-  throws MasterNotRunningException {
+  throws MasterNotRunningException, ZooKeeperConnectionException {
     Configuration copyOfConf = HBaseConfiguration.create(conf);
     copyOfConf.setInt("hbase.client.retries.number", 1);
     new HBaseAdmin(copyOfConf);
   }
-}
+}
\ No newline at end of file

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java?rev=991397&r1=991396&r2=991397&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HBaseFsck.java Tue Aug 31 23:51:44 2010
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Comparator;
+import java.util.NavigableSet;
 import java.util.TreeMap;
 import java.util.TreeSet;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -42,6 +43,7 @@ import org.apache.hadoop.hbase.HServerIn
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.KeyValue;
 import org.apache.hadoop.hbase.MasterNotRunningException;
+import org.apache.hadoop.hbase.ZooKeeperConnectionException;
 import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
 import org.apache.hadoop.hbase.ipc.HMasterInterface;
 import org.apache.hadoop.hbase.ipc.HRegionInterface;
@@ -73,9 +75,10 @@ public class HBaseFsck extends HBaseAdmi
    *
    * @param conf Configuration object
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
   public HBaseFsck(Configuration conf) 
-    throws MasterNotRunningException, IOException {
+    throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
     super(conf);
     this.conf = conf;
 
@@ -261,10 +264,10 @@ public class HBaseFsck extends HBaseAdmi
                                     rsinfo.getServerAddress());
 
         // list all online regions from this region server
-        HRegionInfo[] regions = server.getRegionsAssignment();
+        NavigableSet<HRegionInfo> regions = server.getOnlineRegions();
         if (details) {
           System.out.print("\nRegionServer:" + rsinfo.getServerName() +
-                           " number of regions:" + regions.length);
+                           " number of regions:" + regions.size());
           for (HRegionInfo rinfo: regions) {
             System.out.print("\n\t name:" + rinfo.getRegionNameAsString() +
                              " id:" + rinfo.getRegionId() +

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HConnection.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HConnection.java?rev=991397&r1=991396&r2=991397&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HConnection.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/client/HConnection.java Tue Aug 31 23:51:44 2010
@@ -19,20 +19,21 @@
  */
 package org.apache.hadoop.hbase.client;
 
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ExecutorService;
+
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.HRegionLocation;
 import org.apache.hadoop.hbase.HServerAddress;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.hbase.MasterNotRunningException;
+import org.apache.hadoop.hbase.ZooKeeperConnectionException;
 import org.apache.hadoop.hbase.ipc.HMasterInterface;
 import org.apache.hadoop.hbase.ipc.HRegionInterface;
-import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
-
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
 
 /**
  * Cluster connection.
@@ -40,29 +41,33 @@ import java.util.concurrent.ExecutorServ
  */
 public interface HConnection {
   /**
-   * Retrieve ZooKeeperWrapper used by the connection.
-   * @return ZooKeeperWrapper handle being used by the connection.
+   * Retrieve ZooKeeperWatcher used by the connection.
+   * @return ZooKeeperWatcher handle being used by the connection.
    * @throws IOException if a remote or network exception occurs
    */
-  public ZooKeeperWrapper getZooKeeperWrapper() throws IOException;
+  public ZooKeeperWatcher getZooKeeperWatcher() throws IOException;
 
   /**
    * @return proxy connection to master server for this instance
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
-  public HMasterInterface getMaster() throws MasterNotRunningException;
+  public HMasterInterface getMaster()
+  throws MasterNotRunningException, ZooKeeperConnectionException;
 
   /** @return - true if the master server is running */
-  public boolean isMasterRunning();
+  public boolean isMasterRunning()
+  throws MasterNotRunningException, ZooKeeperConnectionException;
 
   /**
    * Checks if <code>tableName</code> exists.
    * @param tableName Table to check.
    * @return True if table exists already.
    * @throws MasterNotRunningException if the master is not running
+   * @throws ZooKeeperConnectionException if unable to connect to zookeeper
    */
   public boolean tableExists(final byte [] tableName)
-  throws MasterNotRunningException;
+  throws MasterNotRunningException, ZooKeeperConnectionException;
 
   /**
    * A table that isTableEnabled == false and isTableDisabled == false
@@ -131,7 +136,7 @@ public interface HConnection {
    * lives in, ignoring any value that might be in the cache.
    * @param tableName name of the table <i>row</i> is in
    * @param row row key you're trying to find the region of
-   * @return HRegionLocation that describes where to find the reigon in
+   * @return HRegionLocation that describes where to find the region in
    * question
    * @throws IOException if a remote or network exception occurs
    */
@@ -140,6 +145,25 @@ public interface HConnection {
   throws IOException;
 
   /**
+   * Gets the location of the region of <i>regionName</i>.
+   * @param regionName name of the region to locate
+   * @return HRegionLocation that describes where to find the region in
+   * question
+   * @throws IOException if a remote or network exception occurs
+   */
+  public HRegionLocation locateRegion(final byte [] regionName)
+  throws IOException;
+
+  /**
+   * Gets the locations of all regions in the specified table, <i>tableName</i>.
+   * @param tableName table to get regions of
+   * @return list of region locations for all regions of table
+   * @throws IOException
+   */
+  public List<HRegionLocation> locateRegions(byte[] tableName)
+  throws IOException;
+
+  /**
    * Establishes a connection to the region server at the specified address.
    * @param regionServer - the server to connect to
    * @return proxy for HRegionServer
@@ -197,21 +221,6 @@ public interface HConnection {
   public <T> T getRegionServerWithoutRetries(ServerCallable<T> callable)
   throws IOException, RuntimeException;
 
-  /**
-   * Process a mixed batch of Get, Put and Delete actions. All actions for a
-   * RegionServer are forwarded in one RPC call.
-   * 
-   * @param actions The collection of actions.
-   * @param tableName Name of the hbase table
-   * @param pool thread pool for parallel execution
-   * @param results An empty array, same size as list. If an exception is thrown,
-   * you can test here for partial results, and to determine which actions
-   * processed successfully.
-   * @throws IOException
-   */
-  public void processBatch(List<Row> actions, final byte[] tableName,
-      ExecutorService pool, Result[] results)
-  throws IOException;
 
   /**
    * Process a batch of Puts. Does the retries.
@@ -219,32 +228,20 @@ public interface HConnection {
    * @param tableName The name of the table
    * @return Count of committed Puts.  On fault, < list.size().
    * @throws IOException if a remote or network exception occurs
-   * @deprecated Use HConnectionManager::processBatch instead.
    */
-  public int processBatchOfRows(ArrayList<Put> list, byte[] tableName, ExecutorService pool)
+  public int processBatchOfRows(ArrayList<Put> list, byte[] tableName)
   throws IOException;
 
   /**
    * Process a batch of Deletes. Does the retries.
    * @param list A batch of Deletes to process.
-   * @param tableName The name of the table
    * @return Count of committed Deletes. On fault, < list.size().
+   * @param tableName The name of the table
    * @throws IOException if a remote or network exception occurs
-   * @deprecated Use HConnectionManager::processBatch instead.
    */
-  public int processBatchOfDeletes(List<Delete> list, byte[] tableName, ExecutorService pool)
+  public int processBatchOfDeletes(List<Delete> list, byte[] tableName)
   throws IOException;
 
-  /**
-   * Process a batch of Puts.
-   *
-   * @param list The collection of actions. The list is mutated: all successful Puts 
-   * are removed from the list.
-   * @param tableName Name of the hbase table
-   * @param pool Thread pool for parallel execution
-   * @throws IOException
-   * @deprecated Use HConnectionManager::processBatch instead.
-   */
   public void processBatchOfPuts(List<Put> list,
                                  final byte[] tableName, ExecutorService pool) throws IOException;
 
@@ -261,7 +258,7 @@ public interface HConnection {
   /**
    * Check whether region cache prefetch is enabled or not.
    * @param tableName name of table to check
-   * @return true if table's region cache prefetch is enabled. Otherwise
+   * @return true if table's region cache prefecth is enabled. Otherwise
    * it is disabled.
    */
   public boolean getRegionCachePrefetch(final byte[] tableName);