hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From st...@apache.org
Subject svn commit: r991397 [11/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/o...
Date Tue, 31 Aug 2010 23:51:50 GMT
Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java?rev=991397&r1=991396&r2=991397&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/HQuorumPeer.java Tue Aug 31 23:51:44 2010
@@ -19,21 +19,8 @@
  */
 package org.apache.hadoop.hbase.zookeeper;
 
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.HBaseConfiguration;
-import org.apache.hadoop.hbase.HConstants;
-import org.apache.hadoop.net.DNS;
-import org.apache.hadoop.util.StringUtils;
-import org.apache.zookeeper.server.ServerConfig;
-import org.apache.zookeeper.server.ZooKeeperServerMain;
-import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
-import org.apache.zookeeper.server.quorum.QuorumPeerMain;
-
 import java.io.File;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.PrintWriter;
 import java.net.InetAddress;
 import java.net.NetworkInterface;
@@ -41,29 +28,27 @@ import java.net.UnknownHostException;
 import java.util.ArrayList;
 import java.util.Enumeration;
 import java.util.List;
-import java.util.Map.Entry;
 import java.util.Properties;
+import java.util.Map.Entry;
+
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.net.DNS;
+import org.apache.hadoop.util.StringUtils;
+import org.apache.zookeeper.server.ServerConfig;
+import org.apache.zookeeper.server.ZooKeeperServerMain;
+import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
+import org.apache.zookeeper.server.quorum.QuorumPeerMain;
 
 /**
  * HBase's version of ZooKeeper's QuorumPeer. When HBase is set to manage
  * ZooKeeper, this class is used to start up QuorumPeer instances. By doing
  * things in here rather than directly calling to ZooKeeper, we have more
- * control over the process. Currently, this class allows us to parse the
+ * control over the process. This class uses {@link ZKConfig} to parse the
  * zoo.cfg and inject variables from HBase's site.xml configuration in.
  */
 public class HQuorumPeer {
-  private static final Log LOG = LogFactory.getLog(HQuorumPeer.class);
-
-  private static final String VARIABLE_START = "${";
-  private static final int VARIABLE_START_LENGTH = VARIABLE_START.length();
-  private static final String VARIABLE_END = "}";
-  private static final int VARIABLE_END_LENGTH = VARIABLE_END.length();
-
-  private static final String ZK_CFG_PROPERTY = "hbase.zookeeper.property.";
-  private static final int ZK_CFG_PROPERTY_SIZE = ZK_CFG_PROPERTY.length();
-  private static final String ZK_CLIENT_PORT_KEY = ZK_CFG_PROPERTY
-      + "clientPort";
-
+  
   /**
    * Parse ZooKeeper configuration from HBase XML config and run a QuorumPeer.
    * @param args String[] of command line arguments. Not used.
@@ -71,7 +56,7 @@ public class HQuorumPeer {
   public static void main(String[] args) {
     Configuration conf = HBaseConfiguration.create();
     try {
-      Properties zkProperties = makeZKProps(conf);
+      Properties zkProperties = ZKConfig.makeZKProps(conf);
       writeMyID(zkProperties);
       QuorumPeerConfig zkConfig = new QuorumPeerConfig();
       zkConfig.parseProperties(zkProperties);
@@ -158,195 +143,4 @@ public class HQuorumPeer {
     w.println(myId);
     w.close();
   }
-
-  /**
-   * Make a Properties object holding ZooKeeper config equivalent to zoo.cfg.
-   * If there is a zoo.cfg in the classpath, simply read it in. Otherwise parse
-   * the corresponding config options from the HBase XML configs and generate
-   * the appropriate ZooKeeper properties.
-   * @param conf Configuration to read from.
-   * @return Properties holding mappings representing ZooKeeper zoo.cfg file.
-   */
-  public static Properties makeZKProps(Configuration conf) {
-    // First check if there is a zoo.cfg in the CLASSPATH. If so, simply read
-    // it and grab its configuration properties.
-    ClassLoader cl = HQuorumPeer.class.getClassLoader();
-    final InputStream inputStream =
-      cl.getResourceAsStream(HConstants.ZOOKEEPER_CONFIG_NAME);
-    if (inputStream != null) {
-      try {
-        return parseZooCfg(conf, inputStream);
-      } catch (IOException e) {
-        LOG.warn("Cannot read " + HConstants.ZOOKEEPER_CONFIG_NAME +
-                 ", loading from XML files", e);
-      }
-    }
-
-    // Otherwise, use the configuration options from HBase's XML files.
-    Properties zkProperties = new Properties();
-
-    // Directly map all of the hbase.zookeeper.property.KEY properties.
-    for (Entry<String, String> entry : conf) {
-      String key = entry.getKey();
-      if (key.startsWith(ZK_CFG_PROPERTY)) {
-        String zkKey = key.substring(ZK_CFG_PROPERTY_SIZE);
-        String value = entry.getValue();
-        // If the value has variables substitutions, need to do a get.
-        if (value.contains(VARIABLE_START)) {
-          value = conf.get(key);
-        }
-        zkProperties.put(zkKey, value);
-      }
-    }
-
-    // If clientPort is not set, assign the default
-    if (zkProperties.getProperty(ZK_CLIENT_PORT_KEY) == null) {
-      zkProperties.put(ZK_CLIENT_PORT_KEY,
-                       HConstants.DEFAULT_ZOOKEPER_CLIENT_PORT);
-    }
-
-    // Create the server.X properties.
-    int peerPort = conf.getInt("hbase.zookeeper.peerport", 2888);
-    int leaderPort = conf.getInt("hbase.zookeeper.leaderport", 3888);
-
-    final String[] serverHosts = conf.getStrings(HConstants.ZOOKEEPER_QUORUM,
-                                                 "localhost");
-    for (int i = 0; i < serverHosts.length; ++i) {
-      String serverHost = serverHosts[i];
-      String address = serverHost + ":" + peerPort + ":" + leaderPort;
-      String key = "server." + i;
-      zkProperties.put(key, address);
-    }
-
-    return zkProperties;
-  }
-  
-  /**
-   * Return the ZK Quorum servers string given zk properties returned by 
-   * makeZKProps
-   * @param properties
-   * @return
-   */
-  public static String getZKQuorumServersString(Properties properties) {
-    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.
-    boolean anyValid = false;
-    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);
-        try {
-          //noinspection ResultOfMethodCallIgnored
-          InetAddress.getByName(host);
-          anyValid = true;
-        } catch (UnknownHostException e) {
-          LOG.warn(StringUtils.stringifyException(e));
-        }
-      }
-    }
-
-    if (!anyValid) {
-      LOG.error("no valid quorum servers found in " + HConstants.ZOOKEEPER_CONFIG_NAME);
-      return null;
-    }
-
-    if (clientPort == null) {
-      LOG.error("no clientPort found in " + HConstants.ZOOKEEPER_CONFIG_NAME);
-      return null;
-    }
-
-    if (servers.isEmpty()) {
-      LOG.fatal("No server.X lines found in conf/zoo.cfg. HBase must have a " +
-                "ZooKeeper cluster configured for its operation.");
-      return null;
-    }
-
-    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);
-    }
-
-    return hostPortBuilder.toString();
-  }
-
-  /**
-   * Parse ZooKeeper's zoo.cfg, injecting HBase Configuration variables in.
-   * This method is used for testing so we can pass our own InputStream.
-   * @param conf HBaseConfiguration to use for injecting variables.
-   * @param inputStream InputStream to read from.
-   * @return Properties parsed from config stream with variables substituted.
-   * @throws IOException if anything goes wrong parsing config
-   */
-  public static Properties parseZooCfg(Configuration conf,
-      InputStream inputStream) throws IOException {
-    Properties properties = new Properties();
-    try {
-      properties.load(inputStream);
-    } catch (IOException e) {
-      final String msg = "fail to read properties from "
-        + HConstants.ZOOKEEPER_CONFIG_NAME;
-      LOG.fatal(msg);
-      throw new IOException(msg, e);
-    }
-    for (Entry<Object, Object> entry : properties.entrySet()) {
-      String value = entry.getValue().toString().trim();
-      String key = entry.getKey().toString().trim();
-      StringBuilder newValue = new StringBuilder();
-      int varStart = value.indexOf(VARIABLE_START);
-      int varEnd = 0;
-      while (varStart != -1) {
-        varEnd = value.indexOf(VARIABLE_END, varStart);
-        if (varEnd == -1) {
-          String msg = "variable at " + varStart + " has no end marker";
-          LOG.fatal(msg);
-          throw new IOException(msg);
-        }
-        String variable = value.substring(varStart + VARIABLE_START_LENGTH, varEnd);
-
-        String substituteValue = System.getProperty(variable);
-        if (substituteValue == null) {
-          substituteValue = conf.get(variable);
-        }
-        if (substituteValue == null) {
-          String msg = "variable " + variable + " not set in system property "
-                     + "or hbase configs";
-          LOG.fatal(msg);
-          throw new IOException(msg);
-        }
-
-        newValue.append(substituteValue);
-
-        varEnd += VARIABLE_END_LENGTH;
-        varStart = value.indexOf(VARIABLE_START, varEnd);
-      }
-      // Special case for 'hbase.cluster.distributed' property being 'true'
-      if (key.startsWith("server.")) {
-        if (conf.get(HConstants.CLUSTER_DISTRIBUTED).equals(HConstants.CLUSTER_IS_DISTRIBUTED)
-            && value.startsWith("localhost")) {
-          String msg = "The server in zoo.cfg cannot be set to localhost " +
-              "in a fully-distributed setup because it won't be reachable. " +
-              "See \"Getting Started\" for more information.";
-          LOG.fatal(msg);
-          throw new IOException(msg);
-        }
-      }
-      newValue.append(value.substring(varEnd));
-      properties.setProperty(key, newValue.toString());
-    }
-    return properties;
-  }
 }

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaNodeTracker.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaNodeTracker.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaNodeTracker.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MetaNodeTracker.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,73 @@
+/**
+ * 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.zookeeper;
+
+import java.io.IOException;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.catalog.CatalogTracker;
+
+/**
+ * Tracks the unassigned zookeeper node used by the META table.
+ * <p>
+ * A callback is made into a {@link CatalogTracker} when META completes a new
+ * assignment.
+ * <p>
+ * If META is already assigned when instantiating this class, you will not
+ * receive any notification for that assignment.  You will receive a
+ * notification after META has been successfully assigned to a new location.
+ */
+public class MetaNodeTracker extends ZooKeeperListener {
+  private static final Log LOG = LogFactory.getLog(MetaNodeTracker.class);
+
+  private final String node;
+
+  /** Catalog tracker to notify when META has a new assignment completed. */
+  private final CatalogTracker catalogTracker;
+
+  /**
+   * Creates a meta node tracker.
+   * @param watcher
+   * @param abortable
+   */
+  public MetaNodeTracker(ZooKeeperWatcher watcher,
+      CatalogTracker catalogTracker) {
+    super(watcher);
+    this.catalogTracker = catalogTracker;
+    node = ZKUtil.joinZNode(watcher.assignmentZNode,
+            HRegionInfo.FIRST_META_REGIONINFO.getEncodedName());
+  }
+
+  @Override
+  public void nodeDeleted(String path) {
+    if(path.equals(node)) {
+      LOG.info("Detected completed assignment of META, notifying catalog " +
+          "tracker");
+      try {
+        catalogTracker.waitForMetaServerConnectionDefault();
+      } catch (IOException e) {
+        LOG.warn("Tried to reset META server location after seeing the " +
+            "completion of a new META assignment but got an IOE", e);
+      }
+    }
+  }
+}
\ No newline at end of file

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/MiniZooKeeperCluster.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,225 @@
+/*
+ * 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.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.BindException;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.fs.FileUtil;
+import org.apache.zookeeper.server.NIOServerCnxn;
+import org.apache.zookeeper.server.ZooKeeperServer;
+import org.apache.zookeeper.server.persistence.FileTxnLog;
+
+/**
+ * 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);
+
+  private static final int TICK_TIME = 2000;
+  private static final int CONNECTION_TIMEOUT = 30000;
+
+  private boolean started;
+  private int clientPort = 21810; // use non-standard port
+
+  private NIOServerCnxn.Factory standaloneServerFactory;
+  private int tickTime = 0;
+
+  /** Create mini ZooKeeper cluster. */
+  public MiniZooKeeperCluster() {
+    this.started = false;
+  }
+
+  public void setClientPort(int clientPort) {
+    this.clientPort = clientPort;
+  }
+
+  public int getClientPort() {
+    return clientPort;
+  }
+
+  public void setTickTime(int tickTime) {
+    this.tickTime = tickTime;
+  }
+
+  // / 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 baseDir
+   * @return ClientPort server bound to.
+   * @throws IOException
+   * @throws InterruptedException
+   */
+  public int startup(File baseDir) throws IOException,
+      InterruptedException {
+
+    setupTestEnv();
+
+    shutdown();
+
+    File dir = new File(baseDir, "zookeeper").getAbsoluteFile();
+    recreateDir(dir);
+
+    int tickTimeToUse;
+    if (this.tickTime > 0) {
+      tickTimeToUse = this.tickTime;
+    } else {
+      tickTimeToUse = TICK_TIME;
+    }
+    ZooKeeperServer server = new ZooKeeperServer(dir, dir, tickTimeToUse);
+    while (true) {
+      try {
+        standaloneServerFactory =
+          new NIOServerCnxn.Factory(new InetSocketAddress(clientPort));
+      } catch (BindException e) {
+        LOG.info("Failed binding ZK Server to client port: " + clientPort);
+        //this port is already in use. try to use another
+        clientPort++;
+        continue;
+      }
+      break;
+    }
+    standaloneServerFactory.startup(server);
+
+    if (!waitForServerUp(clientPort, CONNECTION_TIMEOUT)) {
+      throw new IOException("Waiting for startup of standalone server");
+    }
+
+    started = true;
+    LOG.info("Started MiniZK Server on client port: " + clientPort);
+    return clientPort;
+  }
+
+  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
+   */
+  public void shutdown() throws IOException {
+    if (!started) {
+      return;
+    }
+
+    standaloneServerFactory.shutdown();
+    if (!waitForServerDown(clientPort, CONNECTION_TIMEOUT)) {
+      throw new IOException("Waiting for shutdown of standalone server");
+    }
+
+    started = false;
+  }
+
+  // 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;
+  }
+}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RegionServerTracker.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RegionServerTracker.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RegionServerTracker.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RegionServerTracker.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,101 @@
+/**
+ * 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.zookeeper;
+
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.HServerInfo;
+import org.apache.hadoop.hbase.master.ServerManager;
+import org.apache.zookeeper.KeeperException;
+
+/**
+ * Tracks the online region servers via ZK.
+ *
+ * <p>Handling of new RSs checking in is done via RPC.  This class
+ * is only responsible for watching for expired nodes.  It handles
+ * listening for changes in the RS node list and watching each node.
+ *
+ * <p>If an RS node gets deleted, this automatically handles calling of
+ * {@link ServerManager#expireServer(org.apache.hadoop.hbase.HServerInfo)}.
+ */
+public class RegionServerTracker extends ZooKeeperListener {
+  private static final Log LOG = LogFactory.getLog(RegionServerTracker.class);
+
+  private ServerManager serverManager;
+  private Abortable abortable;
+
+  public RegionServerTracker(ZooKeeperWatcher watcher,
+      Abortable abortable, ServerManager serverManager) {
+    super(watcher);
+    this.abortable = abortable;
+    this.serverManager = serverManager;
+  }
+
+  /**
+   * Starts the tracking of online RegionServers.
+   *
+   * <p>All RSs will be tracked after this method is called.
+   *
+   * @throws KeeperException
+   */
+  public void start() throws KeeperException {
+    watcher.registerListener(this);
+    ZKUtil.watchAndGetNewChildren(watcher, watcher.rsZNode);
+  }
+
+  @Override
+  public void nodeDeleted(String path) {
+    if(path.startsWith(watcher.rsZNode)) {
+      String serverName = ZKUtil.getNodeName(path);
+      LOG.info("RegionServer ephemeral node deleted, processing expiration [" +
+          serverName + "]");
+      HServerInfo hsi = serverManager.getServerInfo(serverName);
+      if(hsi == null) {
+        LOG.info("No HServerInfo found for " + serverName);
+        return;
+      }
+      serverManager.expireServer(hsi);
+    }
+  }
+
+  @Override
+  public void nodeChildrenChanged(String path) {
+    if(path.equals(watcher.rsZNode)) {
+      try {
+        ZKUtil.watchAndGetNewChildren(watcher, watcher.rsZNode);
+      } catch (KeeperException e) {
+        abortable.abort("Unexpected zk exception getting RS nodes", e);
+      }
+    }
+  }
+
+  /**
+   * Gets the online servers.
+   * @return list of online servers from zk
+   * @throws KeeperException
+   */
+  public List<HServerAddress> getOnlineServers() throws KeeperException {
+    return ZKUtil.listChildrenAndGetAsAddresses(watcher, watcher.rsZNode);
+  }
+}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RootRegionTracker.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RootRegionTracker.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RootRegionTracker.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/RootRegionTracker.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,84 @@
+/**
+ * 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.zookeeper;
+
+import org.apache.hadoop.hbase.Abortable;
+import org.apache.hadoop.hbase.HServerAddress;
+import org.apache.hadoop.hbase.catalog.RootLocationEditor;
+import org.apache.hadoop.hbase.regionserver.RegionServerServices;
+import org.apache.hadoop.hbase.util.Bytes;
+
+/**
+ * Tracks the root region server location node in zookeeper.
+ * Root region location is set by {@link RootLocationEditor} usually called
+ * out of {@link RegionServerServices#postOpenDeployTasks(org.apache.hadoop.hbase.regionserver.HRegion, org.apache.hadoop.hbase.catalog.CatalogTracker)}
+ */
+public class RootRegionTracker extends ZooKeeperNodeTracker {
+  /**
+   * Creates a root region location tracker.
+   *
+   * <p>After construction, use {@link #start} to kick off tracking.
+   *
+   * @param watcher
+   * @param abortable
+   */
+  public RootRegionTracker(ZooKeeperWatcher watcher, Abortable abortable) {
+    super(watcher, watcher.rootServerZNode, abortable);
+  }
+
+  /**
+   * Checks if the root region location is available.
+   * @return true if root region location is available, false if not
+   */
+  public boolean isLocationAvailable() {
+    return super.getData() != null;
+  }
+
+  /**
+   * Gets the root region location, if available.  Null if not.  Does not block.
+   * @return server address for server hosting root region, null if none available
+   * @throws InterruptedException 
+   */
+  public HServerAddress getRootRegionLocation() throws InterruptedException {
+    return dataToHServerAddress(super.getData());
+  }
+
+  /**
+   * Gets the root region location, if available, and waits for up to the
+   * specified timeout if not immediately available.
+   * @param timeout maximum time to wait, in millis, use {@link ZooKeeperNodeTracker#NO_TIMEOUT} for
+   * forever
+   * @return server address for server hosting root region, null if timed out
+   * @throws InterruptedException if interrupted while waiting
+   */
+  public HServerAddress waitRootRegionLocation(long timeout)
+  throws InterruptedException {
+    return dataToHServerAddress(super.blockUntilAvailable(timeout));
+  }
+
+  /*
+   * @param data
+   * @return Returns null if <code>data</code> is null else converts passed data
+   * to an HServerAddress instance.
+   */
+  private static HServerAddress dataToHServerAddress(final byte [] data) {
+    return data == null ? null: new HServerAddress(Bytes.toString(data));
+  }
+}
\ No newline at end of file

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKAssign.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKAssign.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKAssign.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKAssign.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,659 @@
+/**
+ * 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.zookeeper;
+
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.executor.RegionTransitionData;
+import org.apache.hadoop.hbase.executor.EventHandler.EventType;
+import org.apache.zookeeper.KeeperException;
+import org.apache.zookeeper.KeeperException.Code;
+import org.apache.zookeeper.KeeperException.NoNodeException;
+import org.apache.zookeeper.KeeperException.NodeExistsException;
+import org.apache.zookeeper.data.Stat;
+
+/**
+ * Utility class for doing region assignment in ZooKeeper.  This class extends
+ * stuff done in {@link ZKUtil} to cover specific assignment operations.
+ * <p>
+ * Contains only static methods and constants.
+ * <p>
+ * Used by both the Master and RegionServer.
+ * <p>
+ * All valid transitions outlined below:
+ * <p>
+ * <b>MASTER</b>
+ * <ol>
+ *   <li>
+ *     Master creates an unassigned node as OFFLINE.
+ *     - Cluster startup and table enabling.
+ *   </li>
+ *   <li>
+ *     Master forces an existing unassigned node to OFFLINE.
+ *     - RegionServer failure.
+ *     - Allows transitions from all states to OFFLINE.
+ *   </li>
+ *   <li>
+ *     Master deletes an unassigned node that was in a OPENED state.
+ *     - Normal region transitions.  Besides cluster startup, no other deletions
+ *     of unassigned nodes is allowed.
+ *   </li>
+ *   <li>
+ *     Master deletes all unassigned nodes regardless of state.
+ *     - Cluster startup before any assignment happens.
+ *   </li>
+ * </ol>
+ * <p>
+ * <b>REGIONSERVER</b>
+ * <ol>
+ *   <li>
+ *     RegionServer creates an unassigned node as CLOSING.
+ *     - All region closes will do this in response to a CLOSE RPC from Master.
+ *     - A node can never be transitioned to CLOSING, only created.
+ *   </li>
+ *   <li>
+ *     RegionServer transitions an unassigned node from CLOSING to CLOSED.
+ *     - Normal region closes.  CAS operation.
+ *   </li>
+ *   <li>
+ *     RegionServer transitions an unassigned node from OFFLINE to OPENING.
+ *     - All region opens will do this in response to an OPEN RPC from the Master.
+ *     - Normal region opens.  CAS operation.
+ *   </li>
+ *   <li>
+ *     RegionServer transitions an unassigned node from OPENING to OPENED.
+ *     - Normal region opens.  CAS operation.
+ *   </li>
+ * </ol>
+ */
+public class ZKAssign {
+
+  /**
+   * Gets the full path node name for the unassigned node for the specified
+   * region.
+   * @param zkw zk reference
+   * @param regionName region name
+   * @return full path node name
+   */
+  private static String getNodeName(ZooKeeperWatcher zkw, String regionName) {
+    return ZKUtil.joinZNode(zkw.assignmentZNode, regionName);
+  }
+
+  /**
+   * Gets the region name from the full path node name of an unassigned node.
+   * @param path full zk path
+   * @return region name
+   */
+  public static String getRegionName(ZooKeeperWatcher zkw, String path) {
+    return path.substring(zkw.assignmentZNode.length()+1);
+  }
+
+  // Master methods
+
+  /**
+   * Creates a new unassigned node in the OFFLINE state for the specified region.
+   *
+   * <p>Does not transition nodes from other states.  If a node already exists
+   * for this region, a {@link NodeExistsException} will be thrown.
+   *
+   * <p>Sets a watcher on the unassigned region node if the method is successful.
+   *
+   * <p>This method should only be used during cluster startup and the enabling
+   * of a table.
+   *
+   * @param zkw zk reference
+   * @param region region to be created as offline
+   * @param serverName server event originates from
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NodeExistsException if node already exists
+   */
+  public static void createNodeOffline(ZooKeeperWatcher zkw, HRegionInfo region,
+      String serverName)
+  throws KeeperException, KeeperException.NodeExistsException {
+    createNodeOffline(zkw, region, serverName, EventType.M2ZK_REGION_OFFLINE);
+  }
+
+  public static void createNodeOffline(ZooKeeperWatcher zkw, HRegionInfo region,
+      String serverName, final EventType event)
+  throws KeeperException, KeeperException.NodeExistsException {
+    zkw.debug("Creating an unassigned node for " + region.getEncodedName() +
+        " in an OFFLINE state");
+    RegionTransitionData data = new RegionTransitionData(event,
+      region.getRegionName(), serverName);
+    synchronized(zkw.getNodes()) {
+      String node = getNodeName(zkw, region.getEncodedName());
+      zkw.getNodes().add(node);
+      ZKUtil.createAndWatch(zkw, node, data.getBytes());
+    }
+  }
+
+  /**
+   * Forces an existing unassigned node to the OFFLINE state for the specified
+   * region.
+   *
+   * <p>Does not create a new node.  If a node does not already exist for this
+   * region, a {@link NoNodeException} will be thrown.
+   *
+   * <p>Sets a watcher on the unassigned region node if the method is
+   * successful.
+   *
+   * <p>This method should only be used during recovery of regionserver failure.
+   *
+   * @param zkw zk reference
+   * @param region region to be forced as offline
+   * @param serverName server event originates from
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NoNodeException if node does not exist
+   */
+  public static void forceNodeOffline(ZooKeeperWatcher zkw, HRegionInfo region,
+      String serverName)
+  throws KeeperException, KeeperException.NoNodeException {
+    zkw.debug("Forcing an existing unassigned node for " +
+        region.getEncodedName() + " to an OFFLINE state");
+    RegionTransitionData data = new RegionTransitionData(
+        EventType.M2ZK_REGION_OFFLINE, region.getRegionName(), serverName);
+    synchronized(zkw.getNodes()) {
+      String node = getNodeName(zkw, region.getEncodedName());
+      zkw.getNodes().add(node);
+      ZKUtil.setData(zkw, node, data.getBytes());
+    }
+  }
+
+
+  /**
+   * Creates or force updates an unassigned node to the OFFLINE state for the
+   * specified region.
+   * <p>
+   * Attempts to create the node but if it exists will force it to transition to
+   * and OFFLINE state.
+   *
+   * <p>Sets a watcher on the unassigned region node if the method is
+   * successful.
+   *
+   * <p>This method should be used when assigning a region.
+   *
+   * @param zkw zk reference
+   * @param region region to be created as offline
+   * @param serverName server event originates from
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NodeExistsException if node already exists
+   */
+  public static boolean createOrForceNodeOffline(ZooKeeperWatcher zkw,
+      HRegionInfo region, String serverName)
+  throws KeeperException {
+    zkw.debug("Creating or updating an unassigned node for " +
+        region.getEncodedName() + " with an OFFLINE state");
+    RegionTransitionData data = new RegionTransitionData(
+        EventType.M2ZK_REGION_OFFLINE, region.getRegionName(), serverName);
+    synchronized(zkw.getNodes()) {
+      String node = getNodeName(zkw, region.getEncodedName());
+      zkw.getNodes().add(node);
+      int version = ZKUtil.checkExists(zkw, node);
+      if(version == -1) {
+        ZKUtil.createAndWatch(zkw, node, data.getBytes());
+        return true;
+      } else {
+        return ZKUtil.setData(zkw, node, data.getBytes(), version);
+      }
+    }
+  }
+
+  /**
+   * Deletes an existing unassigned node that is in the OPENED state for the
+   * specified region.
+   *
+   * <p>If a node does not already exist for this region, a
+   * {@link NoNodeException} will be thrown.
+   *
+   * <p>No watcher is set whether this succeeds or not.
+   *
+   * <p>Returns false if the node was not in the proper state but did exist.
+   *
+   * <p>This method is used during normal region transitions when a region
+   * finishes successfully opening.  This is the Master acknowledging completion
+   * of the specified regions transition.
+   *
+   * @param zkw zk reference
+   * @param region opened region to be deleted from zk
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NoNodeException if node does not exist
+   */
+  public static boolean deleteOpenedNode(ZooKeeperWatcher zkw,
+      String regionName)
+  throws KeeperException, KeeperException.NoNodeException {
+    return deleteNode(zkw, regionName, EventType.RS2ZK_REGION_OPENED);
+  }
+
+  /**
+   * Deletes an existing unassigned node that is in the CLOSED state for the
+   * specified region.
+   *
+   * <p>If a node does not already exist for this region, a
+   * {@link NoNodeException} will be thrown.
+   *
+   * <p>No watcher is set whether this succeeds or not.
+   *
+   * <p>Returns false if the node was not in the proper state but did exist.
+   *
+   * <p>This method is used during table disables when a region finishes
+   * successfully closing.  This is the Master acknowledging completion
+   * of the specified regions transition to being closed.
+   *
+   * @param zkw zk reference
+   * @param region closed region to be deleted from zk
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NoNodeException if node does not exist
+   */
+  public static boolean deleteClosedNode(ZooKeeperWatcher zkw,
+      String regionName)
+  throws KeeperException, KeeperException.NoNodeException {
+    return deleteNode(zkw, regionName, EventType.RS2ZK_REGION_CLOSED);
+  }
+
+  /**
+   * Deletes an existing unassigned node that is in the CLOSING state for the
+   * specified region.
+   *
+   * <p>If a node does not already exist for this region, a
+   * {@link NoNodeException} will be thrown.
+   *
+   * <p>No watcher is set whether this succeeds or not.
+   *
+   * <p>Returns false if the node was not in the proper state but did exist.
+   *
+   * <p>This method is used during table disables when a region finishes
+   * successfully closing.  This is the Master acknowledging completion
+   * of the specified regions transition to being closed.
+   *
+   * @param zkw zk reference
+   * @param region closing region to be deleted from zk
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NoNodeException if node does not exist
+   */
+  public static boolean deleteClosingNode(ZooKeeperWatcher zkw,
+      String regionName)
+  throws KeeperException, KeeperException.NoNodeException {
+    return deleteNode(zkw, regionName, EventType.RS2ZK_REGION_CLOSING);
+  }
+
+  /**
+   * Deletes an existing unassigned node that is in the specified state for the
+   * specified region.
+   *
+   * <p>If a node does not already exist for this region, a
+   * {@link NoNodeException} will be thrown.
+   *
+   * <p>No watcher is set whether this succeeds or not.
+   *
+   * <p>Returns false if the node was not in the proper state but did exist.
+   *
+   * <p>This method is used during table disables when a region finishes
+   * successfully closing.  This is the Master acknowledging completion
+   * of the specified regions transition to being closed.
+   *
+   * @param zkw zk reference
+   * @param region region to be deleted from zk
+   * @param expectedState state region must be in for delete to complete
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NoNodeException if node does not exist
+   */
+  private static boolean deleteNode(ZooKeeperWatcher zkw, String regionName,
+      EventType expectedState)
+  throws KeeperException, KeeperException.NoNodeException {
+    zkw.debug("Deleting an existing unassigned node for " + regionName +
+        " that is in expected state " + expectedState);
+    String node = getNodeName(zkw, regionName);
+    Stat stat = new Stat();
+    byte [] bytes = ZKUtil.getDataNoWatch(zkw, node, stat);
+    if(bytes == null) {
+      throw KeeperException.create(Code.NONODE);
+    }
+    RegionTransitionData data = RegionTransitionData.fromBytes(bytes);
+    if(!data.getEventType().equals(expectedState)) {
+      zkw.warn("Attempting to delete an unassigned node in " + expectedState +
+          " state but node is in " + data.getEventType() + " state");
+      return false;
+    }
+    synchronized(zkw.getNodes()) {
+      // TODO: Does this go here or only if we successfully delete node?
+      zkw.getNodes().remove(node);
+      if(!ZKUtil.deleteNode(zkw, node, stat.getVersion())) {
+        zkw.warn("Attempting to delete an unassigned node in " + expectedState +
+            " state but " +
+            "after verifying it was in OPENED state, we got a version mismatch");
+        return false;
+      }
+      return true;
+    }
+  }
+
+  /**
+   * Deletes all unassigned nodes regardless of their state.
+   *
+   * <p>No watchers are set.
+   *
+   * <p>This method is used by the Master during cluster startup to clear out
+   * any existing state from other cluster runs.
+   *
+   * @param zkw zk reference
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  public static void deleteAllNodes(ZooKeeperWatcher zkw)
+  throws KeeperException {
+    zkw.debug("Deleting any existing unassigned nodes");
+    ZKUtil.deleteChildrenRecursively(zkw, zkw.assignmentZNode);
+  }
+
+  // RegionServer methods
+
+  /**
+   * Creates a new unassigned node in the CLOSING state for the specified
+   * region.
+   *
+   * <p>Does not transition nodes from any states.  If a node already exists
+   * for this region, a {@link NodeExistsException} will be thrown.
+   *
+   * <p>If creation is successful, returns the version number of the CLOSING
+   * node created.
+   *
+   * <p>Does not set any watches.
+   *
+   * <p>This method should only be used by a RegionServer when initiating a
+   * close of a region after receiving a CLOSE RPC from the Master.
+   *
+   * @param zkw zk reference
+   * @param region region to be created as closing
+   * @param serverName server event originates from
+   * @return version of node after transition, -1 if unsuccessful transition
+   * @throws KeeperException if unexpected zookeeper exception
+   * @throws KeeperException.NodeExistsException if node already exists
+   */
+  public static int createNodeClosing(ZooKeeperWatcher zkw, HRegionInfo region,
+      String serverName)
+  throws KeeperException, KeeperException.NodeExistsException {
+    zkw.debug("Creating an unassigned node for " + region.getEncodedName() +
+    " in a CLOSING state");
+    RegionTransitionData data = new RegionTransitionData(
+        EventType.RS2ZK_REGION_CLOSING, region.getRegionName(), serverName);
+    synchronized(zkw.getNodes()) {
+      String node = getNodeName(zkw, region.getEncodedName());
+      zkw.getNodes().add(node);
+      return ZKUtil.createAndWatch(zkw, node, data.getBytes());
+    }
+  }
+
+  /**
+   * Transitions an existing unassigned node for the specified region which is
+   * currently in the CLOSING state to be in the CLOSED state.
+   *
+   * <p>Does not transition nodes from other states.  If for some reason the
+   * node could not be transitioned, the method returns -1.  If the transition
+   * is successful, the version of the node after transition is returned.
+   *
+   * <p>This method can fail and return false for three different reasons:
+   * <ul><li>Unassigned node for this region does not exist</li>
+   * <li>Unassigned node for this region is not in CLOSING state</li>
+   * <li>After verifying CLOSING state, update fails because of wrong version
+   * (someone else already transitioned the node)</li>
+   * </ul>
+   *
+   * <p>Does not set any watches.
+   *
+   * <p>This method should only be used by a RegionServer when initiating a
+   * close of a region after receiving a CLOSE RPC from the Master.
+   *
+   * @param zkw zk reference
+   * @param region region to be transitioned to closed
+   * @param serverName server event originates from
+   * @return version of node after transition, -1 if unsuccessful transition
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  public static int transitionNodeClosed(ZooKeeperWatcher zkw,
+      HRegionInfo region, String serverName, int expectedVersion)
+  throws KeeperException {
+    return transitionNode(zkw, region, serverName,
+        EventType.RS2ZK_REGION_CLOSING,
+        EventType.RS2ZK_REGION_CLOSED, expectedVersion);
+  }
+
+  /**
+   * Transitions an existing unassigned node for the specified region which is
+   * currently in the OFFLINE state to be in the OPENING state.
+   *
+   * <p>Does not transition nodes from other states.  If for some reason the
+   * node could not be transitioned, the method returns -1.  If the transition
+   * is successful, the version of the node written as OPENING is returned.
+   *
+   * <p>This method can fail and return -1 for three different reasons:
+   * <ul><li>Unassigned node for this region does not exist</li>
+   * <li>Unassigned node for this region is not in OFFLINE state</li>
+   * <li>After verifying OFFLINE state, update fails because of wrong version
+   * (someone else already transitioned the node)</li>
+   * </ul>
+   *
+   * <p>Does not set any watches.
+   *
+   * <p>This method should only be used by a RegionServer when initiating an
+   * open of a region after receiving an OPEN RPC from the Master.
+   *
+   * @param zkw zk reference
+   * @param region region to be transitioned to opening
+   * @param serverName server event originates from
+   * @return version of node after transition, -1 if unsuccessful transition
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  public static int transitionNodeOpening(ZooKeeperWatcher zkw,
+      HRegionInfo region, String serverName)
+  throws KeeperException {
+    return transitionNodeOpening(zkw, region, serverName,
+      EventType.M2ZK_REGION_OFFLINE);
+  }
+
+  public static int transitionNodeOpening(ZooKeeperWatcher zkw,
+      HRegionInfo region, String serverName, final EventType beginState)
+  throws KeeperException {
+    return transitionNode(zkw, region, serverName, beginState,
+      EventType.RS2ZK_REGION_OPENING, -1);
+  }
+
+  /**
+   * Retransitions an existing unassigned node for the specified region which is
+   * currently in the OPENING state to be in the OPENING state.
+   *
+   * <p>Does not transition nodes from other states.  If for some reason the
+   * node could not be transitioned, the method returns -1.  If the transition
+   * is successful, the version of the node rewritten as OPENING is returned.
+   *
+   * <p>This method can fail and return -1 for three different reasons:
+   * <ul><li>Unassigned node for this region does not exist</li>
+   * <li>Unassigned node for this region is not in OPENING state</li>
+   * <li>After verifying OPENING state, update fails because of wrong version
+   * (someone else already transitioned the node)</li>
+   * </ul>
+   *
+   * <p>Does not set any watches.
+   *
+   * <p>This method should only be used by a RegionServer when initiating an
+   * open of a region after receiving an OPEN RPC from the Master.
+   *
+   * @param zkw zk reference
+   * @param region region to be transitioned to opening
+   * @param serverName server event originates from
+   * @return version of node after transition, -1 if unsuccessful transition
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  public static int retransitionNodeOpening(ZooKeeperWatcher zkw,
+      HRegionInfo region, String serverName, int expectedVersion)
+  throws KeeperException {
+    return transitionNode(zkw, region, serverName,
+        EventType.RS2ZK_REGION_OPENING,
+        EventType.RS2ZK_REGION_OPENING, expectedVersion);
+  }
+
+  /**
+   * Transitions an existing unassigned node for the specified region which is
+   * currently in the OPENING state to be in the OPENED state.
+   *
+   * <p>Does not transition nodes from other states.  If for some reason the
+   * node could not be transitioned, the method returns -1.  If the transition
+   * is successful, the version of the node after transition is returned.
+   *
+   * <p>This method can fail and return false for three different reasons:
+   * <ul><li>Unassigned node for this region does not exist</li>
+   * <li>Unassigned node for this region is not in OPENING state</li>
+   * <li>After verifying OPENING state, update fails because of wrong version
+   * (this should never actually happen since an RS only does this transition
+   * following a transition to OPENING.  if two RS are conflicting, one would
+   * fail the original transition to OPENING and not this transition)</li>
+   * </ul>
+   *
+   * <p>Does not set any watches.
+   *
+   * <p>This method should only be used by a RegionServer when completing the
+   * open of a region.
+   *
+   * @param zkw zk reference
+   * @param region region to be transitioned to opened
+   * @param serverName server event originates from
+   * @return version of node after transition, -1 if unsuccessful transition
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  public static int transitionNodeOpened(ZooKeeperWatcher zkw,
+      HRegionInfo region, String serverName, int expectedVersion)
+  throws KeeperException {
+    return transitionNode(zkw, region, serverName,
+        EventType.RS2ZK_REGION_OPENING,
+        EventType.RS2ZK_REGION_OPENED, expectedVersion);
+  }
+
+  /**
+   * Private method that actually performs unassigned node transitions.
+   *
+   * <p>Attempts to transition the unassigned node for the specified region
+   * from the expected state to the state in the specified transition data.
+   *
+   * <p>Method first reads existing data and verifies it is in the expected
+   * state.  If the node does not exist or the node is not in the expected
+   * state, the method returns -1.  If the transition is successful, the
+   * version number of the node following the transition is returned.
+   *
+   * <p>If the read state is what is expected, it attempts to write the new
+   * state and data into the node.  When doing this, it includes the expected
+   * version (determined when the existing state was verified) to ensure that
+   * only one transition is successful.  If there is a version mismatch, the
+   * method returns -1.
+   *
+   * <p>If the write is successful, no watch is set and the method returns true.
+   *
+   * @param zkw zk reference
+   * @param region region to be transitioned to opened
+   * @param serverName server event originates from
+   * @param beginState state the node must currently be in to do transition
+   * @param endState state to transition node to if all checks pass
+   * @return version of node after transition, -1 if unsuccessful transition
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  private static int transitionNode(ZooKeeperWatcher zkw, HRegionInfo region,
+      String serverName, EventType beginState, EventType endState,
+      int expectedVersion)
+  throws KeeperException {
+    String encoded = region.getEncodedName();
+    if(zkw.isDebugEnabled()) {
+      zkw.debug("Attempting to transition node " +
+        HRegionInfo.prettyPrint(encoded) +
+        " from " + beginState.toString() + " to " + endState.toString());
+    }
+
+    String node = getNodeName(zkw, encoded);
+
+    // Read existing data of the node
+    Stat stat = new Stat();
+    byte [] existingBytes =
+      ZKUtil.getDataNoWatch(zkw, node, stat);
+    RegionTransitionData existingData =
+      RegionTransitionData.fromBytes(existingBytes);
+
+    // Verify it is the expected version
+    if(expectedVersion != -1 && stat.getVersion() != expectedVersion) {
+      zkw.warn("Attempt to transition the unassigned node for " + encoded +
+          " from " + beginState + " to " + endState + " failed, " +
+          "the node existed but was version " + stat.getVersion() +
+          " not the expected version " + expectedVersion);
+        return -1;
+    }
+
+    // Verify it is in expected state
+    if(!existingData.getEventType().equals(beginState)) {
+      zkw.warn("Attempt to transition the unassigned node for " + encoded +
+        " from " + beginState + " to " + endState + " failed, " +
+        "the node existed but was in the state " + existingData.getEventType());
+      return -1;
+    }
+
+    // Write new data, ensuring data has not changed since we last read it
+    try {
+      RegionTransitionData data = new RegionTransitionData(endState,
+          region.getRegionName(), serverName);
+      if(!ZKUtil.setData(zkw, node, data.getBytes(), stat.getVersion())) {
+        zkw.warn("Attempt to transition the unassigned node for " + encoded +
+        " from " + beginState + " to " + endState + " failed, " +
+        "the node existed and was in the expected state but then when " +
+        "setting data we got a version mismatch");
+        return -1;
+      }
+      if(zkw.isDebugEnabled()) {
+        zkw.debug("Successfully transitioned node " + encoded +
+          " from " + beginState + " to " + endState);
+      }
+      return stat.getVersion() + 1;
+    } catch (KeeperException.NoNodeException nne) {
+      zkw.warn("Attempt to transition the unassigned node for " + encoded +
+        " from " + beginState + " to " + endState + " failed, " +
+        "the node existed and was in the expected state but then when " +
+        "setting data it no longer existed");
+      return -1;
+    }
+  }
+
+  /**
+   * Gets the current data in the unassigned node for the specified region name
+   * or fully-qualified path.
+   *
+   * <p>Returns null if the region does not currently have a node.
+   *
+   * <p>Sets a watch on the node if the node exists.
+   *
+   * @param watcher zk reference
+   * @param pathOrRegionName fully-specified path or region name
+   * @return data for the unassigned node
+   * @throws KeeperException
+   * @throws KeeperException if unexpected zookeeper exception
+   */
+  public static RegionTransitionData getData(ZooKeeperWatcher zkw,
+      String pathOrRegionName)
+  throws KeeperException {
+    String node = pathOrRegionName.startsWith("/") ?
+        pathOrRegionName : getNodeName(zkw, pathOrRegionName);
+    byte [] data = ZKUtil.getDataAndWatch(zkw, node);
+    if(data == null) {
+      return null;
+    }
+    return RegionTransitionData.fromBytes(data);
+  }
+}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKConfig.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,252 @@
+/**
+ * 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.zookeeper;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+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.conf.Configuration;
+import org.apache.hadoop.hbase.HConstants;
+import org.apache.hadoop.util.StringUtils;
+
+/**
+ * Utility methods for reading, parsing, and building zookeeper configuration.
+ */
+public class ZKConfig {
+  private static final Log LOG = LogFactory.getLog(ZKConfig.class);
+
+  private static final String VARIABLE_START = "${";
+  private static final int VARIABLE_START_LENGTH = VARIABLE_START.length();
+  private static final String VARIABLE_END = "}";
+  private static final int VARIABLE_END_LENGTH = VARIABLE_END.length();
+
+  private static final String ZK_CFG_PROPERTY = "hbase.zookeeper.property.";
+  private static final int ZK_CFG_PROPERTY_SIZE = ZK_CFG_PROPERTY.length();
+  private static final String ZK_CLIENT_PORT_KEY = ZK_CFG_PROPERTY
+      + "clientPort";
+
+  /**
+   * Make a Properties object holding ZooKeeper config equivalent to zoo.cfg.
+   * If there is a zoo.cfg in the classpath, simply read it in. Otherwise parse
+   * the corresponding config options from the HBase XML configs and generate
+   * the appropriate ZooKeeper properties.
+   * @param conf Configuration to read from.
+   * @return Properties holding mappings representing ZooKeeper zoo.cfg file.
+   */
+  public static Properties makeZKProps(Configuration conf) {
+    // First check if there is a zoo.cfg in the CLASSPATH. If so, simply read
+    // it and grab its configuration properties.
+    ClassLoader cl = HQuorumPeer.class.getClassLoader();
+    final InputStream inputStream =
+      cl.getResourceAsStream(HConstants.ZOOKEEPER_CONFIG_NAME);
+    if (inputStream != null) {
+      try {
+        return parseZooCfg(conf, inputStream);
+      } catch (IOException e) {
+        LOG.warn("Cannot read " + HConstants.ZOOKEEPER_CONFIG_NAME +
+                 ", loading from XML files", e);
+      }
+    }
+
+    // Otherwise, use the configuration options from HBase's XML files.
+    Properties zkProperties = new Properties();
+
+    // Directly map all of the hbase.zookeeper.property.KEY properties.
+    for (Entry<String, String> entry : conf) {
+      String key = entry.getKey();
+      if (key.startsWith(ZK_CFG_PROPERTY)) {
+        String zkKey = key.substring(ZK_CFG_PROPERTY_SIZE);
+        String value = entry.getValue();
+        // If the value has variables substitutions, need to do a get.
+        if (value.contains(VARIABLE_START)) {
+          value = conf.get(key);
+        }
+        zkProperties.put(zkKey, value);
+      }
+    }
+
+    // If clientPort is not set, assign the default
+    if (zkProperties.getProperty(ZK_CLIENT_PORT_KEY) == null) {
+      zkProperties.put(ZK_CLIENT_PORT_KEY,
+                       HConstants.DEFAULT_ZOOKEPER_CLIENT_PORT);
+    }
+
+    // Create the server.X properties.
+    int peerPort = conf.getInt("hbase.zookeeper.peerport", 2888);
+    int leaderPort = conf.getInt("hbase.zookeeper.leaderport", 3888);
+
+    final String[] serverHosts = conf.getStrings(HConstants.ZOOKEEPER_QUORUM,
+                                                 "localhost");
+    for (int i = 0; i < serverHosts.length; ++i) {
+      String serverHost = serverHosts[i];
+      String address = serverHost + ":" + peerPort + ":" + leaderPort;
+      String key = "server." + i;
+      zkProperties.put(key, address);
+    }
+
+    return zkProperties;
+  }
+
+  /**
+   * Parse ZooKeeper's zoo.cfg, injecting HBase Configuration variables in.
+   * This method is used for testing so we can pass our own InputStream.
+   * @param conf HBaseConfiguration to use for injecting variables.
+   * @param inputStream InputStream to read from.
+   * @return Properties parsed from config stream with variables substituted.
+   * @throws IOException if anything goes wrong parsing config
+   */
+  public static Properties parseZooCfg(Configuration conf,
+      InputStream inputStream) throws IOException {
+    Properties properties = new Properties();
+    try {
+      properties.load(inputStream);
+    } catch (IOException e) {
+      final String msg = "fail to read properties from "
+        + HConstants.ZOOKEEPER_CONFIG_NAME;
+      LOG.fatal(msg);
+      throw new IOException(msg, e);
+    }
+    for (Entry<Object, Object> entry : properties.entrySet()) {
+      String value = entry.getValue().toString().trim();
+      String key = entry.getKey().toString().trim();
+      StringBuilder newValue = new StringBuilder();
+      int varStart = value.indexOf(VARIABLE_START);
+      int varEnd = 0;
+      while (varStart != -1) {
+        varEnd = value.indexOf(VARIABLE_END, varStart);
+        if (varEnd == -1) {
+          String msg = "variable at " + varStart + " has no end marker";
+          LOG.fatal(msg);
+          throw new IOException(msg);
+        }
+        String variable = value.substring(varStart + VARIABLE_START_LENGTH, varEnd);
+
+        String substituteValue = System.getProperty(variable);
+        if (substituteValue == null) {
+          substituteValue = conf.get(variable);
+        }
+        if (substituteValue == null) {
+          String msg = "variable " + variable + " not set in system property "
+                     + "or hbase configs";
+          LOG.fatal(msg);
+          throw new IOException(msg);
+        }
+
+        newValue.append(substituteValue);
+
+        varEnd += VARIABLE_END_LENGTH;
+        varStart = value.indexOf(VARIABLE_START, varEnd);
+      }
+      // Special case for 'hbase.cluster.distributed' property being 'true'
+      if (key.startsWith("server.")) {
+        if (conf.get(HConstants.CLUSTER_DISTRIBUTED).equals(HConstants.CLUSTER_IS_DISTRIBUTED)
+            && value.startsWith("localhost")) {
+          String msg = "The server in zoo.cfg cannot be set to localhost " +
+              "in a fully-distributed setup because it won't be reachable. " +
+              "See \"Getting Started\" for more information.";
+          LOG.fatal(msg);
+          throw new IOException(msg);
+        }
+      }
+      newValue.append(value.substring(varEnd));
+      properties.setProperty(key, newValue.toString());
+    }
+    return properties;
+  }
+
+  /**
+   * Return the ZK Quorum servers string given zk properties returned by
+   * makeZKProps
+   * @param properties
+   * @return
+   */
+  public static String getZKQuorumServersString(Properties properties) {
+    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.
+    boolean anyValid = false;
+    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);
+        try {
+          //noinspection ResultOfMethodCallIgnored
+          InetAddress.getByName(host);
+          anyValid = true;
+        } catch (UnknownHostException e) {
+          LOG.warn(StringUtils.stringifyException(e));
+        }
+      }
+    }
+
+    if (!anyValid) {
+      LOG.error("no valid quorum servers found in " + HConstants.ZOOKEEPER_CONFIG_NAME);
+      return null;
+    }
+
+    if (clientPort == null) {
+      LOG.error("no clientPort found in " + HConstants.ZOOKEEPER_CONFIG_NAME);
+      return null;
+    }
+
+    if (servers.isEmpty()) {
+      LOG.fatal("No server.X lines found in conf/zoo.cfg. HBase must have a " +
+                "ZooKeeper cluster configured for its operation.");
+      return null;
+    }
+
+    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);
+    }
+
+    return hostPortBuilder.toString();
+  }
+
+  /**
+   * Return the ZK Quorum servers string given the specified configuration.
+   * @param properties
+   * @return
+   */
+  public static String getZKQuorumServersString(Configuration conf) {
+    return getZKQuorumServersString(makeZKProps(conf));
+  }
+}
\ No newline at end of file

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKServerTool.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKServerTool.java?rev=991397&r1=991396&r2=991397&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKServerTool.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKServerTool.java Tue Aug 31 23:51:44 2010
@@ -20,15 +20,14 @@
 
 package org.apache.hadoop.hbase.zookeeper;
 
+import java.util.Properties;
+import java.util.Map.Entry;
+
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.HBaseConfiguration;
-import org.apache.hadoop.hbase.HConstants;
-
-import java.util.Map.Entry;
-import java.util.Properties;
 
 /**
- * Tool for reading ZooKeeper servers from HBase XML configuation and producing
+ * Tool for reading ZooKeeper servers from HBase XML configuration and producing
  * a line-by-line list for use by bash scripts.
  */
 public class ZKServerTool {
@@ -41,7 +40,7 @@ public class ZKServerTool {
     // Note that we do not simply grab the property
     // HConstants.ZOOKEEPER_QUORUM from the HBaseConfiguration because the
     // user may be using a zoo.cfg file.
-    Properties zkProps = HQuorumPeer.makeZKProps(conf);
+    Properties zkProps = ZKConfig.makeZKProps(conf);
     for (Entry<Object, Object> entry : zkProps.entrySet()) {
       String key = entry.getKey().toString().trim();
       String value = entry.getValue().toString().trim();

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKTableDisable.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKTableDisable.java?rev=991397&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKTableDisable.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/zookeeper/ZKTableDisable.java Tue Aug 31 23:51:44 2010
@@ -0,0 +1,70 @@
+/**
+ * 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.zookeeper;
+
+import java.util.List;
+
+import org.apache.zookeeper.KeeperException;
+
+/**
+ * Helper class for table disable tracking in zookeeper.
+ * <p>
+ * The node /disabled will contain a child node for every table which should be
+ * disabled, for example, /disabled/table.
+ */
+public class ZKTableDisable {
+
+  /**
+   * Sets the specified table as disabled in zookeeper.  Fails silently if the
+   * table is already disabled in zookeeper.  Sets no watches.
+   * @param zkw
+   * @param tableName
+   * @throws KeeperException unexpected zookeeper exception
+   */
+  public static void disableTable(ZooKeeperWatcher zkw, String tableName)
+  throws KeeperException {
+    ZKUtil.createAndFailSilent(zkw, ZKUtil.joinZNode(zkw.tableZNode,
+        tableName));
+  }
+
+  /**
+   * Unsets the specified table as disabled in zookeeper.  Fails silently if the
+   * table is not currently disabled in zookeeper.  Sets no watches.
+   * @param zkw
+   * @param tableName
+   * @throws KeeperException unexpected zookeeper exception
+   */
+  public static void undisableTable(ZooKeeperWatcher zkw, String tableName)
+  throws KeeperException {
+    ZKUtil.deleteNodeFailSilent(zkw, ZKUtil.joinZNode(zkw.tableZNode,
+        tableName));
+  }
+
+  /**
+   * Gets a list of all the tables set as disabled in zookeeper.
+   * @param zkw
+   * @return list of disabled tables, empty list if none
+   * @throws KeeperException
+   */
+  public static List<String> getDisabledTables(ZooKeeperWatcher zkw)
+  throws KeeperException {
+    return ZKUtil.listChildrenNoWatch(zkw, zkw.tableZNode);
+  }
+}
\ No newline at end of file



Mime
View raw message