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 org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.fs.Path;
024import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
025import org.apache.hadoop.hbase.zookeeper.ZKWatcher;
026import org.apache.yetus.audience.InterfaceAudience;
027
028/**
029 * Helpers for testing HBase that do not depend on specific server/etc. things. The main difference
030 * from {@link HBaseCommonTestingUtility} is that we can start a zookeeper cluster.
031 * @deprecated since 3.0.0, will be removed in 4.0.0. Use
032 *             {@link org.apache.hadoop.hbase.testing.TestingHBaseCluster} instead.
033 */
034@InterfaceAudience.Public
035@Deprecated
036public class HBaseZKTestingUtility extends HBaseCommonTestingUtility {
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 {@code HBaseCommonTestingUtility#getBaseTestDir()}
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 = Math.min(clientPortList.length, zooKeeperServerNum);
128      for (int i = 0; i < clientPortListSize; i++) {
129        this.zkCluster.addClientPort(clientPortList[i]);
130      }
131    }
132    int clientPort = this.zkCluster.startup(dir, zooKeeperServerNum);
133    this.conf.set(HConstants.ZOOKEEPER_CLIENT_PORT, Integer.toString(clientPort));
134    return this.zkCluster;
135  }
136
137  public MiniZooKeeperCluster getZkCluster() {
138    return zkCluster;
139  }
140
141  public void setZkCluster(MiniZooKeeperCluster zkCluster) {
142    this.passedZkCluster = true;
143    this.zkCluster = zkCluster;
144    conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, zkCluster.getClientPort());
145  }
146
147  /**
148   * Shuts down zk cluster created by call to {@link #startMiniZKCluster()} or does nothing.
149   * @see #startMiniZKCluster()
150   */
151  public void shutdownMiniZKCluster() throws IOException {
152    if (!passedZkCluster && this.zkCluster != null) {
153      this.zkCluster.shutdown();
154      this.zkCluster = null;
155    }
156  }
157
158  /**
159   * Returns a ZKWatcher instance. This instance is shared between HBaseTestingUtility instance
160   * users. Don't close it, it will be closed automatically when the cluster shutdowns
161   * @return The ZKWatcher instance.
162   */
163  public synchronized ZKWatcher getZooKeeperWatcher() throws IOException {
164    if (zooKeeperWatcher == null) {
165      zooKeeperWatcher = new ZKWatcher(conf, "testing utility", new Abortable() {
166        @Override
167        public void abort(String why, Throwable e) {
168          throw new RuntimeException("Unexpected abort in HBaseZKTestingUtility:" + why, e);
169        }
170
171        @Override
172        public boolean isAborted() {
173          return false;
174        }
175      });
176    }
177    return zooKeeperWatcher;
178  }
179
180  /**
181   * Gets a ZKWatcher.
182   */
183  public static ZKWatcher getZooKeeperWatcher(HBaseZKTestingUtility testUtil) throws IOException {
184    return new ZKWatcher(testUtil.getConfiguration(), "unittest", new Abortable() {
185      boolean aborted = false;
186
187      @Override
188      public void abort(String why, Throwable e) {
189        aborted = true;
190        throw new RuntimeException("Fatal ZK error, why=" + why, e);
191      }
192
193      @Override
194      public boolean isAborted() {
195        return aborted;
196      }
197    });
198  }
199
200  /** Returns True if we removed the test dirs */
201  @Override
202  public boolean cleanupTestDir() {
203    boolean ret = super.cleanupTestDir();
204    if (deleteDir(this.clusterTestDir)) {
205      this.clusterTestDir = null;
206      return ret;
207    }
208    return false;
209  }
210}