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