001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import java.io.File;
021import java.io.IOException;
022
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.fs.Path;
025import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
026import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
027import org.apache.yetus.audience.InterfaceAudience;
028
029/**
030 * Helpers for testing HBase that do not depend on specific server/etc. things. The main difference
031 * from {@link HBaseCommonTestingUtility} is that we can start a zookeeper cluster.
032 */
033@InterfaceAudience.Public
034public class HBaseZKTestingUtility extends HBaseCommonTestingUtility {
035
036  private MiniZooKeeperCluster zkCluster;
037
038  /**
039   * Set if we were passed a zkCluster. If so, we won't shutdown zk as part of general shutdown.
040   */
041  private boolean passedZkCluster;
042
043  protected ZKWatcher zooKeeperWatcher;
044
045  /** Directory (a subdirectory of dataTestDir) used by the dfs cluster if any */
046  protected File clusterTestDir;
047
048  public HBaseZKTestingUtility() {
049    this(HBaseConfiguration.create());
050  }
051
052  public HBaseZKTestingUtility(Configuration conf) {
053    super(conf);
054  }
055
056  /**
057   * @return Where the cluster will write data on the local subsystem. Creates it if it does not
058   *         exist already. A subdir of {@link #getBaseTestDir()}
059   * @see #getTestFileSystem()
060   */
061  Path getClusterTestDir() {
062    if (clusterTestDir == null) {
063      setupClusterTestDir();
064    }
065    return new Path(clusterTestDir.getAbsolutePath());
066  }
067
068  /**
069   * Creates a directory for the cluster, under the test data
070   */
071  protected void setupClusterTestDir() {
072    if (clusterTestDir != null) {
073      return;
074    }
075
076    // Using randomUUID ensures that multiple clusters can be launched by
077    // a same test, if it stops & starts them
078    Path testDir = getDataTestDir("cluster_" + getRandomUUID().toString());
079    clusterTestDir = new File(testDir.toString()).getAbsoluteFile();
080    // Have it cleaned up on exit
081    boolean b = deleteOnExit();
082    if (b) {
083      clusterTestDir.deleteOnExit();
084    }
085    LOG.info("Created new mini-cluster data directory: " + clusterTestDir + ", deleteOnExit=" + b);
086  }
087
088  /**
089   * Call this if you only want a zk cluster.
090   * @see #shutdownMiniZKCluster()
091   * @return zk cluster started.
092   */
093  public MiniZooKeeperCluster startMiniZKCluster() throws Exception {
094    return startMiniZKCluster(1);
095  }
096
097  /**
098   * Call this if you only want a zk cluster.
099   * @see #shutdownMiniZKCluster()
100   * @return zk cluster started.
101   */
102  public MiniZooKeeperCluster startMiniZKCluster(int zooKeeperServerNum, int... clientPortList)
103      throws Exception {
104    setupClusterTestDir();
105    return startMiniZKCluster(clusterTestDir, zooKeeperServerNum, clientPortList);
106  }
107
108  /**
109   * Start a mini ZK cluster. If the property "test.hbase.zookeeper.property.clientPort" is set the
110   * port mentioned is used as the default port for ZooKeeper.
111   */
112  private MiniZooKeeperCluster startMiniZKCluster(File dir, int zooKeeperServerNum,
113      int[] clientPortList) throws Exception {
114    if (this.zkCluster != null) {
115      throw new IOException("Cluster already running at " + dir);
116    }
117    this.passedZkCluster = false;
118    this.zkCluster = new MiniZooKeeperCluster(this.getConfiguration());
119    int defPort = this.conf.getInt("test.hbase.zookeeper.property.clientPort", 0);
120    if (defPort > 0) {
121      // If there is a port in the config file, we use it.
122      this.zkCluster.setDefaultClientPort(defPort);
123    }
124
125    if (clientPortList != null) {
126      // Ignore extra client ports
127      int clientPortListSize = (clientPortList.length <= zooKeeperServerNum) ? clientPortList.length
128          : zooKeeperServerNum;
129      for (int i = 0; i < clientPortListSize; i++) {
130        this.zkCluster.addClientPort(clientPortList[i]);
131      }
132    }
133    int clientPort = this.zkCluster.startup(dir, zooKeeperServerNum);
134    this.conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, Integer.toString(clientPort));
135    return this.zkCluster;
136  }
137
138  public MiniZooKeeperCluster getZkCluster() {
139    return zkCluster;
140  }
141
142  public void setZkCluster(MiniZooKeeperCluster zkCluster) {
143    this.passedZkCluster = true;
144    this.zkCluster = zkCluster;
145    conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, zkCluster.getClientPort());
146  }
147
148  /**
149   * Shuts down zk cluster created by call to {@link #startMiniZKCluster(File)} or does nothing.
150   * @see #startMiniZKCluster()
151   */
152  public void shutdownMiniZKCluster() throws IOException {
153    if (!passedZkCluster && this.zkCluster != null) {
154      this.zkCluster.shutdown();
155      this.zkCluster = null;
156    }
157  }
158
159  /**
160   * Returns a ZKWatcher instance. This instance is shared between HBaseTestingUtility instance
161   * users. Don't close it, it will be closed automatically when the cluster shutdowns
162   * @return The ZKWatcher instance.
163   */
164  public synchronized ZKWatcher getZooKeeperWatcher() throws IOException {
165    if (zooKeeperWatcher == null) {
166      zooKeeperWatcher = new ZKWatcher(conf, "testing utility", new Abortable() {
167        @Override
168        public void abort(String why, Throwable e) {
169          throw new RuntimeException("Unexpected abort in HBaseZKTestingUtility:" + why, e);
170        }
171
172        @Override
173        public boolean isAborted() {
174          return false;
175        }
176      });
177    }
178    return zooKeeperWatcher;
179  }
180
181  /**
182   * Gets a ZKWatcher.
183   */
184  public static ZKWatcher getZooKeeperWatcher(HBaseZKTestingUtility testUtil) throws IOException {
185    ZKWatcher zkw = new ZKWatcher(testUtil.getConfiguration(), "unittest", new Abortable() {
186      boolean aborted = false;
187
188      @Override
189      public void abort(String why, Throwable e) {
190        aborted = true;
191        throw new RuntimeException("Fatal ZK error, why=" + why, e);
192      }
193
194      @Override
195      public boolean isAborted() {
196        return aborted;
197      }
198    });
199    return zkw;
200  }
201
202  /**
203   * @return True if we removed the test dirs
204   */
205  @Override
206  public boolean cleanupTestDir() throws IOException {
207    boolean ret = super.cleanupTestDir();
208    if (deleteDir(this.clusterTestDir)) {
209      this.clusterTestDir = null;
210      return ret & true;
211    }
212    return false;
213  }
214}