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/07/14 06:40:14 UTC

svn commit: r963948 - in /hbase/branches/0.20/src/test/org/apache/hadoop/hbase: EmptyWatcher.java HBaseTestingUtility.java

Author: stack
Date: Wed Jul 14 04:40:14 2010
New Revision: 963948

URL: http://svn.apache.org/viewvc?rev=963948&view=rev
Log:
HBASE-2349 Backport HBaseTestingUtility to branch (will include adding support for junit4 to branch)

Added:
    hbase/branches/0.20/src/test/org/apache/hadoop/hbase/EmptyWatcher.java
    hbase/branches/0.20/src/test/org/apache/hadoop/hbase/HBaseTestingUtility.java

Added: hbase/branches/0.20/src/test/org/apache/hadoop/hbase/EmptyWatcher.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.20/src/test/org/apache/hadoop/hbase/EmptyWatcher.java?rev=963948&view=auto
==============================================================================
--- hbase/branches/0.20/src/test/org/apache/hadoop/hbase/EmptyWatcher.java (added)
+++ hbase/branches/0.20/src/test/org/apache/hadoop/hbase/EmptyWatcher.java Wed Jul 14 04:40:14 2010
@@ -0,0 +1,33 @@
+/**
+ * Copyright 2009 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;
+
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.WatchedEvent;
+
+/**
+ * Class used as an empty watche for the tests
+ */
+public class EmptyWatcher implements Watcher{
+  public static EmptyWatcher instance = new EmptyWatcher();
+  private EmptyWatcher() {}
+
+  public void process(WatchedEvent event) {}
+}

Added: hbase/branches/0.20/src/test/org/apache/hadoop/hbase/HBaseTestingUtility.java
URL: http://svn.apache.org/viewvc/hbase/branches/0.20/src/test/org/apache/hadoop/hbase/HBaseTestingUtility.java?rev=963948&view=auto
==============================================================================
--- hbase/branches/0.20/src/test/org/apache/hadoop/hbase/HBaseTestingUtility.java (added)
+++ hbase/branches/0.20/src/test/org/apache/hadoop/hbase/HBaseTestingUtility.java Wed Jul 14 04:40:14 2010
@@ -0,0 +1,624 @@
+/**
+ * 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;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.impl.Jdk14Logger;
+import org.apache.commons.logging.impl.Log4JLogger;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.client.Delete;
+import org.apache.hadoop.hbase.client.HBaseAdmin;
+import org.apache.hadoop.hbase.client.HConnection;
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.Result;
+import org.apache.hadoop.hbase.client.ResultScanner;
+import org.apache.hadoop.hbase.client.Scan;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.FSUtils;
+import org.apache.hadoop.hbase.util.Writables;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
+import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.regionserver.HRegionServer;
+import org.apache.hadoop.hdfs.MiniDFSCluster;
+import org.apache.hadoop.mapred.MiniMRCluster;
+import org.apache.zookeeper.ZooKeeper;
+
+/**
+ * Facility for testing HBase. Added as tool to abet junit4 testing.  Use
+ * instead of old HBaseTestCase and HBaseCluserTestCase functionality.
+ * Create an instance and keep it around doing HBase testing.  This class is
+ * meant to be your one-stop shop for anything you mind need testing.  Manages
+ * one cluster at a time only.  Depends on log4j on classpath and hbase-site.xml
+ * for logging and test-run configuration.  It does not set logging levels nor
+ * make changes to configuration parameters.
+ */
+public class HBaseTestingUtility {
+  
+  private final Log LOG = LogFactory.getLog(getClass());
+
+  private final HBaseConfiguration conf;
+  private MiniZooKeeperCluster zkCluster = null;
+  private MiniDFSCluster dfsCluster = null;
+  private MiniHBaseCluster hbaseCluster = null;
+  private MiniMRCluster mrCluster = null;
+  private File clusterTestBuildDir = null;
+  private HBaseAdmin hbaseAdmin = null;
+
+  public HBaseTestingUtility() {
+    this(new HBaseConfiguration());
+  }
+
+  public HBaseTestingUtility(HBaseConfiguration conf) {
+    this.conf = conf;
+  }
+  
+  /** System property key to get test directory value.
+   */
+  public static final String TEST_DIRECTORY_KEY = "test.build.data";
+
+  /**
+   * @return Instance of Configuration.
+   */
+  public HBaseConfiguration getConfiguration() {
+    return this.conf;
+  }
+
+  /**
+   * @return Where to write test data on local filesystem; usually build/test/data
+   */
+  public Path getTestDir() {
+    return new Path(System.getProperty(TEST_DIRECTORY_KEY, "build/test/data"));
+  }
+
+  /**
+   * @param subdirName
+   * @return Path to a subdirectory named <code>subdirName</code> under
+   * {@link #getTestDir()}.
+   */
+  public Path getTestDir(final String subdirName) {
+    return new Path(getTestDir(), subdirName);
+  }
+
+  /**
+   * Start up a minicluster of hbase, dfs, and zookeeper.
+   * @throws Exception 
+   */
+  public void startMiniCluster() throws Exception {
+    startMiniCluster(1);
+  }
+
+  public void startMiniZKCluster() throws Exception {
+    // Note that this is done before we create the MiniHBaseCluster because we
+    // need to edit the config to add the ZooKeeper servers.
+    this.zkCluster = new MiniZooKeeperCluster();
+    int clientPort = this.zkCluster.startup(this.clusterTestBuildDir);
+    this.conf.set("hbase.zookeeper.property.clientPort",
+      Integer.toString(clientPort));
+  }
+
+  /**
+   * Start up a minicluster of hbase, optinally dfs, and zookeeper.
+   * Modifies Configuration.  Homes the cluster data directory under a random
+   * subdirectory in a directory under System property test.build.data.
+   * @param servers Number of servers to start up.  We'll start this many
+   * datanodes and regionservers.  If servers is > 1, then make sure
+   * hbase.regionserver.info.port is -1 (i.e. no ui per regionserver) otherwise
+   * bind errors.
+   * @throws Exception
+   * @see {@link #shutdownMiniCluster()}
+   */
+  public void startMiniCluster(final int servers)
+  throws Exception {
+    LOG.info("Starting up minicluster");
+    // If we already put up a cluster, fail.
+    if (this.clusterTestBuildDir != null) {
+      throw new IOException("Cluster already running at " +
+        this.clusterTestBuildDir);
+    }
+    // Now, home our cluster in a dir under build/test.  Give it a random name
+    // so can have many concurrent clusters running if we need to.  Need to
+    // amend the test.build.data System property.  Its what minidfscluster bases
+    // it data dir on.  Moding a System property is not the way to do concurrent
+    // instances -- another instance could grab the temporary
+    // value unintentionally -- but not anything can do about it at moment; its
+    // how the minidfscluster works.
+    String oldTestBuildDir =
+      System.getProperty(TEST_DIRECTORY_KEY, "build/test/data");
+    String randomStr = UUID.randomUUID().toString();
+    String clusterTestBuildDirStr = oldTestBuildDir + "." + randomStr;
+    this.clusterTestBuildDir =
+      new File(clusterTestBuildDirStr).getAbsoluteFile();
+    // Have it cleaned up on exit
+    this.clusterTestBuildDir.deleteOnExit();
+    // Set our random dir while minidfscluster is being constructed.
+    System.setProperty(TEST_DIRECTORY_KEY, clusterTestBuildDirStr);
+    // Bring up mini dfs cluster. This spews a bunch of warnings about missing
+    // scheme. TODO: fix.
+    // Complaints are 'Scheme is undefined for build/test/data/dfs/name1'.
+    this.dfsCluster = new MiniDFSCluster(0, this.conf, servers, true,
+      true, true, null, null, null, null);
+    // Restore System property. minidfscluster accesses content of
+    // the TEST_DIRECTORY_KEY to make bad blocks, a feature we are not using,
+    // but otherwise, just in constructor.
+    System.setProperty(TEST_DIRECTORY_KEY, oldTestBuildDir);
+    // Mangle conf so fs parameter points to minidfs we just started up
+    FileSystem fs = this.dfsCluster.getFileSystem();
+    this.conf.set("fs.defaultFS", fs.getUri().toString());
+    this.dfsCluster.waitClusterUp();
+
+    // It could be created before the cluster
+    if(this.zkCluster == null) {
+      startMiniZKCluster();
+    }
+
+    // Now do the mini hbase cluster.  Set the hbase.rootdir in config.
+    Path hbaseRootdir = fs.makeQualified(fs.getHomeDirectory());
+    this.conf.set(HConstants.HBASE_DIR, hbaseRootdir.toString());
+    fs.mkdirs(hbaseRootdir);
+    FSUtils.setVersion(fs, hbaseRootdir);
+    this.hbaseCluster = new MiniHBaseCluster(this.conf, servers);
+    // Don't leave here till we've done a successful scan of the .META.
+    HTable t = new HTable(this.conf, HConstants.META_TABLE_NAME);
+    ResultScanner s = t.getScanner(new Scan());
+    while (s.next() != null) continue;
+    LOG.info("Minicluster is up");
+  }
+
+  /**
+   * @throws IOException
+   * @see {@link #startMiniCluster(boolean, int)}
+   */
+  public void shutdownMiniCluster() throws IOException {
+    LOG.info("Shutting down minicluster");
+    if (this.hbaseCluster != null) {
+      this.hbaseCluster.shutdown();
+      // Wait till hbase is down before going on to shutdown zk.
+      this.hbaseCluster.join();
+    }
+    if (this.zkCluster != null) this.zkCluster.shutdown();
+    if (this.dfsCluster != null) {
+      // The below throws an exception per dn, AsynchronousCloseException.
+      this.dfsCluster.shutdown();
+    }
+    // Clean up our directory.
+    if (this.clusterTestBuildDir != null && this.clusterTestBuildDir.exists()) {
+      // Need to use deleteDirectory because File.delete required dir is empty.
+      if (!FSUtils.deleteDirectory(FileSystem.getLocal(this.conf),
+          new Path(this.clusterTestBuildDir.toString()))) {
+        LOG.warn("Failed delete of " + this.clusterTestBuildDir.toString());
+      }
+    }
+    LOG.info("Minicluster is down");
+  }
+
+  /**
+   * Flushes all caches in the mini hbase cluster
+   * @throws IOException
+   */
+  public void flush() throws IOException {
+    this.hbaseCluster.flushcache();
+  }
+
+
+  /**
+   * Create a table.
+   * @param tableName
+   * @param family
+   * @return An HTable instance for the created table.
+   * @throws IOException
+   */
+  public HTable createTable(byte[] tableName, byte[] family) 
+  throws IOException{
+    return createTable(tableName, new byte[][]{family});
+  }
+
+  /**
+   * Create a table.
+   * @param tableName
+   * @param families
+   * @return An HTable instance for the created table.
+   * @throws IOException
+   */
+  public HTable createTable(byte[] tableName, byte[][] families) 
+  throws IOException {
+    HTableDescriptor desc = new HTableDescriptor(tableName);
+    for(byte[] family : families) {
+      desc.addFamily(new HColumnDescriptor(family));
+    }
+    (new HBaseAdmin(getConfiguration())).createTable(desc);
+    return new HTable(getConfiguration(), tableName);
+  }
+
+  /**
+   * Create a table.
+   * @param tableName
+   * @param family
+   * @param numVersions
+   * @return An HTable instance for the created table.
+   * @throws IOException
+   */
+  public HTable createTable(byte[] tableName, byte[] family, int numVersions)
+  throws IOException {
+    return createTable(tableName, new byte[][]{family}, numVersions);
+  }
+
+  /**
+   * Create a table.
+   * @param tableName
+   * @param families
+   * @param numVersions
+   * @return An HTable instance for the created table.
+   * @throws IOException
+   */
+  public HTable createTable(byte[] tableName, byte[][] families,
+      int numVersions)
+  throws IOException {
+    HTableDescriptor desc = new HTableDescriptor(tableName);
+    for (byte[] family : families) {
+      HColumnDescriptor hcd = new HColumnDescriptor(family, numVersions,
+          HColumnDescriptor.DEFAULT_COMPRESSION,
+          HColumnDescriptor.DEFAULT_IN_MEMORY,
+          HColumnDescriptor.DEFAULT_BLOCKCACHE,
+          Integer.MAX_VALUE, HColumnDescriptor.DEFAULT_TTL,
+          false);
+      desc.addFamily(hcd);
+    }
+    (new HBaseAdmin(getConfiguration())).createTable(desc);
+    return new HTable(getConfiguration(), tableName);
+  }
+
+  /**
+   * Create a table.
+   * @param tableName
+   * @param families
+   * @param numVersions
+   * @return An HTable instance for the created table.
+   * @throws IOException
+   */
+  public HTable createTable(byte[] tableName, byte[][] families,
+      int[] numVersions)
+  throws IOException {
+    HTableDescriptor desc = new HTableDescriptor(tableName);
+    int i = 0;
+    for (byte[] family : families) {
+      HColumnDescriptor hcd = new HColumnDescriptor(family, numVersions[i],
+          HColumnDescriptor.DEFAULT_COMPRESSION,
+          HColumnDescriptor.DEFAULT_IN_MEMORY,
+          HColumnDescriptor.DEFAULT_BLOCKCACHE,
+          Integer.MAX_VALUE, HColumnDescriptor.DEFAULT_TTL,
+          false);
+      desc.addFamily(hcd);
+      i++;
+    }
+    (new HBaseAdmin(getConfiguration())).createTable(desc);
+    return new HTable(getConfiguration(), tableName);
+  }
+
+  /**
+   * Provide an existing table name to truncate
+   * @param tableName existing table
+   * @return HTable to that new table
+   * @throws IOException
+   */
+  public HTable truncateTable(byte [] tableName) throws IOException {
+    HTable table = new HTable(getConfiguration(), tableName);
+    Scan scan = new Scan();
+    ResultScanner resScan = table.getScanner(scan);
+    for(Result res : resScan) {
+      Delete del = new Delete(res.getRow());
+      table.delete(del);
+    }
+    return table;
+  }
+
+  /**
+   * Load table with rows from 'aaa' to 'zzz'.
+   * @param t Table
+   * @param f Family
+   * @return Count of rows loaded.
+   * @throws IOException
+   */
+  public int loadTable(final HTable t, final byte[] f) throws IOException {
+    byte[] k = new byte[3];
+    int rowCount = 0;
+    for (byte b1 = 'a'; b1 <= 'z'; b1++) {
+      for (byte b2 = 'a'; b2 <= 'z'; b2++) {
+        for (byte b3 = 'a'; b3 <= 'z'; b3++) {
+          k[0] = b1;
+          k[1] = b2;
+          k[2] = b3;
+          Put put = new Put(k);
+          put.add(f, null, k);
+          t.put(put);
+          rowCount++;
+        }
+      }
+    }
+    return rowCount;
+  }
+  
+  /**
+   * Creates many regions names "aaa" to "zzz".
+   * 
+   * @param table  The table to use for the data.
+   * @param columnFamily  The family to insert the data into.
+   * @throws IOException When creating the regions fails.
+   */
+  public void createMultiRegions(HTable table, byte[] columnFamily) 
+  throws IOException {
+    byte[][] KEYS = {
+      HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes("bbb"),
+      Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"), 
+      Bytes.toBytes("fff"), Bytes.toBytes("ggg"), Bytes.toBytes("hhh"),
+      Bytes.toBytes("iii"), Bytes.toBytes("jjj"), Bytes.toBytes("kkk"),
+      Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"),
+      Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"),
+      Bytes.toBytes("rrr"), Bytes.toBytes("sss"), Bytes.toBytes("ttt"),
+      Bytes.toBytes("uuu"), Bytes.toBytes("vvv"), Bytes.toBytes("www"),
+      Bytes.toBytes("xxx"), Bytes.toBytes("yyy")
+    };
+
+    HBaseConfiguration c = getConfiguration();
+    HTable meta = new HTable(c, HConstants.META_TABLE_NAME);
+    HTableDescriptor htd = table.getTableDescriptor();
+    if(!htd.hasFamily(columnFamily)) {
+      HColumnDescriptor hcd = new HColumnDescriptor(columnFamily);
+      htd.addFamily(hcd);
+    }
+    // remove empty region - this is tricky as the mini cluster during the test
+    // setup already has the "<tablename>,,123456789" row with an empty start 
+    // and end key. Adding the custom regions below adds those blindly, 
+    // including the new start region from empty to "bbb". lg 
+    List<byte[]> rows = getMetaTableRows();
+    // add custom ones
+    for (int i = 0; i < KEYS.length; i++) {
+      int j = (i + 1) % KEYS.length;
+      HRegionInfo hri = new HRegionInfo(table.getTableDescriptor(), 
+        KEYS[i], KEYS[j]);
+      Put put = new Put(hri.getRegionName());
+      put.add(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER, 
+        Writables.getBytes(hri));
+      meta.put(put);
+      LOG.info("createMultiRegions: inserted " + hri.toString());
+    }
+    // see comment above, remove "old" (or previous) single region
+    for (byte[] row : rows) {
+      LOG.info("createMultiRegions: deleting meta row -> " + 
+        Bytes.toStringBinary(row));
+      meta.delete(new Delete(row));
+    }
+    // flush cache of regions
+    HConnection conn = table.getConnection();
+    conn.clearRegionCache();
+  }
+
+  /**
+   * Returns all rows from the .META. table.
+   *
+   * @throws IOException When reading the rows fails.
+   */
+  public List<byte[]> getMetaTableRows() throws IOException {
+    HTable t = new HTable(this.conf, HConstants.META_TABLE_NAME);
+    List<byte[]> rows = new ArrayList<byte[]>();
+    ResultScanner s = t.getScanner(new Scan());
+    for (Result result : s) {
+      LOG.info("getMetaTableRows: row -> " + 
+        Bytes.toStringBinary(result.getRow()));
+      rows.add(result.getRow());
+    }
+    s.close();
+    return rows;
+  }
+
+  /**
+   * Removes all rows from the .META. in preparation to add custom ones.
+   *
+   * @throws IOException When removing the rows fails.
+   */
+  private void emptyMetaTable() throws IOException {
+    HTable t = new HTable(this.conf, HConstants.META_TABLE_NAME);
+    ArrayList<Delete> deletes = new ArrayList<Delete>();
+    ResultScanner s = t.getScanner(new Scan());
+    for (Result result : s) {
+      LOG.info("emptyMetaTable: remove row -> " + 
+        Bytes.toStringBinary(result.getRow()));
+      Delete del = new Delete(result.getRow());
+      deletes.add(del);
+    }
+    s.close();
+    t.delete(deletes);
+  }
+  
+  /**
+   * Starts a <code>MiniMRCluster</code> with a default number of 
+   * <code>TaskTracker</code>'s.
+   *
+   * @throws IOException When starting the cluster fails.
+   */
+  public void startMiniMapReduceCluster() throws IOException {
+    startMiniMapReduceCluster(2);
+  }
+  
+  /**
+   * Starts a <code>MiniMRCluster</code>.
+   *
+   * @param servers  The number of <code>TaskTracker</code>'s to start.
+   * @throws IOException When starting the cluster fails.
+   */
+  public void startMiniMapReduceCluster(final int servers) throws IOException {
+    LOG.info("Starting mini mapreduce cluster...");
+    // These are needed for the new and improved Map/Reduce framework
+    Configuration c = getConfiguration();
+    System.setProperty("hadoop.log.dir", c.get("hadoop.log.dir"));
+    c.set("mapred.output.dir", c.get("hadoop.tmp.dir"));
+    mrCluster = new MiniMRCluster(servers, 
+      FileSystem.get(c).getUri().toString(), 1);
+    LOG.info("Mini mapreduce cluster started");
+  }
+  
+  /**
+   * Stops the previously started <code>MiniMRCluster</code>. 
+   */
+  public void shutdownMiniMapReduceCluster() {
+    LOG.info("Stopping mini mapreduce cluster...");
+    if (mrCluster != null) {
+      mrCluster.shutdown();
+    }
+    LOG.info("Mini mapreduce cluster stopped");
+  }
+
+  /**
+   * Switches the logger for the given class to DEBUG level.
+   *
+   * @param clazz  The class for which to switch to debug logging.
+   */
+  public void enableDebug(Class<?> clazz) {
+    Log l = LogFactory.getLog(clazz);
+    if (l instanceof Log4JLogger) {
+      ((Log4JLogger) l).getLogger().setLevel(org.apache.log4j.Level.DEBUG);
+    } else if (l instanceof Jdk14Logger) {
+      ((Jdk14Logger) l).getLogger().setLevel(java.util.logging.Level.ALL);
+    }
+  }
+
+  /**
+   * Expire the Master's session
+   * @throws Exception
+   */
+  public void expireMasterSession() throws Exception {
+    HMaster master = hbaseCluster.getMaster();
+    expireSession(master.getZooKeeperWrapper());
+  }
+
+  /**
+   * Expire a region server's session
+   * @param index which RS
+   * @throws Exception
+   */
+  public void expireRegionServerSession(int index) throws Exception {
+    HRegionServer rs = hbaseCluster.getRegionServer(index);
+    expireSession(rs.getZooKeeperWrapper());
+  }
+
+  public void expireSession(ZooKeeperWrapper nodeZK) throws Exception{
+    ZooKeeperWrapper zkw = new ZooKeeperWrapper(conf, EmptyWatcher.instance);
+    String quorumServers = zkw.getQuorumServers();
+    int sessionTimeout = 5 * 1000; // 5 seconds
+
+    byte[] password = nodeZK.getSessionPassword();
+    long sessionID = nodeZK.getSessionID();
+
+    ZooKeeper zk = new ZooKeeper(quorumServers,
+        sessionTimeout, EmptyWatcher.instance, sessionID, password);
+    zk.close();
+
+    Thread.sleep(sessionTimeout * 5L);
+
+    new HTable(conf, HConstants.META_TABLE_NAME);
+  }
+
+  /**
+   * Get the HBase cluster.
+   * 
+   * @return hbase cluster
+   */
+  public MiniHBaseCluster getHBaseCluster() {
+    return hbaseCluster;
+  }
+  
+  /**
+   * Returns a HBaseAdmin instance.
+   *
+   * @return The HBaseAdmin instance.
+   * @throws MasterNotRunningException
+   */
+  public HBaseAdmin getHBaseAdmin() throws MasterNotRunningException {
+    if (hbaseAdmin == null) {
+      hbaseAdmin = new HBaseAdmin(getConfiguration());
+    }
+    return hbaseAdmin;
+  }
+  
+  /**
+   * Closes the named region. 
+   *
+   * @param regionName  The region to close.
+   * @throws IOException
+   */
+  public void closeRegion(String regionName) throws IOException {
+    closeRegion(Bytes.toBytes(regionName));
+  }
+  
+  /**
+   * Closes the named region. 
+   *
+   * @param regionName  The region to close.
+   * @throws IOException
+   */
+  public void closeRegion(byte[] regionName) throws IOException {
+    HBaseAdmin admin = getHBaseAdmin();
+    admin.closeRegion(regionName, (Object[]) null);
+  }
+  
+  /**
+   * Closes the region containing the given row. 
+   *
+   * @param row  The row to find the containing region.
+   * @param table  The table to find the region.
+   * @throws IOException
+   */
+  public void closeRegionByRow(String row, HTable table) throws IOException {
+    closeRegionByRow(Bytes.toBytes(row), table);
+  }
+
+  /**
+   * Closes the region containing the given row. 
+   *
+   * @param row  The row to find the containing region.
+   * @param table  The table to find the region.
+   * @throws IOException
+   */
+  public void closeRegionByRow(byte[] row, HTable table) throws IOException {
+    HRegionLocation hrl = table.getRegionLocation(row);
+    closeRegion(hrl.getRegionInfo().getRegionName());
+  }
+
+  public MiniZooKeeperCluster getZkCluster() {
+    return zkCluster;
+  }
+
+  public void setZkCluster(MiniZooKeeperCluster zkCluster) {
+    this.zkCluster = zkCluster;
+  }
+
+  public MiniDFSCluster getDFSCluster() {
+    return dfsCluster;
+  }
+}
\ No newline at end of file