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 2009/01/30 02:20:05 UTC

svn commit: r739112 - in /hadoop/hbase/trunk: ./ src/java/ src/java/org/apache/hadoop/hbase/ src/java/org/apache/hadoop/hbase/client/ src/java/org/apache/hadoop/hbase/ipc/ src/java/org/apache/hadoop/hbase/master/ src/java/org/apache/hadoop/hbase/region...

Author: stack
Date: Fri Jan 30 01:20:05 2009
New Revision: 739112

URL: http://svn.apache.org/viewvc?rev=739112&view=rev
Log:
HBASE-1144 Store the ROOT region location in Zookeeper

Added:
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/WatcherWrapper.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWrapper.java
    hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/MiniZooKeeperCluster.java
    hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestZooKeeper.java
Modified:
    hadoop/hbase/trunk/CHANGES.txt
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/HConstants.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HBaseRPCProtocolVersion.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterRegionInterface.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/HMaster.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/RegionManager.java
    hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
    hadoop/hbase/trunk/src/java/overview.html
    hadoop/hbase/trunk/src/test/log4j.properties
    hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/HBaseClusterTestCase.java
    hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestEmptyMetaInfo.java

Modified: hadoop/hbase/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/CHANGES.txt?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/CHANGES.txt (original)
+++ hadoop/hbase/trunk/CHANGES.txt Fri Jan 30 01:20:05 2009
@@ -2,6 +2,8 @@
 Release 0.20.0 - Unreleased
   INCOMPATIBLE CHANGES
    HBASE-1147  Modify the scripts to use Zookeeper
+   HBASE-1144  Store the ROOT region location in Zookeeper
+               (Nitay Joffe via Stack)
 
   BUG FIXES
    HBASE-1140  "ant clean test" fails (Nitay Joffe via Stack)

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/HConstants.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/HConstants.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/HConstants.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/HConstants.java Fri Jan 30 01:20:05 2009
@@ -78,6 +78,39 @@
   /** default port for master web api */
   static final int DEFAULT_MASTER_INFOPORT = 60010;
 
+  /** Name of ZooKeeper config file in conf/ directory. */
+  static final String ZOOKEEPER_CONFIG_NAME = "zoo.cfg";
+
+  /** Parameter name for ZooKeeper session timeout (in milliseconds). */
+  static final String ZOOKEEPER_SESSION_TIMEOUT = "zookeeper.session.timeout";
+  /** Default ZooKeeper session timeout. In milliseconds. */
+  static final int DEFAULT_ZOOKEEPER_SESSION_TIMEOUT = 10 * 1000;
+
+  /** Parameter name for number of times to retry writes to ZooKeeper. */
+  static final String ZOOKEEPER_RETRIES = "zookeeper.retries";
+  /** Default number of times to retry writes to ZooKeeper. */
+  static final int DEFAULT_ZOOKEEPER_RETRIES = 5;
+
+  /** Parameter name for ZooKeeper pause between retries. In milliseconds. */
+  static final String ZOOKEEPER_PAUSE = "zookeeper.pause";
+  /** Default ZooKeeper pause value. In milliseconds. */
+  static final int DEFAULT_ZOOKEEPER_PAUSE = 2 * 1000;
+
+  /** Parameter name for HBase parent ZNode in ZooKeeper. */
+  static final String ZOOKEEPER_PARENT_ZNODE = "zookeeper.znode.parent";
+  /** Default HBase parent ZNode in ZooKeeper. */
+  static final String DEFAULT_ZOOKEEPER_PARENT_ZNODE = "/hbase";
+
+  /** Parameter name for ZooKeeper ZNode storing root server location. */
+  static final String ZOOKEEPER_ROOT_SERVER_ZNODE = "zookeeper.znode.rootserver";
+  /** Default ZooKeeper ZNode storing root server location. */
+  static final String DEFAULT_ZOOKEEPER_ROOT_SERVER_ZNODE = "root-region-server";
+
+  /** Parameter name for ZooKeeper ZNode storing safe mode. */
+  static final String ZOOKEEPER_SAFE_MODE_ZNODE = "zookeeper.znode.safemode";
+  /** Default ZooKeeper ZNode storing safe mode. */
+  static final String DEFAULT_ZOOKEEPER_SAFE_MODE_ZNODE = "safe-mode";
+
   /** Parameter name for hbase.regionserver address. */
   static final String REGIONSERVER_ADDRESS = "hbase.regionserver";
   

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/client/HConnectionManager.java Fri Jan 30 01:20:05 2009
@@ -55,6 +55,7 @@
 import org.apache.hadoop.hbase.util.MetaUtils;
 import org.apache.hadoop.hbase.util.SoftValueSortedMap;
 import org.apache.hadoop.hbase.util.Writables;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
 import org.apache.hadoop.ipc.RemoteException;
 
 /**
@@ -140,7 +141,9 @@
     private final Map<Integer, SoftValueSortedMap<byte [], HRegionLocation>> 
       cachedRegionLocations =
         new HashMap<Integer, SoftValueSortedMap<byte [], HRegionLocation>>();
-    
+
+    private ZooKeeperWrapper zooKeeperWrapper;
+
     /** 
      * constructor
      * @param conf Configuration object
@@ -751,6 +754,13 @@
       return server;
     }
 
+    private synchronized ZooKeeperWrapper getZooKeeperWrapper() throws IOException {
+      if (zooKeeperWrapper == null) {
+        zooKeeperWrapper = new ZooKeeperWrapper(conf);
+      }
+      return zooKeeperWrapper;
+    }
+
     /*
      * Repeatedly try to find the root region by asking the master for where it is
      * @return HRegionLocation for root region if found
@@ -761,12 +771,22 @@
     private HRegionLocation locateRootRegion()
     throws IOException {
       getMaster();
+
+      // We lazily instantiate the ZooKeeper object because we don't want to
+      // make the constructor have to throw IOException or handle it itself.
+      ZooKeeperWrapper zooKeeperWrapper = getZooKeeperWrapper();
+
       HServerAddress rootRegionAddress = null;
       for (int tries = 0; tries < numRetries; tries++) {
         int localTimeouts = 0;
         // ask the master which server has the root region
         while (rootRegionAddress == null && localTimeouts < numRetries) {
-          rootRegionAddress = master.findRootRegion();
+          // Don't read root region until we're out of safe mode so we know
+          // that the meta regions have been assigned.
+          boolean outOfSafeMode = zooKeeperWrapper.checkOutOfSafeMode();
+          if (outOfSafeMode) {
+            rootRegionAddress = zooKeeperWrapper.readRootRegionLocation();
+          }
           if (rootRegionAddress == null) {
             try {
               if (LOG.isDebugEnabled()) {

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HBaseRPCProtocolVersion.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HBaseRPCProtocolVersion.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HBaseRPCProtocolVersion.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HBaseRPCProtocolVersion.java Fri Jan 30 01:20:05 2009
@@ -65,7 +65,10 @@
    * <li>Version 13: HBASE-847</li>
    * <li>Version 14: HBASE-900</li>
    * <li>Version 15: HRegionInterface.exists</li>
+   * <li>Version 16: Removed HMasterRegionInterface.getRootRegionLocation and
+   * HMasterInterface.findRootRegion. We use ZooKeeper to store root region
+   * location instead.</li>
    * </ul>
    */
-  public static final long versionID = 15L;
+  public static final long versionID = 16L;
 }

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterInterface.java Fri Jan 30 01:20:05 2009
@@ -22,7 +22,6 @@
 import java.io.IOException;
 
 import org.apache.hadoop.hbase.HColumnDescriptor;
-import org.apache.hadoop.hbase.HServerAddress;
 import org.apache.hadoop.hbase.HTableDescriptor;
 import org.apache.hadoop.io.Writable;
 
@@ -117,10 +116,4 @@
    * @throws IOException
    */
   public void shutdown() throws IOException;
-
-  /**
-   * Get the location of the root region
-   * @return address of server that serves the root region
-   */
-  public HServerAddress findRootRegion();
 }

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterRegionInterface.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterRegionInterface.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterRegionInterface.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/ipc/HMasterRegionInterface.java Fri Jan 30 01:20:05 2009
@@ -25,7 +25,6 @@
 import org.apache.hadoop.hbase.HServerInfo;
 import org.apache.hadoop.hbase.HMsg;
 import org.apache.hadoop.hbase.HRegionInfo;
-import org.apache.hadoop.hbase.HServerAddress;
 
 /**
  * HRegionServers interact with the HMasterRegionInterface to report on local 
@@ -62,11 +61,4 @@
   public HMsg[] regionServerReport(HServerInfo info, HMsg msgs[], 
     HRegionInfo mostLoadedRegions[])
   throws IOException;
-
-  /**
-   * @return Root region region server address. Unlike
-   * HMasterInterface.findRootRegion, does not wait until all regions are 
-   * assigned.
-   */
-  public HServerAddress getRootRegionLocation();
 }
\ No newline at end of file

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/HMaster.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/HMaster.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/HMaster.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/HMaster.java Fri Jan 30 01:20:05 2009
@@ -408,7 +408,7 @@
    */ 
   private boolean processToDoQueue() {
     RegionServerOperation op = null;
-    
+
     // block until the root region is online
     if (regionManager.getRootRegionLocation() != null) {
       // We can't process server shutdowns unless the root region is online
@@ -858,14 +858,6 @@
     }
   }
 
-  public HServerAddress findRootRegion() {
-    HServerAddress rootServer = null;
-    if (!regionManager.inSafeMode()) {
-      rootServer = regionManager.getRootRegionLocation();
-    }
-    return rootServer;
-  }
-
   /**
    * @return Server metrics
    */

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/RegionManager.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/RegionManager.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/RegionManager.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/master/RegionManager.java Fri Jan 30 01:20:05 2009
@@ -41,6 +41,7 @@
 import org.apache.hadoop.fs.FileStatus;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.fs.PathFilter;
+import org.apache.hadoop.hbase.HBaseConfiguration;
 import org.apache.hadoop.hbase.HConstants;
 import org.apache.hadoop.hbase.HServerAddress;
 import org.apache.hadoop.hbase.HServerInfo;
@@ -55,6 +56,7 @@
 import org.apache.hadoop.hbase.util.Threads;
 import org.apache.hadoop.hbase.io.BatchUpdate;
 import org.apache.hadoop.hbase.util.Writables;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
 
 /**
  * Class to manage assigning regions to servers, state of root and meta, etc.
@@ -126,23 +128,31 @@
       new TreeMap<byte[],Pair<HRegionInfo,HServerAddress>>
       (Bytes.BYTES_COMPARATOR));
 
-  RegionManager(HMaster master) {
+  private final ZooKeeperWrapper zooKeeperWrapper;
+  private final int zooKeeperNumRetries;
+  private final int zooKeeperPause;
+
+  RegionManager(HMaster master) throws IOException {
+    HBaseConfiguration conf = master.getConfiguration();
+
     this.master = master;
     this.historian = RegionHistorian.getInstance();
-    this.maxAssignInOneGo = this.master.getConfiguration().
-      getInt("hbase.regions.percheckin", 10);
-    this.slop = this.master.getConfiguration().getFloat("hbase.regions.slop",
-      (float)0.1);
+    this.maxAssignInOneGo = conf.getInt("hbase.regions.percheckin", 10);
+    this.slop = conf.getFloat("hbase.regions.slop", (float)0.1);
 
     // The root region
     rootScannerThread = new RootScanner(master);
 
     // Scans the meta table
     metaScannerThread = new MetaScanner(master);
-    
+
+    zooKeeperWrapper = new ZooKeeperWrapper(conf);
+    zooKeeperNumRetries = conf.getInt(ZOOKEEPER_RETRIES, DEFAULT_ZOOKEEPER_RETRIES);
+    zooKeeperPause = conf.getInt(ZOOKEEPER_PAUSE, DEFAULT_ZOOKEEPER_PAUSE);
+
     reassignRootRegion();
   }
-  
+
   void start() {
     Threads.setDaemonThreadRunning(rootScannerThread,
       "RegionManager.rootScanner");
@@ -540,6 +550,7 @@
     } catch(Exception iex) {
       LOG.warn("meta scanner", iex);
     }
+    zooKeeperWrapper.close();
   }
   
   /**
@@ -895,14 +906,30 @@
   public boolean isInitialMetaScanComplete() {
     return metaScannerThread.isInitialScanComplete();
   }
-  
+
+  private boolean tellZooKeeperOutOfSafeMode() {
+    for (int attempt = 0; attempt < zooKeeperNumRetries; ++attempt) {
+      if (zooKeeperWrapper.writeOutOfSafeMode()) {
+        return true;
+      }
+
+      sleep(attempt);
+    }
+
+    LOG.error("Failed to tell ZooKeeper we're out of safe mode after " +
+              zooKeeperNumRetries + " retries");
+
+    return false;
+  }
+
   /** 
    * @return true if the initial meta scan is complete and there are no
    * unassigned or pending regions
    */
   public boolean inSafeMode() {
     if (safeMode) {
-      if(isInitialMetaScanComplete() && regionsInTransition.size() == 0) {
+      if(isInitialMetaScanComplete() && regionsInTransition.size() == 0 &&
+         tellZooKeeperOutOfSafeMode()) {
         master.connection.unsetRootRegionLocation();
         safeMode = false;
         LOG.info("exiting safe mode");
@@ -955,11 +982,46 @@
     numberOfMetaRegions.incrementAndGet();
   }
 
+  private long getPauseTime(int tries) {
+    int attempt = tries;
+    if (attempt >= RETRY_BACKOFF.length) {
+      attempt = RETRY_BACKOFF.length - 1;
+    }
+    return this.zooKeeperPause * RETRY_BACKOFF[attempt];
+  }
+
+  private void sleep(int attempt) {
+    try {
+      Thread.sleep(getPauseTime(attempt));
+    } catch (InterruptedException e) {
+      // continue
+    }
+  }
+
+  private void writeRootRegionLocationToZooKeeper(HServerAddress address) {
+    for (int attempt = 0; attempt < zooKeeperNumRetries; ++attempt) {
+      if (zooKeeperWrapper.writeRootRegionLocation(address)) {
+        return;
+      }
+
+      sleep(attempt);
+    }
+
+    LOG.error("Failed to write root region location to ZooKeeper after " +
+              zooKeeperNumRetries + " retries, shutting down");
+
+    this.master.shutdown();
+  }
+
   /**
    * Set the root region location.
    * @param address Address of the region server where the root lives
+   * @throws IOException If there's a problem connection to ZooKeeper.
    */
-  public void setRootRegionLocation(HServerAddress address) {
+  public void setRootRegionLocation(HServerAddress address)
+  throws IOException {
+    writeRootRegionLocationToZooKeeper(address);
+
     synchronized (rootRegionLocation) {
       rootRegionLocation.set(new HServerAddress(address));
       rootRegionLocation.notifyAll();
@@ -1270,4 +1332,4 @@
       return Bytes.compareTo(getRegionName(), o.getRegionName());
     }
   }
-}
\ No newline at end of file
+}

Modified: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java (original)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java Fri Jan 30 01:20:05 2009
@@ -98,6 +98,7 @@
 import org.apache.hadoop.hbase.util.InfoServer;
 import org.apache.hadoop.hbase.util.Sleeper;
 import org.apache.hadoop.hbase.util.Threads;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
 import org.apache.hadoop.io.MapWritable;
 import org.apache.hadoop.io.Writable;
 import org.apache.hadoop.util.Progressable;
@@ -211,6 +212,8 @@
   final Map<String, InternalScanner> scanners =
     new ConcurrentHashMap<String, InternalScanner>();
 
+  private final ZooKeeperWrapper zooKeeperWrapper;
+
   /**
    * Starts a HRegionServer at the default location
    * @param conf
@@ -292,6 +295,8 @@
     for(int i = 0; i < nbBlocks; i++)  {
       reservedSpace.add(new byte[DEFAULT_SIZE_RESERVATION_BLOCK]);
     }
+
+    this.zooKeeperWrapper = new ZooKeeperWrapper(conf);
   }
 
   /**
@@ -310,7 +315,7 @@
       for (int tries = 0; !stopRequested.get() && isHealthy();) {
         // Try to get the root region location from the master.
         if (!haveRootRegion.get()) {
-          HServerAddress rootServer = hbaseMaster.getRootRegionLocation();
+          HServerAddress rootServer = zooKeeperWrapper.readRootRegionLocation();
           if (rootServer != null) {
             // By setting the root region location, we bypass the wait imposed on
             // HTable for all regions being assigned.

Added: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/WatcherWrapper.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/WatcherWrapper.java?rev=739112&view=auto
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/WatcherWrapper.java (added)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/WatcherWrapper.java Fri Jan 30 01:20:05 2009
@@ -0,0 +1,50 @@
+/**
+ * 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.zookeeper;
+
+import org.apache.zookeeper.WatchedEvent;
+import org.apache.zookeeper.Watcher;
+
+/**
+ * Place-holder Watcher.
+ * Does nothing currently.
+ */
+public class WatcherWrapper implements Watcher {
+  private final Watcher otherWatcher;
+
+  /**
+   * Construct with a Watcher to pass events to.
+   * @param otherWatcher Watcher to pass events to.
+   */
+  public WatcherWrapper(Watcher otherWatcher) {
+    this.otherWatcher = otherWatcher;
+  }
+
+  /**
+   * @param event WatchedEvent from ZooKeeper.
+   */
+  @Override
+  public void process(WatchedEvent event) {
+    if (otherWatcher != null) {
+      otherWatcher.process(event);
+    }
+  }
+
+}

Added: hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWrapper.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWrapper.java?rev=739112&view=auto
==============================================================================
--- hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWrapper.java (added)
+++ hadoop/hbase/trunk/src/java/org/apache/hadoop/hbase/zookeeper/ZooKeeperWrapper.java Fri Jan 30 01:20:05 2009
@@ -0,0 +1,377 @@
+/**
+ * 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.zookeeper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.Map.Entry;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.zookeeper.CreateMode;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.Watcher;
+import org.apache.zookeeper.ZooDefs.Ids;
+import org.apache.zookeeper.ZooKeeper;
+import org.apache.zookeeper.ZooKeeper.States;
+import org.apache.zookeeper.data.Stat;
+
+/**
+ * Wraps a ZooKeeper instance and adds HBase specific functionality.
+ *
+ * This class provides methods to:
+ * - read/write/delete the root region location in ZooKeeper.
+ * - set/check out of safe mode flag.
+ */
+public class ZooKeeperWrapper implements HConstants {
+  protected static final Log LOG = LogFactory.getLog(ZooKeeperWrapper.class);
+
+  // TODO: Replace this with ZooKeeper constant when ZOOKEEPER-277 is resolved.
+  private static final String ZNODE_PATH_SEPARATOR = "/";
+
+  private static String quorumServers = null;
+  static {
+    loadZooKeeperConfig();
+  }
+
+  private final ZooKeeper zooKeeper;
+  private final WatcherWrapper watcher;
+
+  private final String parentZNode;
+  private final String rootRegionZNode;
+  private final String outOfSafeModeZNode;
+
+  /**
+   * Create a ZooKeeperWrapper.
+   * @param conf HBaseConfiguration to read settings from.
+   * @throws IOException If a connection error occurs.
+   */
+  public ZooKeeperWrapper(HBaseConfiguration conf)
+  throws IOException {
+    this(conf, null);
+  }
+
+  /**
+   * Create a ZooKeeperWrapper.
+   * @param conf HBaseConfiguration to read settings from.
+   * @param watcher ZooKeeper watcher to register.
+   * @throws IOException If a connection error occurs.
+   */
+  public ZooKeeperWrapper(HBaseConfiguration conf, Watcher watcher)
+  throws IOException {
+    if (quorumServers == null) {
+      throw new IOException("Could not read quorum servers from " +
+                            ZOOKEEPER_CONFIG_NAME);
+    }
+
+    int sessionTimeout = conf.getInt(ZOOKEEPER_SESSION_TIMEOUT,
+                                     DEFAULT_ZOOKEEPER_SESSION_TIMEOUT);
+    this.watcher = new WatcherWrapper(watcher);
+    try {
+      zooKeeper = new ZooKeeper(quorumServers, sessionTimeout, this.watcher);
+    } catch (IOException e) {
+      LOG.error("Failed to create ZooKeeper object: " + e);
+      throw new IOException(e);
+    }
+
+    parentZNode = conf.get(ZOOKEEPER_PARENT_ZNODE,
+                           DEFAULT_ZOOKEEPER_PARENT_ZNODE);
+
+    String rootServerZNodeName = conf.get(ZOOKEEPER_ROOT_SERVER_ZNODE,
+                                          DEFAULT_ZOOKEEPER_ROOT_SERVER_ZNODE);
+    if (rootServerZNodeName.startsWith(ZNODE_PATH_SEPARATOR)) {
+      rootRegionZNode = rootServerZNodeName;
+    } else {
+      rootRegionZNode = parentZNode + ZNODE_PATH_SEPARATOR + rootServerZNodeName;
+    }
+
+    String outOfSafeModeZNodeName = conf.get(ZOOKEEPER_SAFE_MODE_ZNODE,
+                                             DEFAULT_ZOOKEEPER_SAFE_MODE_ZNODE);
+    if (outOfSafeModeZNodeName.startsWith(ZNODE_PATH_SEPARATOR)) {
+      outOfSafeModeZNode = outOfSafeModeZNodeName;
+    } else {
+      outOfSafeModeZNode = parentZNode + ZNODE_PATH_SEPARATOR +
+                           outOfSafeModeZNodeName;
+    }
+  }
+
+  /**
+   * This is for tests to directly set the ZooKeeper quorum servers.
+   * @param servers comma separated host:port ZooKeeper quorum servers.
+   */
+  public static void setQuorumServers(String servers) {
+    quorumServers = servers;
+  }
+
+  private static void loadZooKeeperConfig() {
+    InputStream inputStream =
+      ZooKeeperWrapper.class.getClassLoader().getResourceAsStream(ZOOKEEPER_CONFIG_NAME);
+    if (inputStream == null) {
+      LOG.error("fail to open ZooKeeper config file " + ZOOKEEPER_CONFIG_NAME);
+      return;
+    }
+
+    Properties properties = new Properties();
+    try {
+      properties.load(inputStream);
+    } catch (IOException e) {
+      LOG.error("fail to read properties from " + ZOOKEEPER_CONFIG_NAME);
+      return;
+    }
+
+    String clientPort = null;
+    List<String> servers = new ArrayList<String>();
+
+    // The clientPort option may come after the server.X hosts, so we need to
+    // grab everything and then create the final host:port comma separated list.
+    for (Entry<Object,Object> property : properties.entrySet()) {
+      String key = property.getKey().toString().trim();
+      String value = property.getValue().toString().trim();
+      if (key.equals("clientPort")) {
+        clientPort = value;
+      }
+      else if (key.startsWith("server.")) {
+        String host = value.substring(0, value.indexOf(':'));
+        servers.add(host);
+      }
+    }
+
+    if (clientPort == null) {
+      LOG.error("no clientPort found in " + ZOOKEEPER_CONFIG_NAME);
+      return;
+    }
+
+    // If no server.X lines exist, then we're using a single instance ZooKeeper
+    // on the master node.
+    if (servers.isEmpty()) {
+      HBaseConfiguration conf = new HBaseConfiguration();
+      String masterAddress = conf.get(MASTER_ADDRESS, DEFAULT_MASTER_ADDRESS);
+      String masterHost = "localhost";
+      if (!masterAddress.equals("local")) {
+        masterHost = masterAddress.substring(0, masterAddress.indexOf(':'));
+      }
+      servers.add(masterHost);
+    }
+
+    StringBuilder hostPortBuilder = new StringBuilder();
+    for (int i = 0; i < servers.size(); ++i) {
+      String host = servers.get(i);
+      if (i > 0) {
+        hostPortBuilder.append(',');
+      }
+      hostPortBuilder.append(host);
+      hostPortBuilder.append(':');
+      hostPortBuilder.append(clientPort);
+    }
+
+    quorumServers = hostPortBuilder.toString();
+    LOG.info("Quorum servers: " + quorumServers);
+  }
+
+  /** @return true if currently connected to ZooKeeper, false otherwise. */
+  public boolean isConnected() {
+    return zooKeeper.getState() == States.CONNECTED;
+  }
+
+  /**
+   * Read location of server storing root region.
+   * @return HServerAddress pointing to server serving root region or null if
+   *         there was a problem reading the ZNode.
+   */
+  public HServerAddress readRootRegionLocation() {
+    byte[] data;
+    try {
+      data = zooKeeper.getData(rootRegionZNode, false, null);
+    } catch (InterruptedException e) {
+      return null;
+    } catch (KeeperException e) {
+      return null;
+    }
+
+    String addressString = Bytes.toString(data);
+    LOG.debug("Read ZNode " + rootRegionZNode + " got " + addressString);
+    HServerAddress address = new HServerAddress(addressString);
+    return address;
+  }
+
+  private boolean ensureParentZNodeExists() {
+    try {
+      zooKeeper.create(parentZNode, new byte[0],
+                       Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+      LOG.debug("Created ZNode " + parentZNode);
+      return true;
+    } catch (KeeperException.NodeExistsException e) {
+      return true;      // ok, move on.
+    } catch (KeeperException e) {
+      LOG.warn("Failed to create " + parentZNode + ": " + e);
+    } catch (InterruptedException e) {
+      LOG.warn("Failed to create " + parentZNode + ": " + e);
+    }
+
+    return false;
+  }
+
+  /**
+   * Delete ZNode containing root region location.
+   * @return true if operation succeeded, false otherwise.
+   */
+  public boolean deleteRootRegionLocation()  {
+    if (!ensureParentZNodeExists()) {
+      return false;
+    }
+
+    try {
+      zooKeeper.delete(rootRegionZNode, -1);
+      LOG.debug("Deleted ZNode " + rootRegionZNode);
+      return true;
+    } catch (KeeperException.NoNodeException e) {
+      return true;    // ok, move on.
+    } catch (KeeperException e) {
+      LOG.warn("Failed to delete " + rootRegionZNode + ": " + e);
+    } catch (InterruptedException e) {
+      LOG.warn("Failed to delete " + rootRegionZNode + ": " + e);
+    }
+
+    return false;
+  }
+
+  private boolean createRootRegionLocation(String address) {
+    byte[] data = Bytes.toBytes(address);
+    try {
+      zooKeeper.create(rootRegionZNode, data, Ids.OPEN_ACL_UNSAFE,
+                       CreateMode.PERSISTENT);
+      LOG.debug("Created ZNode " + rootRegionZNode + " with data " + address);
+      return true;
+    } catch (KeeperException e) {
+      LOG.warn("Failed to create root region in ZooKeeper: " + e);
+    } catch (InterruptedException e) {
+      LOG.warn("Failed to create root region in ZooKeeper: " + e);
+    }
+
+    return false;
+  }
+
+  private boolean updateRootRegionLocation(String address) {
+    byte[] data = Bytes.toBytes(address);
+    try {
+      zooKeeper.setData(rootRegionZNode, data, -1);
+      LOG.debug("SetData of ZNode " + rootRegionZNode + " with " + address);
+      return true;
+    } catch (KeeperException e) {
+      LOG.warn("Failed to set root region location in ZooKeeper: " + e);
+    } catch (InterruptedException e) {
+      LOG.warn("Failed to set root region location in ZooKeeper: " + e);
+    }
+
+    return false;
+  }
+
+  /**
+   * Write root region location to ZooKeeper. If address is null, delete ZNode.
+   * containing root region location.
+   * @param address HServerAddress to write to ZK.
+   * @return true if operation succeeded, false otherwise.
+   */
+  public boolean writeRootRegionLocation(HServerAddress address) {
+    if (address == null) {
+      return deleteRootRegionLocation();
+    }
+
+    if (!ensureParentZNodeExists()) {
+      return false;
+    }
+
+    String addressString = address.toString();
+
+    if (checkExistenceOf(rootRegionZNode)) {
+      return updateRootRegionLocation(addressString);
+    }
+
+    return createRootRegionLocation(addressString);
+  }
+
+  /**
+   * Check if we're out of safe mode. Being out of safe mode is signified by an
+   * ephemeral ZNode existing in ZooKeeper.
+   * @return true if we're out of safe mode, false otherwise.
+   */
+  public boolean checkOutOfSafeMode() {
+    if (!ensureParentZNodeExists()) {
+      return false;
+    }
+
+    return checkExistenceOf(outOfSafeModeZNode);
+  }
+
+  /**
+   * Create ephemeral ZNode signifying that we're out of safe mode.
+   * @return true if ephemeral ZNode created successfully, false otherwise.
+   */
+  public boolean writeOutOfSafeMode() {
+    if (!ensureParentZNodeExists()) {
+      return false;
+    }
+
+    try {
+      zooKeeper.create(outOfSafeModeZNode, new byte[0], Ids.OPEN_ACL_UNSAFE,
+                       CreateMode.EPHEMERAL);
+      LOG.debug("Wrote out of safe mode");
+      return true;
+    } catch (InterruptedException e) {
+      LOG.warn("Failed to create out of safe mode in ZooKeeper: " + e);
+    } catch (KeeperException e) {
+      LOG.warn("Failed to create out of safe mode in ZooKeeper: " + e);
+    }
+
+    return false;
+  }
+
+  private boolean checkExistenceOf(String path) {
+    Stat stat = null;
+    try {
+      stat = zooKeeper.exists(path, false);
+    } catch (KeeperException e) {
+      LOG.warn("checking existence of " + path, e);
+    } catch (InterruptedException e) {
+      LOG.warn("checking existence of " + path, e);
+    }
+
+    return stat != null;
+  }
+
+  /**
+   * Close this ZooKeeper session.
+   */
+  public void close() {
+    try {
+      zooKeeper.close();
+      LOG.debug("Closed connection with ZooKeeper");
+    } catch (InterruptedException e) {
+      LOG.warn("Failed to close connection with ZooKeeper");
+    }
+  }
+}

Modified: hadoop/hbase/trunk/src/java/overview.html
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/java/overview.html?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/java/overview.html (original)
+++ hadoop/hbase/trunk/src/java/overview.html Fri Jan 30 01:20:05 2009
@@ -68,9 +68,16 @@
 </p>
 
 <h2><a name="distributed">Distributed Operation</a></h2>
-<p>Distributed mode requires an instance of the Hadoop Distributed File System (DFS).
+<p>Distributed mode requires an instance of the Hadoop Distributed File System (DFS) and a ZooKeeper cluster.
 See the Hadoop <a href="http://lucene.apache.org/hadoop/api/overview-summary.html#overview_description">
-requirements and instructions</a> for how to set up a DFS.</p> 
+requirements and instructions</a> for how to set up a DFS.
+See the ZooKeeeper <a href="http://hadoop.apache.org/zookeeper/docs/current/zookeeperStarted.html">Getting Started Guide</a>
+for information about the ZooKeeper distributed coordination service.
+If you do not configure a ZooKeeper cluster, HBase will manage a single instance
+ZooKeeper service for you running on the master node.
+This is intended for development and local testing only.
+It SHOULD NOT be used in a fully-distributed production operation.
+</p>
 
 <h3><a name="pseudo-distrib">Pseudo-Distributed Operation</a></h3>
 <p>A pseudo-distributed operation is simply a distributed operation run on a single host.  
@@ -130,6 +137,14 @@
 <code>regionserver</code> lists all the hosts running HRegionServers, one host per line  (This file 
 in HBase is like the hadoop slaves file at <code>${HADOOP_HOME}/conf/slaves</code>).
 </p>
+<p>
+Furthermore, you should configure a distributed ZooKeeper cluster.
+The ZooKeeper configuration file is stored at <code>${HBASE_HOME}/conf/zoo.cfg</code>.
+See the ZooKeeper <a href="http://hadoop.apache.org/zookeeper/docs/current/zookeeperStarted.html"> Getting Started Guide</a> for information about the format and options of that file.
+Specifically, look at the <a href="http://hadoop.apache.org/zookeeper/docs/current/zookeeperStarted.html#sc_RunningReplicatedZooKeeper">Running Replicated ZooKeeper</a> section.
+In <code>${HBASE_HOME}/conf/hbase-env.sh</code>, set <code>HBASE_MANAGES_ZK=false</code> to tell HBase not to manage its own single instance ZooKeeper service.
+</p>
+
 <p>Of note, if you have made <i>HDFS client configuration</i> on your hadoop cluster, hbase will not
 see this configuration unless you do one of the following:
 <ul>

Modified: hadoop/hbase/trunk/src/test/log4j.properties
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/test/log4j.properties?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/test/log4j.properties (original)
+++ hadoop/hbase/trunk/src/test/log4j.properties Fri Jan 30 01:20:05 2009
@@ -44,3 +44,4 @@
 
 log4j.logger.org.apache.hadoop=WARN
 log4j.logger.org.apache.hadoop.hbase=DEBUG
+log4j.logger.org.apache.zookeeper=WARN

Modified: hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/HBaseClusterTestCase.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/HBaseClusterTestCase.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/HBaseClusterTestCase.java (original)
+++ hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/HBaseClusterTestCase.java Fri Jan 30 01:20:05 2009
@@ -19,6 +19,8 @@
  */
 package org.apache.hadoop.hbase;
 
+import java.io.File;
+import java.io.IOException;
 import java.io.PrintWriter;
 
 import org.apache.commons.logging.Log;
@@ -39,8 +41,11 @@
   private static final Log LOG = LogFactory.getLog(HBaseClusterTestCase.class);
   protected MiniHBaseCluster cluster;
   protected MiniDFSCluster dfsCluster;
+  protected MiniZooKeeperCluster zooKeeperCluster;
   protected int regionServers;
+  protected int numZooKeeperPeers;
   protected boolean startDfs;
+  private boolean openMetaTable = true;
 
   /** default constructor */
   public HBaseClusterTestCase() {
@@ -57,8 +62,7 @@
     this(regionServers, true);
   }
   
-  /**
-   * Start a MiniHBaseCluster with regionServers region servers in-process to
+  /**  in-process to
    * start with. Optionally, startDfs indicates if a MiniDFSCluster should be
    * started. If startDfs is false, the assumption is that an external DFS is
    * configured in hbase-site.xml and is already started, or you have started a
@@ -71,8 +75,13 @@
     super();
     this.startDfs = startDfs;
     this.regionServers = regionServers;
+    this.numZooKeeperPeers = 1;
   }
-  
+
+  protected void setOpenMetaTable(boolean val) {
+    openMetaTable = val;
+  }
+
   /**
    * Run after dfs is ready but before hbase cluster is started up.
    */
@@ -84,10 +93,20 @@
    * Actually start the MiniHBase instance.
    */
   protected void hBaseClusterSetup() throws Exception {
+    File testDir = new File(getUnitTestdir(getName()).toString());
+
+    // Note that this is done before we create the MiniHBaseCluster because we
+    // need to edit the config to add the ZooKeeper servers.
+    this.zooKeeperCluster = new MiniZooKeeperCluster(conf);
+    this.zooKeeperCluster.startup(numZooKeeperPeers, testDir);
+
     // start the mini cluster
     this.cluster = new MiniHBaseCluster(conf, regionServers);
-    // opening the META table ensures that cluster is running
-    new HTable(conf, HConstants.META_TABLE_NAME);
+
+    if (openMetaTable) {
+      // opening the META table ensures that cluster is running
+      new HTable(conf, HConstants.META_TABLE_NAME);
+    }
   }
   
   /**
@@ -120,10 +139,10 @@
     
       // run the pre-cluster setup
       preHBaseClusterSetup();    
-    
+
       // start the instance
       hBaseClusterSetup();
-      
+
       // run post-cluster setup
       postHBaseClusterSetup();
     } catch (Exception e) {
@@ -131,6 +150,9 @@
       if (cluster != null) {
         cluster.shutdown();
       }
+      if (zooKeeperCluster != null) {
+        zooKeeperCluster.shutdown();
+      }
       if (dfsCluster != null) {
         shutdownDfs(dfsCluster);
       }
@@ -140,6 +162,10 @@
 
   @Override
   protected void tearDown() throws Exception {
+    if (!openMetaTable) {
+      // open the META table now to ensure cluster is running before shutdown.
+      new HTable(conf, HConstants.META_TABLE_NAME);
+    }
     super.tearDown();
     try {
       HConnectionManager.deleteConnectionInfo(conf, true);
@@ -149,6 +175,11 @@
         } catch (Exception e) {
           LOG.warn("Closing mini dfs", e);
         }
+        try {
+          this.zooKeeperCluster.shutdown();
+        } catch (IOException e) {
+          LOG.warn("Shutting down ZooKeeper cluster", e);
+        }
       }
       if (startDfs) {
         shutdownDfs(dfsCluster);

Added: hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/MiniZooKeeperCluster.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/MiniZooKeeperCluster.java?rev=739112&view=auto
==============================================================================
--- hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/MiniZooKeeperCluster.java (added)
+++ hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/MiniZooKeeperCluster.java Fri Jan 30 01:20:05 2009
@@ -0,0 +1,312 @@
+/**
+ * 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 java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.util.HashMap;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
+import org.apache.zookeeper.server.NIOServerCnxn;
+import org.apache.zookeeper.server.ServerStats;
+import org.apache.zookeeper.server.ZooKeeperServer;
+import org.apache.zookeeper.server.persistence.FileTxnLog;
+import org.apache.zookeeper.server.quorum.QuorumPeer;
+import org.apache.zookeeper.server.quorum.QuorumStats;
+import org.apache.zookeeper.server.quorum.QuorumPeer.QuorumServer;
+
+/**
+ * TODO: Most of the code in this class is ripped from ZooKeeper tests. Instead
+ * of redoing it, we should contribute updates to their code which let us more
+ * easily access testing helper objects.
+ */
+public class MiniZooKeeperCluster {
+  private static final Log LOG = LogFactory.getLog(MiniZooKeeperCluster.class);
+
+  // TODO: make this more configurable?
+  private static final int CLIENT_PORT_START = 21810; // use non-standard port
+  private static final int LEADER_PORT_START = 31810; // use non-standard port
+  private static final int TICK_TIME = 2000;
+  private static final int INIT_LIMIT = 3;
+  private static final int SYNC_LIMIT = 3;
+  private static final int CONNECTION_TIMEOUT = 30000;
+
+  private HBaseConfiguration conf;
+
+  private boolean started;
+  private int numPeers;
+  private File baseDir;
+
+  // for distributed mode.
+  private QuorumPeer[] quorumPeers;
+  // for standalone mode.
+  private NIOServerCnxn.Factory standaloneServerFactory;
+
+  /**
+   * @param conf
+   * @throws IOException
+   */
+  public MiniZooKeeperCluster(HBaseConfiguration conf) throws IOException {
+    this.conf = conf;
+    this.started = false;
+  }
+
+  // / XXX: From o.a.zk.t.ClientBase
+  private static void setupTestEnv() {
+    // during the tests we run with 100K prealloc in the logs.
+    // on windows systems prealloc of 64M was seen to take ~15seconds
+    // resulting in test failure (client timeout on first session).
+    // set env and directly in order to handle static init/gc issues
+    System.setProperty("zookeeper.preAllocSize", "100");
+    FileTxnLog.setPreallocSize(100);
+  }
+
+  /**
+   * @param numPeers
+   * @param baseDir
+   * @throws IOException
+   * @throws InterruptedException
+   */
+  public void startup(int numPeers, File baseDir) throws IOException,
+      InterruptedException {
+    setupTestEnv();
+
+    shutdown();
+
+    if (numPeers < 1) {
+      return;
+    }
+
+    this.numPeers = numPeers;
+    this.baseDir = baseDir.getAbsoluteFile();
+    if (isDistributed()) {
+      startupDistributed();
+    } else {
+      startupStandalone();
+    }
+
+    started = true;
+  }
+
+  private void startupStandalone() throws IOException, InterruptedException {
+    ServerStats.registerAsConcrete();
+
+    File dir = new File(baseDir, "zookeeper-standalone");
+    recreateDir(dir);
+
+    ZooKeeperServer server = new ZooKeeperServer(dir, dir, TICK_TIME);
+    standaloneServerFactory = new NIOServerCnxn.Factory(CLIENT_PORT_START);
+    standaloneServerFactory.startup(server);
+
+    ZooKeeperWrapper.setQuorumServers("localhost:" + CLIENT_PORT_START);
+
+    if (!waitForServerUp(CLIENT_PORT_START, CONNECTION_TIMEOUT)) {
+      throw new IOException("Waiting for startup of standalone server");
+    }
+  }
+
+  // XXX: From o.a.zk.t.QuorumTest.startServers
+  private void startupDistributed() throws IOException {
+    QuorumStats.registerAsConcrete();
+
+    // Create map of peers
+    HashMap<Long, QuorumServer> peers = new HashMap<Long, QuorumServer>();
+    for (int id = 1; id <= numPeers; ++id) {
+      int port = LEADER_PORT_START + id;
+      InetSocketAddress addr = new InetSocketAddress("localhost", port);
+      QuorumServer server = new QuorumServer(id, addr);
+      peers.put(Long.valueOf(id), server);
+    }
+
+    StringBuffer serversBuffer = new StringBuffer();
+
+    // Initialize each quorum peer.
+    quorumPeers = new QuorumPeer[numPeers];
+    for (int id = 1; id <= numPeers; ++id) {
+      File dir = new File(baseDir, "zookeeper-peer-" + id);
+      recreateDir(dir);
+
+      int port = CLIENT_PORT_START + id;
+      quorumPeers[id - 1] = new QuorumPeer(peers, dir, dir, port, 0, id,
+          TICK_TIME, INIT_LIMIT, SYNC_LIMIT);
+
+      if (id > 1) {
+        serversBuffer.append(",");
+      }
+      serversBuffer.append("localhost:" + port);
+    }
+
+    String servers = serversBuffer.toString();
+    ZooKeeperWrapper.setQuorumServers(servers);
+
+    // Start quorum peer threads.
+    for (QuorumPeer qp : quorumPeers) {
+      qp.start();
+    }
+
+    // Wait for quorum peers to be up before going on.
+    for (int id = 1; id <= numPeers; ++id) {
+      int port = CLIENT_PORT_START + id;
+      if (!waitForServerUp(port, CONNECTION_TIMEOUT)) {
+        throw new IOException("Waiting for startup of peer " + id);
+      }
+    }
+  }
+
+  private void recreateDir(File dir) throws IOException {
+    if (dir.exists()) {
+      FileUtil.fullyDelete(dir);
+    }
+    try {
+      dir.mkdirs();
+    } catch (SecurityException e) {
+      throw new IOException("creating dir: " + dir, e);
+    }
+  }
+
+  /**
+   * @throws IOException
+   * @throws InterruptedException
+   */
+  public void shutdown() throws IOException, InterruptedException {
+    if (!started) {
+      return;
+    }
+
+    if (isDistributed()) {
+      shutdownDistributed();
+    } else {
+      shutdownStandalone();
+    }
+
+    started = false;
+  }
+
+  private boolean isDistributed() {
+    return numPeers > 1;
+  }
+
+  private void shutdownDistributed() throws IOException, InterruptedException {
+    for (QuorumPeer qp : quorumPeers) {
+      qp.shutdown();
+      qp.join(CONNECTION_TIMEOUT);
+      if (qp.isAlive()) {
+        throw new IOException("QuorumPeer " + qp.getId()
+            + " failed to shutdown");
+      }
+    }
+
+    for (int id = 1; id <= quorumPeers.length; ++id) {
+      int port = CLIENT_PORT_START + id;
+      if (!waitForServerDown(port, CONNECTION_TIMEOUT)) {
+        throw new IOException("Waiting for shutdown of peer " + id);
+      }
+    }
+
+    ServerStats.unregister();
+  }
+
+  private void shutdownStandalone() throws IOException {
+    standaloneServerFactory.shutdown();
+    if (!waitForServerDown(CLIENT_PORT_START, CONNECTION_TIMEOUT)) {
+      throw new IOException("Waiting for shutdown of standalone server");
+    }
+    ServerStats.unregister();
+  }
+
+  // XXX: From o.a.zk.t.ClientBase
+  private static boolean waitForServerDown(int port, long timeout) {
+    long start = System.currentTimeMillis();
+    while (true) {
+      try {
+        Socket sock = new Socket("localhost", port);
+        try {
+          OutputStream outstream = sock.getOutputStream();
+          outstream.write("stat".getBytes());
+          outstream.flush();
+        } finally {
+          sock.close();
+        }
+      } catch (IOException e) {
+        return true;
+      }
+
+      if (System.currentTimeMillis() > start + timeout) {
+        break;
+      }
+      try {
+        Thread.sleep(250);
+      } catch (InterruptedException e) {
+        // ignore
+      }
+    }
+    return false;
+  }
+
+  // XXX: From o.a.zk.t.ClientBase
+  private static boolean waitForServerUp(int port, long timeout) {
+    long start = System.currentTimeMillis();
+    while (true) {
+      try {
+        Socket sock = new Socket("localhost", port);
+        BufferedReader reader = null;
+        try {
+          OutputStream outstream = sock.getOutputStream();
+          outstream.write("stat".getBytes());
+          outstream.flush();
+
+          Reader isr = new InputStreamReader(sock.getInputStream());
+          reader = new BufferedReader(isr);
+          String line = reader.readLine();
+          if (line != null && line.startsWith("Zookeeper version:")) {
+            return true;
+          }
+        } finally {
+          sock.close();
+          if (reader != null) {
+            reader.close();
+          }
+        }
+      } catch (IOException e) {
+        // ignore as this is expected
+        LOG.info("server localhost:" + port + " not up " + e);
+      }
+
+      if (System.currentTimeMillis() > start + timeout) {
+        break;
+      }
+      try {
+        Thread.sleep(250);
+      } catch (InterruptedException e) {
+        // ignore
+      }
+    }
+    return false;
+  }
+}

Modified: hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestEmptyMetaInfo.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestEmptyMetaInfo.java?rev=739112&r1=739111&r2=739112&view=diff
==============================================================================
--- hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestEmptyMetaInfo.java (original)
+++ hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestEmptyMetaInfo.java Fri Jan 30 01:20:05 2009
@@ -22,6 +22,7 @@
 
 import java.io.IOException;
 
+import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.hbase.client.HTable;
 import org.apache.hadoop.hbase.client.Scanner;
 import org.apache.hadoop.hbase.io.BatchUpdate;

Added: hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestZooKeeper.java
URL: http://svn.apache.org/viewvc/hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestZooKeeper.java?rev=739112&view=auto
==============================================================================
--- hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestZooKeeper.java (added)
+++ hadoop/hbase/trunk/src/test/org/apache/hadoop/hbase/TestZooKeeper.java Fri Jan 30 01:20:05 2009
@@ -0,0 +1,65 @@
+/**
+ * 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 java.io.IOException;
+
+import org.apache.hadoop.hbase.client.HTable;
+import org.apache.hadoop.hbase.master.HMaster;
+import org.apache.hadoop.hbase.zookeeper.ZooKeeperWrapper;
+
+/**
+ *
+ */
+public class TestZooKeeper extends HBaseClusterTestCase {
+  @Override
+  protected void setUp() throws Exception {
+    setOpenMetaTable(false);
+    super.setUp();
+  }
+
+  /** 
+   * @throws IOException
+   */
+  public void testWritesRootRegionLocation() throws IOException {
+    ZooKeeperWrapper zooKeeper = new ZooKeeperWrapper(conf);
+
+    boolean outOfSafeMode = zooKeeper.checkOutOfSafeMode();
+    assertFalse(outOfSafeMode);
+
+    HServerAddress zooKeeperRootAddress = zooKeeper.readRootRegionLocation();
+    assertNull(zooKeeperRootAddress);
+
+    HMaster master = cluster.getMaster();
+    HServerAddress masterRootAddress = master.getRootRegionLocation();
+    assertNull(masterRootAddress);
+
+    new HTable(conf, HConstants.META_TABLE_NAME);
+
+    outOfSafeMode = zooKeeper.checkOutOfSafeMode();
+    assertTrue(outOfSafeMode);
+
+    zooKeeperRootAddress = zooKeeper.readRootRegionLocation();
+    assertNotNull(zooKeeperRootAddress);
+
+    masterRootAddress = master.getRootRegionLocation();
+    assertEquals(masterRootAddress, zooKeeperRootAddress);
+  }
+}