View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase;
19  
20  import java.io.File;
21  import java.io.IOException;
22  import java.io.OutputStream;
23  import java.lang.reflect.Field;
24  import java.lang.reflect.Modifier;
25  import java.net.BindException;
26  import java.net.DatagramSocket;
27  import java.net.InetAddress;
28  import java.net.ServerSocket;
29  import java.net.Socket;
30  import java.net.UnknownHostException;
31  import java.security.MessageDigest;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Collection;
35  import java.util.Collections;
36  import java.util.HashSet;
37  import java.util.List;
38  import java.util.Map;
39  import java.util.NavigableSet;
40  import java.util.Properties;
41  import java.util.Random;
42  import java.util.Set;
43  import java.util.TreeSet;
44  import java.util.UUID;
45  import java.util.concurrent.TimeUnit;
46  
47  import org.apache.commons.io.FileUtils;
48  import org.apache.commons.lang.RandomStringUtils;
49  import org.apache.commons.logging.Log;
50  import org.apache.commons.logging.LogFactory;
51  import org.apache.commons.logging.impl.Jdk14Logger;
52  import org.apache.commons.logging.impl.Log4JLogger;
53  import org.apache.hadoop.conf.Configuration;
54  import org.apache.hadoop.fs.FileSystem;
55  import org.apache.hadoop.fs.Path;
56  import org.apache.hadoop.hbase.Waiter.ExplainingPredicate;
57  import org.apache.hadoop.hbase.Waiter.Predicate;
58  import org.apache.hadoop.hbase.classification.InterfaceAudience;
59  import org.apache.hadoop.hbase.classification.InterfaceStability;
60  import org.apache.hadoop.hbase.client.Admin;
61  import org.apache.hadoop.hbase.client.Connection;
62  import org.apache.hadoop.hbase.client.ConnectionFactory;
63  import org.apache.hadoop.hbase.client.Consistency;
64  import org.apache.hadoop.hbase.client.Delete;
65  import org.apache.hadoop.hbase.client.Durability;
66  import org.apache.hadoop.hbase.client.Get;
67  import org.apache.hadoop.hbase.client.HBaseAdmin;
68  import org.apache.hadoop.hbase.client.HConnection;
69  import org.apache.hadoop.hbase.client.HTable;
70  import org.apache.hadoop.hbase.client.Put;
71  import org.apache.hadoop.hbase.client.RegionLocator;
72  import org.apache.hadoop.hbase.client.Result;
73  import org.apache.hadoop.hbase.client.ResultScanner;
74  import org.apache.hadoop.hbase.client.Scan;
75  import org.apache.hadoop.hbase.client.Table;
76  import org.apache.hadoop.hbase.fs.HFileSystem;
77  import org.apache.hadoop.hbase.io.compress.Compression;
78  import org.apache.hadoop.hbase.io.compress.Compression.Algorithm;
79  import org.apache.hadoop.hbase.io.encoding.DataBlockEncoding;
80  import org.apache.hadoop.hbase.io.hfile.ChecksumUtil;
81  import org.apache.hadoop.hbase.io.hfile.HFile;
82  import org.apache.hadoop.hbase.ipc.RpcServerInterface;
83  import org.apache.hadoop.hbase.ipc.ServerNotRunningYetException;
84  import org.apache.hadoop.hbase.mapreduce.MapreduceTestingShim;
85  import org.apache.hadoop.hbase.master.HMaster;
86  import org.apache.hadoop.hbase.master.AssignmentManager;
87  import org.apache.hadoop.hbase.master.RegionStates;
88  import org.apache.hadoop.hbase.master.ServerManager;
89  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
90  import org.apache.hadoop.hbase.regionserver.BloomType;
91  import org.apache.hadoop.hbase.regionserver.HRegion;
92  import org.apache.hadoop.hbase.regionserver.HRegionServer;
93  import org.apache.hadoop.hbase.regionserver.HStore;
94  import org.apache.hadoop.hbase.regionserver.InternalScanner;
95  import org.apache.hadoop.hbase.regionserver.Region;
96  import org.apache.hadoop.hbase.regionserver.RegionServerServices;
97  import org.apache.hadoop.hbase.regionserver.RegionServerStoppedException;
98  import org.apache.hadoop.hbase.regionserver.wal.MetricsWAL;
99  import org.apache.hadoop.hbase.regionserver.wal.WALActionsListener;
100 import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
101 import org.apache.hadoop.hbase.security.User;
102 import org.apache.hadoop.hbase.security.visibility.VisibilityLabelsCache;
103 import org.apache.hadoop.hbase.tool.Canary;
104 import org.apache.hadoop.hbase.util.Bytes;
105 import org.apache.hadoop.hbase.util.FSTableDescriptors;
106 import org.apache.hadoop.hbase.util.FSUtils;
107 import org.apache.hadoop.hbase.util.JVMClusterUtil;
108 import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread;
109 import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
110 import org.apache.hadoop.hbase.util.Pair;
111 import org.apache.hadoop.hbase.util.RegionSplitter;
112 import org.apache.hadoop.hbase.util.RetryCounter;
113 import org.apache.hadoop.hbase.util.Threads;
114 import org.apache.hadoop.hbase.wal.WAL;
115 import org.apache.hadoop.hbase.wal.WALFactory;
116 import org.apache.hadoop.hbase.zookeeper.EmptyWatcher;
117 import org.apache.hadoop.hbase.zookeeper.MiniZooKeeperCluster;
118 import org.apache.hadoop.hbase.zookeeper.ZKAssign;
119 import org.apache.hadoop.hbase.zookeeper.ZKConfig;
120 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
121 import org.apache.hadoop.hdfs.DFSClient;
122 import org.apache.hadoop.hdfs.DistributedFileSystem;
123 import org.apache.hadoop.hdfs.MiniDFSCluster;
124 import org.apache.hadoop.hdfs.server.namenode.EditLogFileOutputStream;
125 import org.apache.hadoop.mapred.JobConf;
126 import org.apache.hadoop.mapred.MiniMRCluster;
127 import org.apache.hadoop.mapred.TaskLog;
128 import org.apache.hadoop.minikdc.MiniKdc;
129 import org.apache.zookeeper.KeeperException;
130 import org.apache.zookeeper.KeeperException.NodeExistsException;
131 import org.apache.zookeeper.WatchedEvent;
132 import org.apache.zookeeper.ZooKeeper;
133 import org.apache.zookeeper.ZooKeeper.States;
134 
135 import static org.junit.Assert.assertEquals;
136 import static org.junit.Assert.assertTrue;
137 import static org.junit.Assert.fail;
138 
139 /**
140  * Facility for testing HBase. Replacement for
141  * old HBaseTestCase and HBaseClusterTestCase functionality.
142  * Create an instance and keep it around testing HBase.  This class is
143  * meant to be your one-stop shop for anything you might need testing.  Manages
144  * one cluster at a time only. Managed cluster can be an in-process
145  * {@link MiniHBaseCluster}, or a deployed cluster of type {@link HBaseCluster}.
146  * Not all methods work with the real cluster.
147  * Depends on log4j being on classpath and
148  * hbase-site.xml for logging and test-run configuration.  It does not set
149  * logging levels nor make changes to configuration parameters.
150  * <p>To preserve test data directories, pass the system property "hbase.testing.preserve.testdir"
151  * setting it to true.
152  */
153 @InterfaceAudience.Public
154 @InterfaceStability.Evolving
155 @SuppressWarnings("deprecation")
156 public class HBaseTestingUtility extends HBaseCommonTestingUtility {
157    private MiniZooKeeperCluster zkCluster = null;
158 
159   public static final String REGIONS_PER_SERVER_KEY = "hbase.test.regions-per-server";
160   /**
161    * The default number of regions per regionserver when creating a pre-split
162    * table.
163    */
164   public static final int DEFAULT_REGIONS_PER_SERVER = 3;
165 
166 
167   public static final String PRESPLIT_TEST_TABLE_KEY = "hbase.test.pre-split-table";
168   public static final boolean PRESPLIT_TEST_TABLE = true;
169 
170   public static final String USE_LOCAL_FILESYSTEM = "hbase.test.local.fileSystem";
171   /**
172    * Set if we were passed a zkCluster.  If so, we won't shutdown zk as
173    * part of general shutdown.
174    */
175   private boolean passedZkCluster = false;
176   private MiniDFSCluster dfsCluster = null;
177 
178   private volatile HBaseCluster hbaseCluster = null;
179   private MiniMRCluster mrCluster = null;
180 
181   /** If there is a mini cluster running for this testing utility instance. */
182   private volatile boolean miniClusterRunning;
183 
184   private String hadoopLogDir;
185 
186   /** Directory (a subdirectory of dataTestDir) used by the dfs cluster if any */
187   private File clusterTestDir = null;
188 
189   /** Directory on test filesystem where we put the data for this instance of
190     * HBaseTestingUtility*/
191   private Path dataTestDirOnTestFS = null;
192 
193   /**
194    * Shared cluster connection.
195    */
196   private volatile Connection connection;
197 
198   /**
199    * System property key to get test directory value.
200    * Name is as it is because mini dfs has hard-codings to put test data here.
201    * It should NOT be used directly in HBase, as it's a property used in
202    *  mini dfs.
203    *  @deprecated can be used only with mini dfs
204    */
205   @Deprecated
206   private static final String TEST_DIRECTORY_KEY = "test.build.data";
207 
208   /** Filesystem URI used for map-reduce mini-cluster setup */
209   private static String FS_URI;
210 
211   /** A set of ports that have been claimed using {@link #randomFreePort()}. */
212   private static final Set<Integer> takenRandomPorts = new HashSet<Integer>();
213 
214   /** Compression algorithms to use in parameterized JUnit 4 tests */
215   public static final List<Object[]> COMPRESSION_ALGORITHMS_PARAMETERIZED =
216     Arrays.asList(new Object[][] {
217       { Compression.Algorithm.NONE },
218       { Compression.Algorithm.GZ }
219     });
220 
221   /** This is for unit tests parameterized with a two booleans. */
222   public static final List<Object[]> BOOLEAN_PARAMETERIZED =
223       Arrays.asList(new Object[][] {
224           { new Boolean(false) },
225           { new Boolean(true) }
226       });
227 
228   /** This is for unit tests parameterized with a single boolean. */
229   public static final List<Object[]> MEMSTORETS_TAGS_PARAMETRIZED = memStoreTSAndTagsCombination()  ;
230   /** Compression algorithms to use in testing */
231   public static final Compression.Algorithm[] COMPRESSION_ALGORITHMS ={
232       Compression.Algorithm.NONE, Compression.Algorithm.GZ
233     };
234 
235   /**
236    * Checks to see if a specific port is available.
237    *
238    * @param port the port number to check for availability
239    * @return <tt>true</tt> if the port is available, or <tt>false</tt> if not
240    */
241   public static boolean available(int port) {
242     ServerSocket ss = null;
243     DatagramSocket ds = null;
244     try {
245       ss = new ServerSocket(port);
246       ss.setReuseAddress(true);
247       ds = new DatagramSocket(port);
248       ds.setReuseAddress(true);
249       return true;
250     } catch (IOException e) {
251       // Do nothing
252     } finally {
253       if (ds != null) {
254         ds.close();
255       }
256 
257       if (ss != null) {
258         try {
259           ss.close();
260         } catch (IOException e) {
261           /* should not be thrown */
262         }
263       }
264     }
265 
266     return false;
267   }
268 
269   /**
270    * Create all combinations of Bloom filters and compression algorithms for
271    * testing.
272    */
273   private static List<Object[]> bloomAndCompressionCombinations() {
274     List<Object[]> configurations = new ArrayList<Object[]>();
275     for (Compression.Algorithm comprAlgo :
276          HBaseTestingUtility.COMPRESSION_ALGORITHMS) {
277       for (BloomType bloomType : BloomType.values()) {
278         configurations.add(new Object[] { comprAlgo, bloomType });
279       }
280     }
281     return Collections.unmodifiableList(configurations);
282   }
283 
284   /**
285    * Create combination of memstoreTS and tags
286    */
287   private static List<Object[]> memStoreTSAndTagsCombination() {
288     List<Object[]> configurations = new ArrayList<Object[]>();
289     configurations.add(new Object[] { false, false });
290     configurations.add(new Object[] { false, true });
291     configurations.add(new Object[] { true, false });
292     configurations.add(new Object[] { true, true });
293     return Collections.unmodifiableList(configurations);
294   }
295 
296   public static final Collection<Object[]> BLOOM_AND_COMPRESSION_COMBINATIONS =
297       bloomAndCompressionCombinations();
298 
299   public HBaseTestingUtility() {
300     this(HBaseConfiguration.create());
301   }
302 
303   public HBaseTestingUtility(Configuration conf) {
304     super(conf);
305 
306     // a hbase checksum verification failure will cause unit tests to fail
307     ChecksumUtil.generateExceptionForChecksumFailureForTest(true);
308   }
309 
310   /**
311    * Create an HBaseTestingUtility where all tmp files are written to the local test data dir.
312    * It is needed to properly base FSUtil.getRootDirs so that they drop temp files in the proper
313    * test dir.  Use this when you aren't using an Mini HDFS cluster.
314    * @return HBaseTestingUtility that use local fs for temp files.
315    */
316   public static HBaseTestingUtility createLocalHTU() {
317     Configuration c = HBaseConfiguration.create();
318     return createLocalHTU(c);
319   }
320 
321   /**
322    * Create an HBaseTestingUtility where all tmp files are written to the local test data dir.
323    * It is needed to properly base FSUtil.getRootDirs so that they drop temp files in the proper
324    * test dir.  Use this when you aren't using an Mini HDFS cluster.
325    * @param c Configuration (will be modified)
326    * @return HBaseTestingUtility that use local fs for temp files.
327    */
328   public static HBaseTestingUtility createLocalHTU(Configuration c) {
329     HBaseTestingUtility htu = new HBaseTestingUtility(c);
330     String dataTestDir = htu.getDataTestDir().toString();
331     htu.getConfiguration().set(HConstants.HBASE_DIR, dataTestDir);
332     LOG.debug("Setting " + HConstants.HBASE_DIR + " to " + dataTestDir);
333     return htu;
334   }
335 
336  /**
337   * Close the Region {@code r}. For use in tests.
338   */
339  public static void closeRegion(final Region r) throws IOException {
340    if (r != null) {
341      ((HRegion)r).close();
342    }
343  }
344 
345   /**
346    * Returns this classes's instance of {@link Configuration}.  Be careful how
347    * you use the returned Configuration since {@link HConnection} instances
348    * can be shared.  The Map of HConnections is keyed by the Configuration.  If
349    * say, a Connection was being used against a cluster that had been shutdown,
350    * see {@link #shutdownMiniCluster()}, then the Connection will no longer
351    * be wholesome.  Rather than use the return direct, its usually best to
352    * make a copy and use that.  Do
353    * <code>Configuration c = new Configuration(INSTANCE.getConfiguration());</code>
354    * @return Instance of Configuration.
355    */
356   @Override
357   public Configuration getConfiguration() {
358     return super.getConfiguration();
359   }
360 
361   public void setHBaseCluster(HBaseCluster hbaseCluster) {
362     this.hbaseCluster = hbaseCluster;
363   }
364 
365   /**
366    * Home our data in a dir under {@link #DEFAULT_BASE_TEST_DIRECTORY}.
367    * Give it a random name so can have many concurrent tests running if
368    * we need to.  It needs to amend the {@link #TEST_DIRECTORY_KEY}
369    * System property, as it's what minidfscluster bases
370    * it data dir on.  Moding a System property is not the way to do concurrent
371    * instances -- another instance could grab the temporary
372    * value unintentionally -- but not anything can do about it at moment;
373    * single instance only is how the minidfscluster works.
374    *
375    * We also create the underlying directory for
376    *  hadoop.log.dir, mapreduce.cluster.local.dir and hadoop.tmp.dir, and set the values
377    *  in the conf, and as a system property for hadoop.tmp.dir
378    *
379    * @return The calculated data test build directory, if newly-created.
380    */
381   @Override
382   protected Path setupDataTestDir() {
383     Path testPath = super.setupDataTestDir();
384     if (null == testPath) {
385       return null;
386     }
387 
388     createSubDirAndSystemProperty(
389       "hadoop.log.dir",
390       testPath, "hadoop-log-dir");
391 
392     // This is defaulted in core-default.xml to /tmp/hadoop-${user.name}, but
393     //  we want our own value to ensure uniqueness on the same machine
394     createSubDirAndSystemProperty(
395       "hadoop.tmp.dir",
396       testPath, "hadoop-tmp-dir");
397 
398     // Read and modified in org.apache.hadoop.mapred.MiniMRCluster
399     createSubDir(
400       "mapreduce.cluster.local.dir",
401       testPath, "mapred-local-dir");
402 
403     return testPath;
404   }
405 
406   public void setJobWithoutMRCluster() throws IOException {
407     conf.set("hbase.fs.tmp.dir", getDataTestDirOnTestFS("hbase-staging").toString());
408     conf.setBoolean(HBaseTestingUtility.USE_LOCAL_FILESYSTEM, true);
409   }
410 
411   private void createSubDirAndSystemProperty(
412     String propertyName, Path parent, String subDirName){
413 
414     String sysValue = System.getProperty(propertyName);
415 
416     if (sysValue != null) {
417       // There is already a value set. So we do nothing but hope
418       //  that there will be no conflicts
419       LOG.info("System.getProperty(\""+propertyName+"\") already set to: "+
420         sysValue + " so I do NOT create it in " + parent);
421       String confValue = conf.get(propertyName);
422       if (confValue != null && !confValue.endsWith(sysValue)){
423        LOG.warn(
424          propertyName + " property value differs in configuration and system: "+
425          "Configuration="+confValue+" while System="+sysValue+
426          " Erasing configuration value by system value."
427        );
428       }
429       conf.set(propertyName, sysValue);
430     } else {
431       // Ok, it's not set, so we create it as a subdirectory
432       createSubDir(propertyName, parent, subDirName);
433       System.setProperty(propertyName, conf.get(propertyName));
434     }
435   }
436 
437   /**
438    * @return Where to write test data on the test filesystem; Returns working directory
439    * for the test filesystem by default
440    * @see #setupDataTestDirOnTestFS()
441    * @see #getTestFileSystem()
442    */
443   private Path getBaseTestDirOnTestFS() throws IOException {
444     FileSystem fs = getTestFileSystem();
445     return new Path(fs.getWorkingDirectory(), "test-data");
446   }
447 
448   /**
449    * @return META table descriptor
450    */
451   public HTableDescriptor getMetaTableDescriptor() {
452     try {
453       return new FSTableDescriptors(conf).get(TableName.META_TABLE_NAME);
454     } catch (IOException e) {
455       throw new RuntimeException("Unable to create META table descriptor", e);
456     }
457   }
458 
459   /**
460    * @return Where the DFS cluster will write data on the local subsystem.
461    * Creates it if it does not exist already.  A subdir of {@link #getBaseTestDir()}
462    * @see #getTestFileSystem()
463    */
464   Path getClusterTestDir() {
465     if (clusterTestDir == null){
466       setupClusterTestDir();
467     }
468     return new Path(clusterTestDir.getAbsolutePath());
469   }
470 
471   /**
472    * Creates a directory for the DFS cluster, under the test data
473    */
474   private void setupClusterTestDir() {
475     if (clusterTestDir != null) {
476       return;
477     }
478 
479     // Using randomUUID ensures that multiple clusters can be launched by
480     //  a same test, if it stops & starts them
481     Path testDir = getDataTestDir("dfscluster_" + UUID.randomUUID().toString());
482     clusterTestDir = new File(testDir.toString()).getAbsoluteFile();
483     // Have it cleaned up on exit
484     boolean b = deleteOnExit();
485     if (b) clusterTestDir.deleteOnExit();
486     conf.set(TEST_DIRECTORY_KEY, clusterTestDir.getPath());
487     LOG.info("Created new mini-cluster data directory: " + clusterTestDir + ", deleteOnExit=" + b);
488   }
489 
490   /**
491    * Returns a Path in the test filesystem, obtained from {@link #getTestFileSystem()}
492    * to write temporary test data. Call this method after setting up the mini dfs cluster
493    * if the test relies on it.
494    * @return a unique path in the test filesystem
495    */
496   public Path getDataTestDirOnTestFS() throws IOException {
497     if (dataTestDirOnTestFS == null) {
498       setupDataTestDirOnTestFS();
499     }
500 
501     return dataTestDirOnTestFS;
502   }
503 
504   /**
505    * Returns a Path in the test filesystem, obtained from {@link #getTestFileSystem()}
506    * to write temporary test data. Call this method after setting up the mini dfs cluster
507    * if the test relies on it.
508    * @return a unique path in the test filesystem
509    * @param subdirName name of the subdir to create under the base test dir
510    */
511   public Path getDataTestDirOnTestFS(final String subdirName) throws IOException {
512     return new Path(getDataTestDirOnTestFS(), subdirName);
513   }
514 
515   /**
516    * Sets up a path in test filesystem to be used by tests.
517    * Creates a new directory if not already setup.
518    */
519   private void setupDataTestDirOnTestFS() throws IOException {
520     if (dataTestDirOnTestFS != null) {
521       LOG.warn("Data test on test fs dir already setup in "
522           + dataTestDirOnTestFS.toString());
523       return;
524     }
525     dataTestDirOnTestFS = getNewDataTestDirOnTestFS();
526   }
527 
528   /**
529    * Sets up a new path in test filesystem to be used by tests.
530    */
531   private Path getNewDataTestDirOnTestFS() throws IOException {
532     //The file system can be either local, mini dfs, or if the configuration
533     //is supplied externally, it can be an external cluster FS. If it is a local
534     //file system, the tests should use getBaseTestDir, otherwise, we can use
535     //the working directory, and create a unique sub dir there
536     FileSystem fs = getTestFileSystem();
537     Path newDataTestDir = null;
538     if (fs.getUri().getScheme().equals(FileSystem.getLocal(conf).getUri().getScheme())) {
539       File dataTestDir = new File(getDataTestDir().toString());
540       if (deleteOnExit()) dataTestDir.deleteOnExit();
541       newDataTestDir = new Path(dataTestDir.getAbsolutePath());
542     } else {
543       Path base = getBaseTestDirOnTestFS();
544       String randomStr = UUID.randomUUID().toString();
545       newDataTestDir = new Path(base, randomStr);
546       if (deleteOnExit()) fs.deleteOnExit(newDataTestDir);
547     }
548     return newDataTestDir;
549   }
550 
551   /**
552    * Cleans the test data directory on the test filesystem.
553    * @return True if we removed the test dirs
554    * @throws IOException
555    */
556   public boolean cleanupDataTestDirOnTestFS() throws IOException {
557     boolean ret = getTestFileSystem().delete(dataTestDirOnTestFS, true);
558     if (ret)
559       dataTestDirOnTestFS = null;
560     return ret;
561   }
562 
563   /**
564    * Cleans a subdirectory under the test data directory on the test filesystem.
565    * @return True if we removed child
566    * @throws IOException
567    */
568   public boolean cleanupDataTestDirOnTestFS(String subdirName) throws IOException {
569     Path cpath = getDataTestDirOnTestFS(subdirName);
570     return getTestFileSystem().delete(cpath, true);
571   }
572 
573   /**
574    * Start a minidfscluster.
575    * @param servers How many DNs to start.
576    * @throws Exception
577    * @see {@link #shutdownMiniDFSCluster()}
578    * @return The mini dfs cluster created.
579    */
580   public MiniDFSCluster startMiniDFSCluster(int servers) throws Exception {
581     return startMiniDFSCluster(servers, null);
582   }
583 
584   /**
585    * Start a minidfscluster.
586    * This is useful if you want to run datanode on distinct hosts for things
587    * like HDFS block location verification.
588    * If you start MiniDFSCluster without host names, all instances of the
589    * datanodes will have the same host name.
590    * @param hosts hostnames DNs to run on.
591    * @throws Exception
592    * @see {@link #shutdownMiniDFSCluster()}
593    * @return The mini dfs cluster created.
594    */
595   public MiniDFSCluster startMiniDFSCluster(final String hosts[])
596   throws Exception {
597     if ( hosts != null && hosts.length != 0) {
598       return startMiniDFSCluster(hosts.length, hosts);
599     } else {
600       return startMiniDFSCluster(1, null);
601     }
602   }
603 
604   /**
605    * Start a minidfscluster.
606    * Can only create one.
607    * @param servers How many DNs to start.
608    * @param hosts hostnames DNs to run on.
609    * @throws Exception
610    * @see {@link #shutdownMiniDFSCluster()}
611    * @return The mini dfs cluster created.
612    */
613   public MiniDFSCluster startMiniDFSCluster(int servers, final String hosts[])
614   throws Exception {
615     createDirsAndSetProperties();
616     EditLogFileOutputStream.setShouldSkipFsyncForTesting(true);
617 
618     // Error level to skip some warnings specific to the minicluster. See HBASE-4709
619     org.apache.log4j.Logger.getLogger(org.apache.hadoop.metrics2.util.MBeans.class).
620         setLevel(org.apache.log4j.Level.ERROR);
621     org.apache.log4j.Logger.getLogger(org.apache.hadoop.metrics2.impl.MetricsSystemImpl.class).
622         setLevel(org.apache.log4j.Level.ERROR);
623 
624 
625     this.dfsCluster = new MiniDFSCluster(0, this.conf, servers, true, true,
626       true, null, null, hosts, null);
627 
628     // Set this just-started cluster as our filesystem.
629     setFs();
630 
631     // Wait for the cluster to be totally up
632     this.dfsCluster.waitClusterUp();
633 
634     //reset the test directory for test file system
635     dataTestDirOnTestFS = null;
636 
637     return this.dfsCluster;
638   }
639 
640   private void setFs() throws IOException {
641     if(this.dfsCluster == null){
642       LOG.info("Skipping setting fs because dfsCluster is null");
643       return;
644     }
645     FileSystem fs = this.dfsCluster.getFileSystem();
646     FSUtils.setFsDefault(this.conf, new Path(fs.getUri()));
647     if (this.conf.getBoolean(USE_LOCAL_FILESYSTEM, false)) {
648       FSUtils.setFsDefault(this.conf, new Path("file:///"));
649     }
650   }
651 
652   public MiniDFSCluster startMiniDFSCluster(int servers, final  String racks[], String hosts[])
653       throws Exception {
654     createDirsAndSetProperties();
655     this.dfsCluster = new MiniDFSCluster(0, this.conf, servers, true, true,
656         true, null, racks, hosts, null);
657 
658     // Set this just-started cluster as our filesystem.
659     FileSystem fs = this.dfsCluster.getFileSystem();
660     FSUtils.setFsDefault(this.conf, new Path(fs.getUri()));
661 
662     // Wait for the cluster to be totally up
663     this.dfsCluster.waitClusterUp();
664 
665     //reset the test directory for test file system
666     dataTestDirOnTestFS = null;
667 
668     return this.dfsCluster;
669   }
670 
671   public MiniDFSCluster startMiniDFSClusterForTestWAL(int namenodePort) throws IOException {
672     createDirsAndSetProperties();
673     dfsCluster = new MiniDFSCluster(namenodePort, conf, 5, false, true, true, null,
674         null, null, null);
675     return dfsCluster;
676   }
677 
678   /** This is used before starting HDFS and map-reduce mini-clusters */
679   private void createDirsAndSetProperties() throws IOException {
680     setupClusterTestDir();
681     System.setProperty(TEST_DIRECTORY_KEY, clusterTestDir.getPath());
682     createDirAndSetProperty("cache_data", "test.cache.data");
683     createDirAndSetProperty("hadoop_tmp", "hadoop.tmp.dir");
684     hadoopLogDir = createDirAndSetProperty("hadoop_logs", "hadoop.log.dir");
685     createDirAndSetProperty("mapred_local", "mapreduce.cluster.local.dir");
686     createDirAndSetProperty("mapred_temp", "mapreduce.cluster.temp.dir");
687     enableShortCircuit();
688 
689     Path root = getDataTestDirOnTestFS("hadoop");
690     conf.set(MapreduceTestingShim.getMROutputDirProp(),
691       new Path(root, "mapred-output-dir").toString());
692     conf.set("mapreduce.jobtracker.system.dir", new Path(root, "mapred-system-dir").toString());
693     conf.set("mapreduce.jobtracker.staging.root.dir",
694       new Path(root, "mapreduce-jobtracker-staging-root-dir").toString());
695     conf.set("mapreduce.job.working.dir", new Path(root, "mapred-working-dir").toString());
696   }
697 
698 
699   /**
700    *  Get the HBase setting for dfs.client.read.shortcircuit from the conf or a system property.
701    *  This allows to specify this parameter on the command line.
702    *   If not set, default is true.
703    */
704   public boolean isReadShortCircuitOn(){
705     final String propName = "hbase.tests.use.shortcircuit.reads";
706     String readOnProp = System.getProperty(propName);
707     if (readOnProp != null){
708       return  Boolean.parseBoolean(readOnProp);
709     } else {
710       return conf.getBoolean(propName, false);
711     }
712   }
713 
714   /** Enable the short circuit read, unless configured differently.
715    * Set both HBase and HDFS settings, including skipping the hdfs checksum checks.
716    */
717   private void enableShortCircuit() {
718     if (isReadShortCircuitOn()) {
719       String curUser = System.getProperty("user.name");
720       LOG.info("read short circuit is ON for user " + curUser);
721       // read short circuit, for hdfs
722       conf.set("dfs.block.local-path-access.user", curUser);
723       // read short circuit, for hbase
724       conf.setBoolean("dfs.client.read.shortcircuit", true);
725       // Skip checking checksum, for the hdfs client and the datanode
726       conf.setBoolean("dfs.client.read.shortcircuit.skip.checksum", true);
727     } else {
728       LOG.info("read short circuit is OFF");
729     }
730   }
731 
732   private String createDirAndSetProperty(final String relPath, String property) {
733     String path = getDataTestDir(relPath).toString();
734     System.setProperty(property, path);
735     conf.set(property, path);
736     new File(path).mkdirs();
737     LOG.info("Setting " + property + " to " + path + " in system properties and HBase conf");
738     return path;
739   }
740 
741   /**
742    * Shuts down instance created by call to {@link #startMiniDFSCluster(int)}
743    * or does nothing.
744    * @throws IOException
745    */
746   public void shutdownMiniDFSCluster() throws IOException {
747     if (this.dfsCluster != null) {
748       // The below throws an exception per dn, AsynchronousCloseException.
749       this.dfsCluster.shutdown();
750       dfsCluster = null;
751       dataTestDirOnTestFS = null;
752       FSUtils.setFsDefault(this.conf, new Path("file:///"));
753     }
754   }
755 
756   /**
757    * Call this if you only want a zk cluster.
758    * @see #startMiniZKCluster() if you want zk + dfs + hbase mini cluster.
759    * @throws Exception
760    * @see #shutdownMiniZKCluster()
761    * @return zk cluster started.
762    */
763   public MiniZooKeeperCluster startMiniZKCluster() throws Exception {
764     return startMiniZKCluster(1);
765   }
766 
767   /**
768    * Call this if you only want a zk cluster.
769    * @param zooKeeperServerNum
770    * @see #startMiniZKCluster() if you want zk + dfs + hbase mini cluster.
771    * @throws Exception
772    * @see #shutdownMiniZKCluster()
773    * @return zk cluster started.
774    */
775   public MiniZooKeeperCluster startMiniZKCluster(
776       final int zooKeeperServerNum,
777       final int ... clientPortList)
778       throws Exception {
779     setupClusterTestDir();
780     return startMiniZKCluster(clusterTestDir, zooKeeperServerNum, clientPortList);
781   }
782 
783   private MiniZooKeeperCluster startMiniZKCluster(final File dir)
784     throws Exception {
785     return startMiniZKCluster(dir, 1, null);
786   }
787 
788   /**
789    * Start a mini ZK cluster. If the property "test.hbase.zookeeper.property.clientPort" is set
790    *  the port mentionned is used as the default port for ZooKeeper.
791    */
792   private MiniZooKeeperCluster startMiniZKCluster(final File dir,
793       final int zooKeeperServerNum,
794       final int [] clientPortList)
795   throws Exception {
796     if (this.zkCluster != null) {
797       throw new IOException("Cluster already running at " + dir);
798     }
799     this.passedZkCluster = false;
800     this.zkCluster = new MiniZooKeeperCluster(this.getConfiguration());
801     final int defPort = this.conf.getInt("test.hbase.zookeeper.property.clientPort", 0);
802     if (defPort > 0){
803       // If there is a port in the config file, we use it.
804       this.zkCluster.setDefaultClientPort(defPort);
805     }
806 
807     if (clientPortList != null) {
808       // Ignore extra client ports
809       int clientPortListSize = (clientPortList.length <= zooKeeperServerNum) ?
810           clientPortList.length : zooKeeperServerNum;
811       for (int i=0; i < clientPortListSize; i++) {
812         this.zkCluster.addClientPort(clientPortList[i]);
813       }
814     }
815     int clientPort =   this.zkCluster.startup(dir,zooKeeperServerNum);
816     this.conf.set(HConstants.ZOOKEEPER_CLIENT_PORT,
817       Integer.toString(clientPort));
818     return this.zkCluster;
819   }
820 
821   /**
822    * Shuts down zk cluster created by call to {@link #startMiniZKCluster(File)}
823    * or does nothing.
824    * @throws IOException
825    * @see #startMiniZKCluster()
826    */
827   public void shutdownMiniZKCluster() throws IOException {
828     if (this.zkCluster != null) {
829       this.zkCluster.shutdown();
830       this.zkCluster = null;
831     }
832   }
833 
834   /**
835    * Start up a minicluster of hbase, dfs, and zookeeper.
836    * @throws Exception
837    * @return Mini hbase cluster instance created.
838    * @see {@link #shutdownMiniDFSCluster()}
839    */
840   public MiniHBaseCluster startMiniCluster() throws Exception {
841     return startMiniCluster(1, 1);
842   }
843 
844   /**
845    * Start up a minicluster of hbase, dfs, and zookeeper.
846    * Set the <code>create</code> flag to create root or data directory path or not
847    * (will overwrite if dir already exists)
848    * @throws Exception
849    * @return Mini hbase cluster instance created.
850    * @see {@link #shutdownMiniDFSCluster()}
851    */
852   public MiniHBaseCluster startMiniCluster(final int numSlaves, boolean create)
853   throws Exception {
854     return startMiniCluster(1, numSlaves, create);
855   }
856 
857   /**
858    * Start up a minicluster of hbase, optionally dfs, and zookeeper.
859    * Modifies Configuration.  Homes the cluster data directory under a random
860    * subdirectory in a directory under System property test.build.data.
861    * Directory is cleaned up on exit.
862    * @param numSlaves Number of slaves to start up.  We'll start this many
863    * datanodes and regionservers.  If numSlaves is > 1, then make sure
864    * hbase.regionserver.info.port is -1 (i.e. no ui per regionserver) otherwise
865    * bind errors.
866    * @throws Exception
867    * @see {@link #shutdownMiniCluster()}
868    * @return Mini hbase cluster instance created.
869    */
870   public MiniHBaseCluster startMiniCluster(final int numSlaves)
871   throws Exception {
872     return startMiniCluster(1, numSlaves, false);
873   }
874 
875   /**
876    * Start minicluster. Whether to create a new root or data dir path even if such a path
877    * has been created earlier is decided based on flag <code>create</code>
878    * @throws Exception
879    * @see {@link #shutdownMiniCluster()}
880    * @return Mini hbase cluster instance created.
881    */
882   public MiniHBaseCluster startMiniCluster(final int numMasters,
883       final int numSlaves, boolean create)
884     throws Exception {
885       return startMiniCluster(numMasters, numSlaves, null, create);
886   }
887 
888   /**
889    * start minicluster
890    * @throws Exception
891    * @see {@link #shutdownMiniCluster()}
892    * @return Mini hbase cluster instance created.
893    */
894   public MiniHBaseCluster startMiniCluster(final int numMasters,
895     final int numSlaves)
896   throws Exception {
897     return startMiniCluster(numMasters, numSlaves, null, false);
898   }
899 
900   public MiniHBaseCluster startMiniCluster(final int numMasters,
901       final int numSlaves, final String[] dataNodeHosts, boolean create)
902       throws Exception {
903     return startMiniCluster(numMasters, numSlaves, numSlaves, dataNodeHosts,
904         null, null, create);
905   }
906 
907   /**
908    * Start up a minicluster of hbase, optionally dfs, and zookeeper.
909    * Modifies Configuration.  Homes the cluster data directory under a random
910    * subdirectory in a directory under System property test.build.data.
911    * Directory is cleaned up on exit.
912    * @param numMasters Number of masters to start up.  We'll start this many
913    * hbase masters.  If numMasters > 1, you can find the active/primary master
914    * with {@link MiniHBaseCluster#getMaster()}.
915    * @param numSlaves Number of slaves to start up.  We'll start this many
916    * regionservers. If dataNodeHosts == null, this also indicates the number of
917    * datanodes to start. If dataNodeHosts != null, the number of datanodes is
918    * based on dataNodeHosts.length.
919    * If numSlaves is > 1, then make sure
920    * hbase.regionserver.info.port is -1 (i.e. no ui per regionserver) otherwise
921    * bind errors.
922    * @param dataNodeHosts hostnames DNs to run on.
923    * This is useful if you want to run datanode on distinct hosts for things
924    * like HDFS block location verification.
925    * If you start MiniDFSCluster without host names,
926    * all instances of the datanodes will have the same host name.
927    * @throws Exception
928    * @see {@link #shutdownMiniCluster()}
929    * @return Mini hbase cluster instance created.
930    */
931   public MiniHBaseCluster startMiniCluster(final int numMasters,
932       final int numSlaves, final String[] dataNodeHosts) throws Exception {
933     return startMiniCluster(numMasters, numSlaves, numSlaves, dataNodeHosts,
934         null, null);
935   }
936 
937   /**
938    * Same as {@link #startMiniCluster(int, int)}, but with custom number of datanodes.
939    * @param numDataNodes Number of data nodes.
940    */
941   public MiniHBaseCluster startMiniCluster(final int numMasters,
942       final int numSlaves, final int numDataNodes) throws Exception {
943     return startMiniCluster(numMasters, numSlaves, numDataNodes, null, null, null);
944   }
945 
946   /**
947    * Start up a minicluster of hbase, optionally dfs, and zookeeper.
948    * Modifies Configuration.  Homes the cluster data directory under a random
949    * subdirectory in a directory under System property test.build.data.
950    * Directory is cleaned up on exit.
951    * @param numMasters Number of masters to start up.  We'll start this many
952    * hbase masters.  If numMasters > 1, you can find the active/primary master
953    * with {@link MiniHBaseCluster#getMaster()}.
954    * @param numSlaves Number of slaves to start up.  We'll start this many
955    * regionservers. If dataNodeHosts == null, this also indicates the number of
956    * datanodes to start. If dataNodeHosts != null, the number of datanodes is
957    * based on dataNodeHosts.length.
958    * If numSlaves is > 1, then make sure
959    * hbase.regionserver.info.port is -1 (i.e. no ui per regionserver) otherwise
960    * bind errors.
961    * @param dataNodeHosts hostnames DNs to run on.
962    * This is useful if you want to run datanode on distinct hosts for things
963    * like HDFS block location verification.
964    * If you start MiniDFSCluster without host names,
965    * all instances of the datanodes will have the same host name.
966    * @param masterClass The class to use as HMaster, or null for default
967    * @param regionserverClass The class to use as HRegionServer, or null for
968    * default
969    * @throws Exception
970    * @see {@link #shutdownMiniCluster()}
971    * @return Mini hbase cluster instance created.
972    */
973   public MiniHBaseCluster startMiniCluster(final int numMasters,
974       final int numSlaves, final String[] dataNodeHosts, Class<? extends HMaster> masterClass,
975       Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
976           throws Exception {
977     return startMiniCluster(
978         numMasters, numSlaves, numSlaves, dataNodeHosts, masterClass, regionserverClass);
979   }
980 
981   public MiniHBaseCluster startMiniCluster(final int numMasters,
982       final int numSlaves, int numDataNodes, final String[] dataNodeHosts,
983       Class<? extends HMaster> masterClass,
984       Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
985     throws Exception {
986     return startMiniCluster(numMasters, numSlaves, numDataNodes, dataNodeHosts,
987         masterClass, regionserverClass, false);
988   }
989 
990   /**
991    * Same as {@link #startMiniCluster(int, int, String[], Class, Class)}, but with custom
992    * number of datanodes.
993    * @param numDataNodes Number of data nodes.
994    * @param create Set this flag to create a new
995    * root or data directory path or not (will overwrite if exists already).
996    */
997   public MiniHBaseCluster startMiniCluster(final int numMasters,
998     final int numSlaves, int numDataNodes, final String[] dataNodeHosts,
999     Class<? extends HMaster> masterClass,
1000     Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass,
1001     boolean create)
1002   throws Exception {
1003     if (dataNodeHosts != null && dataNodeHosts.length != 0) {
1004       numDataNodes = dataNodeHosts.length;
1005     }
1006 
1007     LOG.info("Starting up minicluster with " + numMasters + " master(s) and " +
1008         numSlaves + " regionserver(s) and " + numDataNodes + " datanode(s)");
1009 
1010     // If we already put up a cluster, fail.
1011     if (miniClusterRunning) {
1012       throw new IllegalStateException("A mini-cluster is already running");
1013     }
1014     miniClusterRunning = true;
1015 
1016     setupClusterTestDir();
1017     System.setProperty(TEST_DIRECTORY_KEY, this.clusterTestDir.getPath());
1018 
1019     // Bring up mini dfs cluster. This spews a bunch of warnings about missing
1020     // scheme. Complaints are 'Scheme is undefined for build/test/data/dfs/name1'.
1021     if(this.dfsCluster == null) {
1022       dfsCluster = startMiniDFSCluster(numDataNodes, dataNodeHosts);
1023     }
1024 
1025     // Start up a zk cluster.
1026     if (this.zkCluster == null) {
1027       startMiniZKCluster(clusterTestDir);
1028     }
1029 
1030     // Start the MiniHBaseCluster
1031     return startMiniHBaseCluster(numMasters, numSlaves, masterClass,
1032       regionserverClass, create);
1033   }
1034 
1035   public MiniHBaseCluster startMiniHBaseCluster(final int numMasters, final int numSlaves)
1036       throws IOException, InterruptedException{
1037     return startMiniHBaseCluster(numMasters, numSlaves, null, null, false);
1038   }
1039 
1040   /**
1041    * Starts up mini hbase cluster.  Usually used after call to
1042    * {@link #startMiniCluster(int, int)} when doing stepped startup of clusters.
1043    * Usually you won't want this.  You'll usually want {@link #startMiniCluster()}.
1044    * @param numMasters
1045    * @param numSlaves
1046    * @param create Whether to create a
1047    * root or data directory path or not; will overwrite if exists already.
1048    * @return Reference to the hbase mini hbase cluster.
1049    * @throws IOException
1050    * @throws InterruptedException
1051    * @see {@link #startMiniCluster()}
1052    */
1053   public MiniHBaseCluster startMiniHBaseCluster(final int numMasters,
1054         final int numSlaves, Class<? extends HMaster> masterClass,
1055         Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass,
1056         boolean create)
1057   throws IOException, InterruptedException {
1058     // Now do the mini hbase cluster.  Set the hbase.rootdir in config.
1059     createRootDir(create);
1060 
1061     // These settings will make the server waits until this exact number of
1062     // regions servers are connected.
1063     if (conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1) == -1) {
1064       conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, numSlaves);
1065     }
1066     if (conf.getInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, -1) == -1) {
1067       conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, numSlaves);
1068     }
1069 
1070     Configuration c = new Configuration(this.conf);
1071     this.hbaseCluster =
1072         new MiniHBaseCluster(c, numMasters, numSlaves, masterClass, regionserverClass);
1073     // Don't leave here till we've done a successful scan of the hbase:meta
1074     Table t = new HTable(c, TableName.META_TABLE_NAME);
1075     ResultScanner s = t.getScanner(new Scan());
1076     while (s.next() != null) {
1077       continue;
1078     }
1079     s.close();
1080     t.close();
1081 
1082     getHBaseAdmin(); // create immediately the hbaseAdmin
1083     LOG.info("Minicluster is up");
1084 
1085     // Set the hbase.fs.tmp.dir config to make sure that we have some default value. This is
1086     // for tests that do not read hbase-defaults.xml
1087     setHBaseFsTmpDir();
1088 
1089     return (MiniHBaseCluster)this.hbaseCluster;
1090   }
1091 
1092   /**
1093    * Starts the hbase cluster up again after shutting it down previously in a
1094    * test.  Use this if you want to keep dfs/zk up and just stop/start hbase.
1095    * @param servers number of region servers
1096    * @throws IOException
1097    */
1098   public void restartHBaseCluster(int servers) throws IOException, InterruptedException {
1099     this.hbaseCluster = new MiniHBaseCluster(this.conf, servers);
1100     // Don't leave here till we've done a successful scan of the hbase:meta
1101     Table t = new HTable(new Configuration(this.conf), TableName.META_TABLE_NAME);
1102     ResultScanner s = t.getScanner(new Scan());
1103     while (s.next() != null) {
1104       // do nothing
1105     }
1106     LOG.info("HBase has been restarted");
1107     s.close();
1108     t.close();
1109   }
1110 
1111   /**
1112    * @return Current mini hbase cluster. Only has something in it after a call
1113    * to {@link #startMiniCluster()}.
1114    * @see #startMiniCluster()
1115    */
1116   public MiniHBaseCluster getMiniHBaseCluster() {
1117     if (this.hbaseCluster == null || this.hbaseCluster instanceof MiniHBaseCluster) {
1118       return (MiniHBaseCluster)this.hbaseCluster;
1119     }
1120     throw new RuntimeException(hbaseCluster + " not an instance of " +
1121                                MiniHBaseCluster.class.getName());
1122   }
1123 
1124   /**
1125    * Stops mini hbase, zk, and hdfs clusters.
1126    * @throws IOException
1127    * @see {@link #startMiniCluster(int)}
1128    */
1129   public void shutdownMiniCluster() throws Exception {
1130     LOG.info("Shutting down minicluster");
1131     if (this.connection != null && !this.connection.isClosed()) {
1132       this.connection.close();
1133       this.connection = null;
1134     }
1135     shutdownMiniHBaseCluster();
1136     if (!this.passedZkCluster){
1137       shutdownMiniZKCluster();
1138     }
1139     shutdownMiniDFSCluster();
1140 
1141     cleanupTestDir();
1142     miniClusterRunning = false;
1143     LOG.info("Minicluster is down");
1144   }
1145 
1146   /**
1147    * @return True if we removed the test dirs
1148    * @throws IOException
1149    */
1150   @Override
1151   public boolean cleanupTestDir() throws IOException {
1152     boolean ret = super.cleanupTestDir();
1153     if (deleteDir(this.clusterTestDir)) {
1154       this.clusterTestDir = null;
1155       return ret & true;
1156     }
1157     return false;
1158   }
1159 
1160   /**
1161    * Shutdown HBase mini cluster.  Does not shutdown zk or dfs if running.
1162    * @throws IOException
1163    */
1164   public void shutdownMiniHBaseCluster() throws IOException {
1165     if (hbaseAdmin != null) {
1166       hbaseAdmin.close0();
1167       hbaseAdmin = null;
1168     }
1169 
1170     // unset the configuration for MIN and MAX RS to start
1171     conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1);
1172     conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MAXTOSTART, -1);
1173     if (this.hbaseCluster != null) {
1174       this.hbaseCluster.shutdown();
1175       // Wait till hbase is down before going on to shutdown zk.
1176       this.hbaseCluster.waitUntilShutDown();
1177       this.hbaseCluster = null;
1178     }
1179 
1180     if (zooKeeperWatcher != null) {
1181       zooKeeperWatcher.close();
1182       zooKeeperWatcher = null;
1183     }
1184   }
1185 
1186   /**
1187    * Returns the path to the default root dir the minicluster uses. If <code>create</code>
1188    * is true, a new root directory path is fetched irrespective of whether it has been fetched
1189    * before or not. If false, previous path is used.
1190    * Note: this does not cause the root dir to be created.
1191    * @return Fully qualified path for the default hbase root dir
1192    * @throws IOException
1193    */
1194   public Path getDefaultRootDirPath(boolean create) throws IOException {
1195     if (!create) {
1196       return getDataTestDirOnTestFS();
1197     } else {
1198       return getNewDataTestDirOnTestFS();
1199     }
1200   }
1201 
1202   /**
1203    * Same as {{@link HBaseTestingUtility#getDefaultRootDirPath(boolean create)}
1204    * except that <code>create</code> flag is false.
1205    * Note: this does not cause the root dir to be created.
1206    * @return Fully qualified path for the default hbase root dir
1207    * @throws IOException
1208    */
1209   public Path getDefaultRootDirPath() throws IOException {
1210     return getDefaultRootDirPath(false);
1211   }
1212 
1213   /**
1214    * Creates an hbase rootdir in user home directory.  Also creates hbase
1215    * version file.  Normally you won't make use of this method.  Root hbasedir
1216    * is created for you as part of mini cluster startup.  You'd only use this
1217    * method if you were doing manual operation.
1218    * @param create This flag decides whether to get a new
1219    * root or data directory path or not, if it has been fetched already.
1220    * Note : Directory will be made irrespective of whether path has been fetched or not.
1221    * If directory already exists, it will be overwritten
1222    * @return Fully qualified path to hbase root dir
1223    * @throws IOException
1224    */
1225   public Path createRootDir(boolean create) throws IOException {
1226     FileSystem fs = FileSystem.get(this.conf);
1227     Path hbaseRootdir = getDefaultRootDirPath(create);
1228     FSUtils.setRootDir(this.conf, hbaseRootdir);
1229     fs.mkdirs(hbaseRootdir);
1230     FSUtils.setVersion(fs, hbaseRootdir);
1231     return hbaseRootdir;
1232   }
1233 
1234   /**
1235    * Same as {@link HBaseTestingUtility#createRootDir(boolean create)}
1236    * except that <code>create</code> flag is false.
1237    * @return Fully qualified path to hbase root dir
1238    * @throws IOException
1239    */
1240   public Path createRootDir() throws IOException {
1241     return createRootDir(false);
1242   }
1243 
1244 
1245   private void setHBaseFsTmpDir() throws IOException {
1246     String hbaseFsTmpDirInString = this.conf.get("hbase.fs.tmp.dir");
1247     if (hbaseFsTmpDirInString == null) {
1248       this.conf.set("hbase.fs.tmp.dir",  getDataTestDirOnTestFS("hbase-staging").toString());
1249       LOG.info("Setting hbase.fs.tmp.dir to " + this.conf.get("hbase.fs.tmp.dir"));
1250     } else {
1251       LOG.info("The hbase.fs.tmp.dir is set to " + hbaseFsTmpDirInString);
1252     }
1253   }
1254 
1255   /**
1256    * Flushes all caches in the mini hbase cluster
1257    * @throws IOException
1258    */
1259   public void flush() throws IOException {
1260     getMiniHBaseCluster().flushcache();
1261   }
1262 
1263   /**
1264    * Flushes all caches in the mini hbase cluster
1265    * @throws IOException
1266    */
1267   public void flush(TableName tableName) throws IOException {
1268     getMiniHBaseCluster().flushcache(tableName);
1269   }
1270 
1271   /**
1272    * Compact all regions in the mini hbase cluster
1273    * @throws IOException
1274    */
1275   public void compact(boolean major) throws IOException {
1276     getMiniHBaseCluster().compact(major);
1277   }
1278 
1279   /**
1280    * Compact all of a table's reagion in the mini hbase cluster
1281    * @throws IOException
1282    */
1283   public void compact(TableName tableName, boolean major) throws IOException {
1284     getMiniHBaseCluster().compact(tableName, major);
1285   }
1286 
1287   /**
1288    * Create a table.
1289    * @param tableName
1290    * @param family
1291    * @return An HTable instance for the created table.
1292    * @throws IOException
1293    */
1294   public Table createTable(TableName tableName, String family)
1295   throws IOException{
1296     return createTable(tableName, new String[]{family});
1297   }
1298 
1299   /**
1300    * Create a table.
1301    * @param tableName
1302    * @param family
1303    * @return An HTable instance for the created table.
1304    * @throws IOException
1305    */
1306   public HTable createTable(byte[] tableName, byte[] family)
1307   throws IOException{
1308     return createTable(TableName.valueOf(tableName), new byte[][]{family});
1309   }
1310 
1311   /**
1312    * Create a table.
1313    * @param tableName
1314    * @param families
1315    * @return An HTable instance for the created table.
1316    * @throws IOException
1317    */
1318   public Table createTable(TableName tableName, String[] families)
1319   throws IOException {
1320     List<byte[]> fams = new ArrayList<byte[]>(families.length);
1321     for (String family : families) {
1322       fams.add(Bytes.toBytes(family));
1323     }
1324     return createTable(tableName, fams.toArray(new byte[0][]));
1325   }
1326 
1327   /**
1328    * Create a table.
1329    * @param tableName
1330    * @param family
1331    * @return An HTable instance for the created table.
1332    * @throws IOException
1333    */
1334   public HTable createTable(TableName tableName, byte[] family)
1335   throws IOException{
1336     return createTable(tableName, new byte[][]{family});
1337   }
1338 
1339   /**
1340    * Create a table with multiple regions.
1341    * @param tableName
1342    * @param family
1343    * @param numRegions
1344    * @return An HTable instance for the created table.
1345    * @throws IOException
1346    */
1347   public HTable createMultiRegionTable(TableName tableName, byte[] family, int numRegions)
1348       throws IOException {
1349     if (numRegions < 3) throw new IOException("Must create at least 3 regions");
1350     byte[] startKey = Bytes.toBytes("aaaaa");
1351     byte[] endKey = Bytes.toBytes("zzzzz");
1352     byte[][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
1353 
1354     return createTable(tableName, new byte[][] { family }, splitKeys);
1355   }
1356 
1357 
1358   /**
1359    * Create a table.
1360    * @param tableName
1361    * @param families
1362    * @return An HTable instance for the created table.
1363    * @throws IOException
1364    */
1365   public HTable createTable(byte[] tableName, byte[][] families)
1366   throws IOException {
1367     return createTable(tableName, families,
1368         new Configuration(getConfiguration()));
1369   }
1370 
1371   /**
1372    * Create a table.
1373    * @param tableName
1374    * @param families
1375    * @return An HTable instance for the created table.
1376    * @throws IOException
1377    */
1378   public HTable createTable(TableName tableName, byte[][] families)
1379   throws IOException {
1380     return createTable(tableName, families, (byte[][]) null);
1381   }
1382 
1383   /**
1384    * Create a table with multiple regions.
1385    * @param tableName
1386    * @param families
1387    * @return An HTable instance for the created table.
1388    * @throws IOException
1389    */
1390   public HTable createMultiRegionTable(TableName tableName, byte[][] families) throws IOException {
1391     return createTable(tableName, families, KEYS_FOR_HBA_CREATE_TABLE);
1392   }
1393 
1394   /**
1395    * Create a table.
1396    * @param tableName
1397    * @param families
1398    * @param splitKeys
1399    * @return An HTable instance for the created table.
1400    * @throws IOException
1401    */
1402   public HTable createTable(TableName tableName, byte[][] families, byte[][] splitKeys)
1403       throws IOException {
1404     return createTable(tableName, families, splitKeys, new Configuration(getConfiguration()));
1405   }
1406 
1407   public HTable createTable(byte[] tableName, byte[][] families,
1408       int numVersions, byte[] startKey, byte[] endKey, int numRegions) throws IOException {
1409     return createTable(TableName.valueOf(tableName), families, numVersions,
1410         startKey, endKey, numRegions);
1411   }
1412 
1413   public HTable createTable(String tableName, byte[][] families,
1414       int numVersions, byte[] startKey, byte[] endKey, int numRegions) throws IOException {
1415     return createTable(TableName.valueOf(tableName), families, numVersions,
1416         startKey, endKey, numRegions);
1417   }
1418 
1419   public HTable createTable(TableName tableName, byte[][] families,
1420       int numVersions, byte[] startKey, byte[] endKey, int numRegions)
1421   throws IOException{
1422     HTableDescriptor desc = new HTableDescriptor(tableName);
1423     for (byte[] family : families) {
1424       HColumnDescriptor hcd = new HColumnDescriptor(family)
1425           .setMaxVersions(numVersions);
1426       desc.addFamily(hcd);
1427     }
1428     getHBaseAdmin().createTable(desc, startKey, endKey, numRegions);
1429     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1430     waitUntilAllRegionsAssigned(tableName);
1431     return new HTable(getConfiguration(), tableName);
1432   }
1433 
1434   /**
1435    * Create a table.
1436    * @param htd
1437    * @param families
1438    * @param c Configuration to use
1439    * @return An HTable instance for the created table.
1440    * @throws IOException
1441    */
1442   public HTable createTable(HTableDescriptor htd, byte[][] families, Configuration c)
1443   throws IOException {
1444     return createTable(htd, families, (byte[][]) null, c);
1445   }
1446 
1447   /**
1448    * Create a table.
1449    * @param htd
1450    * @param families
1451    * @param splitKeys
1452    * @param c Configuration to use
1453    * @return An HTable instance for the created table.
1454    * @throws IOException
1455    */
1456   public HTable createTable(HTableDescriptor htd, byte[][] families, byte[][] splitKeys,
1457       Configuration c) throws IOException {
1458     for (byte[] family : families) {
1459       HColumnDescriptor hcd = new HColumnDescriptor(family);
1460       // Disable blooms (they are on by default as of 0.95) but we disable them here because
1461       // tests have hard coded counts of what to expect in block cache, etc., and blooms being
1462       // on is interfering.
1463       hcd.setBloomFilterType(BloomType.NONE);
1464       htd.addFamily(hcd);
1465     }
1466     getHBaseAdmin().createTable(htd, splitKeys);
1467     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are
1468     // assigned
1469     waitUntilAllRegionsAssigned(htd.getTableName());
1470     return (HTable) getConnection().getTable(htd.getTableName());
1471   }
1472 
1473   /**
1474    * Create a table.
1475    * @param htd
1476    * @param splitRows
1477    * @return An HTable instance for the created table.
1478    * @throws IOException
1479    */
1480   public HTable createTable(HTableDescriptor htd, byte[][] splitRows)
1481       throws IOException {
1482     getHBaseAdmin().createTable(htd, splitRows);
1483     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1484     waitUntilAllRegionsAssigned(htd.getTableName());
1485     return new HTable(getConfiguration(), htd.getTableName());
1486   }
1487 
1488   /**
1489    * Create a table.
1490    * @param tableName
1491    * @param families
1492    * @param c Configuration to use
1493    * @return An HTable instance for the created table.
1494    * @throws IOException
1495    */
1496   public HTable createTable(TableName tableName, byte[][] families,
1497       final Configuration c)
1498   throws IOException {
1499     return createTable(tableName, families, (byte[][]) null, c);
1500   }
1501 
1502   /**
1503    * Create a table.
1504    * @param tableName
1505    * @param families
1506    * @param splitKeys
1507    * @param c Configuration to use
1508    * @return An HTable instance for the created table.
1509    * @throws IOException
1510    */
1511   public HTable createTable(TableName tableName, byte[][] families, byte[][] splitKeys,
1512       final Configuration c) throws IOException {
1513     return createTable(new HTableDescriptor(tableName), families, splitKeys, c);
1514   }
1515 
1516   /**
1517    * Create a table.
1518    * @param tableName
1519    * @param families
1520    * @param c Configuration to use
1521    * @return An HTable instance for the created table.
1522    * @throws IOException
1523    */
1524   public HTable createTable(byte[] tableName, byte[][] families,
1525       final Configuration c)
1526   throws IOException {
1527     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
1528     for(byte[] family : families) {
1529       HColumnDescriptor hcd = new HColumnDescriptor(family);
1530       // Disable blooms (they are on by default as of 0.95) but we disable them here because
1531       // tests have hard coded counts of what to expect in block cache, etc., and blooms being
1532       // on is interfering.
1533       hcd.setBloomFilterType(BloomType.NONE);
1534       desc.addFamily(hcd);
1535     }
1536     getHBaseAdmin().createTable(desc);
1537     return new HTable(c, desc.getTableName());
1538   }
1539 
1540   /**
1541    * Create a table.
1542    * @param tableName
1543    * @param families
1544    * @param c Configuration to use
1545    * @param numVersions
1546    * @return An HTable instance for the created table.
1547    * @throws IOException
1548    */
1549   public HTable createTable(TableName tableName, byte[][] families,
1550       final Configuration c, int numVersions)
1551   throws IOException {
1552     HTableDescriptor desc = new HTableDescriptor(tableName);
1553     for(byte[] family : families) {
1554       HColumnDescriptor hcd = new HColumnDescriptor(family)
1555           .setMaxVersions(numVersions);
1556       desc.addFamily(hcd);
1557     }
1558     getHBaseAdmin().createTable(desc);
1559     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1560     waitUntilAllRegionsAssigned(tableName);
1561     return new HTable(c, tableName);
1562   }
1563 
1564   /**
1565    * Create a table.
1566    * @param tableName
1567    * @param families
1568    * @param c Configuration to use
1569    * @param numVersions
1570    * @return An HTable instance for the created table.
1571    * @throws IOException
1572    */
1573   public HTable createTable(byte[] tableName, byte[][] families,
1574       final Configuration c, int numVersions)
1575   throws IOException {
1576     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
1577     for(byte[] family : families) {
1578       HColumnDescriptor hcd = new HColumnDescriptor(family)
1579           .setMaxVersions(numVersions);
1580       desc.addFamily(hcd);
1581     }
1582     getHBaseAdmin().createTable(desc);
1583     return new HTable(c, desc.getTableName());
1584   }
1585 
1586   /**
1587    * Create a table.
1588    * @param tableName
1589    * @param family
1590    * @param numVersions
1591    * @return An HTable instance for the created table.
1592    * @throws IOException
1593    */
1594   public HTable createTable(byte[] tableName, byte[] family, int numVersions)
1595   throws IOException {
1596     return createTable(tableName, new byte[][]{family}, numVersions);
1597   }
1598 
1599   /**
1600    * Create a table.
1601    * @param tableName
1602    * @param family
1603    * @param numVersions
1604    * @return An HTable instance for the created table.
1605    * @throws IOException
1606    */
1607   public HTable createTable(TableName tableName, byte[] family, int numVersions)
1608   throws IOException {
1609     return createTable(tableName, new byte[][]{family}, numVersions);
1610   }
1611 
1612   /**
1613    * Create a table.
1614    * @param tableName
1615    * @param families
1616    * @param numVersions
1617    * @return An HTable instance for the created table.
1618    * @throws IOException
1619    */
1620   public HTable createTable(byte[] tableName, byte[][] families,
1621       int numVersions)
1622   throws IOException {
1623     return createTable(TableName.valueOf(tableName), families, numVersions);
1624   }
1625 
1626   /**
1627    * Create a table.
1628    * @param tableName
1629    * @param families
1630    * @param numVersions
1631    * @return An HTable instance for the created table.
1632    * @throws IOException
1633    */
1634   public HTable createTable(TableName tableName, byte[][] families,
1635       int numVersions)
1636   throws IOException {
1637     return createTable(tableName, families, numVersions, (byte[][]) null);
1638   }
1639 
1640   /**
1641    * Create a table.
1642    * @param tableName
1643    * @param families
1644    * @param numVersions
1645    * @param splitKeys
1646    * @return An HTable instance for the created table.
1647    * @throws IOException
1648    */
1649   public HTable createTable(TableName tableName, byte[][] families, int numVersions,
1650       byte[][] splitKeys) throws IOException {
1651     HTableDescriptor desc = new HTableDescriptor(tableName);
1652     for (byte[] family : families) {
1653       HColumnDescriptor hcd = new HColumnDescriptor(family).setMaxVersions(numVersions);
1654       desc.addFamily(hcd);
1655     }
1656     getHBaseAdmin().createTable(desc, splitKeys);
1657     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1658     waitUntilAllRegionsAssigned(tableName);
1659     return new HTable(new Configuration(getConfiguration()), tableName);
1660   }
1661 
1662   /**
1663    * Create a table with multiple regions.
1664    * @param tableName
1665    * @param families
1666    * @param numVersions
1667    * @return An HTable instance for the created table.
1668    * @throws IOException
1669    */
1670   public HTable createMultiRegionTable(TableName tableName, byte[][] families, int numVersions)
1671       throws IOException {
1672     return createTable(tableName, families, numVersions, KEYS_FOR_HBA_CREATE_TABLE);
1673   }
1674 
1675   /**
1676    * Create a table.
1677    * @param tableName
1678    * @param families
1679    * @param numVersions
1680    * @param blockSize
1681    * @return An HTable instance for the created table.
1682    * @throws IOException
1683    */
1684   public HTable createTable(byte[] tableName, byte[][] families,
1685     int numVersions, int blockSize) throws IOException {
1686     return createTable(TableName.valueOf(tableName),
1687         families, numVersions, blockSize);
1688   }
1689 
1690   /**
1691    * Create a table.
1692    * @param tableName
1693    * @param families
1694    * @param numVersions
1695    * @param blockSize
1696    * @return An HTable instance for the created table.
1697    * @throws IOException
1698    */
1699   public HTable createTable(TableName tableName, byte[][] families,
1700     int numVersions, int blockSize) throws IOException {
1701     HTableDescriptor desc = new HTableDescriptor(tableName);
1702     for (byte[] family : families) {
1703       HColumnDescriptor hcd = new HColumnDescriptor(family)
1704           .setMaxVersions(numVersions)
1705           .setBlocksize(blockSize);
1706       desc.addFamily(hcd);
1707     }
1708     getHBaseAdmin().createTable(desc);
1709     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1710     waitUntilAllRegionsAssigned(tableName);
1711     return new HTable(new Configuration(getConfiguration()), tableName);
1712   }
1713 
1714   /**
1715    * Create a table.
1716    * @param tableName
1717    * @param families
1718    * @param numVersions
1719    * @return An HTable instance for the created table.
1720    * @throws IOException
1721    */
1722   public HTable createTable(byte[] tableName, byte[][] families,
1723       int[] numVersions)
1724   throws IOException {
1725     return createTable(TableName.valueOf(tableName), families, numVersions);
1726   }
1727 
1728   /**
1729    * Create a table.
1730    * @param tableName
1731    * @param families
1732    * @param numVersions
1733    * @return An HTable instance for the created table.
1734    * @throws IOException
1735    */
1736   public HTable createTable(TableName tableName, byte[][] families,
1737       int[] numVersions)
1738   throws IOException {
1739     HTableDescriptor desc = new HTableDescriptor(tableName);
1740     int i = 0;
1741     for (byte[] family : families) {
1742       HColumnDescriptor hcd = new HColumnDescriptor(family)
1743           .setMaxVersions(numVersions[i]);
1744       desc.addFamily(hcd);
1745       i++;
1746     }
1747     getHBaseAdmin().createTable(desc);
1748     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1749     waitUntilAllRegionsAssigned(tableName);
1750     return new HTable(new Configuration(getConfiguration()), tableName);
1751   }
1752 
1753   /**
1754    * Create a table.
1755    * @param tableName
1756    * @param family
1757    * @param splitRows
1758    * @return An HTable instance for the created table.
1759    * @throws IOException
1760    */
1761   public HTable createTable(byte[] tableName, byte[] family, byte[][] splitRows)
1762     throws IOException{
1763     return createTable(TableName.valueOf(tableName), family, splitRows);
1764   }
1765 
1766   /**
1767    * Create a table.
1768    * @param tableName
1769    * @param family
1770    * @param splitRows
1771    * @return An HTable instance for the created table.
1772    * @throws IOException
1773    */
1774   public HTable createTable(TableName tableName, byte[] family, byte[][] splitRows)
1775       throws IOException {
1776     HTableDescriptor desc = new HTableDescriptor(tableName);
1777     HColumnDescriptor hcd = new HColumnDescriptor(family);
1778     desc.addFamily(hcd);
1779     getHBaseAdmin().createTable(desc, splitRows);
1780     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1781     waitUntilAllRegionsAssigned(tableName);
1782     return new HTable(getConfiguration(), tableName);
1783   }
1784 
1785   /**
1786    * Create a table with multiple regions.
1787    * @param tableName
1788    * @param family
1789    * @return An HTable instance for the created table.
1790    * @throws IOException
1791    */
1792   public HTable createMultiRegionTable(TableName tableName, byte[] family) throws IOException {
1793     return createTable(tableName, family, KEYS_FOR_HBA_CREATE_TABLE);
1794   }
1795 
1796   /**
1797    * Create a table.
1798    * @param tableName
1799    * @param families
1800    * @param splitRows
1801    * @return An HTable instance for the created table.
1802    * @throws IOException
1803    */
1804   public HTable createTable(byte[] tableName, byte[][] families, byte[][] splitRows)
1805       throws IOException {
1806     HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(tableName));
1807     for(byte[] family:families) {
1808       HColumnDescriptor hcd = new HColumnDescriptor(family);
1809       desc.addFamily(hcd);
1810     }
1811     getHBaseAdmin().createTable(desc, splitRows);
1812     // HBaseAdmin only waits for regions to appear in hbase:meta we should wait until they are assigned
1813     waitUntilAllRegionsAssigned(desc.getTableName());
1814     return new HTable(getConfiguration(), desc.getTableName());
1815   }
1816 
1817   /**
1818    * Create an unmanaged WAL. Be sure to close it when you're through.
1819    */
1820   public static WAL createWal(final Configuration conf, final Path rootDir, final HRegionInfo hri)
1821       throws IOException {
1822     // The WAL subsystem will use the default rootDir rather than the passed in rootDir
1823     // unless I pass along via the conf.
1824     Configuration confForWAL = new Configuration(conf);
1825     confForWAL.set(HConstants.HBASE_DIR, rootDir.toString());
1826     return (new WALFactory(confForWAL,
1827         Collections.<WALActionsListener>singletonList(new MetricsWAL()),
1828         "hregion-" + RandomStringUtils.randomNumeric(8))).
1829         getWAL(hri.getEncodedNameAsBytes(), hri.getTable().getNamespace());
1830   }
1831 
1832   /**
1833    * Create a region with it's own WAL. Be sure to call
1834    * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources.
1835    */
1836   public static HRegion createRegionAndWAL(final HRegionInfo info, final Path rootDir,
1837       final Configuration conf, final HTableDescriptor htd) throws IOException {
1838     return createRegionAndWAL(info, rootDir, conf, htd, true);
1839   }
1840 
1841   /**
1842    * Create a region with it's own WAL. Be sure to call
1843    * {@link HBaseTestingUtility#closeRegionAndWAL(HRegion)} to clean up all resources.
1844    */
1845   public static HRegion createRegionAndWAL(final HRegionInfo info, final Path rootDir,
1846       final Configuration conf, final HTableDescriptor htd, boolean initialize)
1847       throws IOException {
1848     WAL wal = createWal(conf, rootDir, info);
1849     return HRegion.createHRegion(info, rootDir, conf, htd, wal, initialize);
1850   }
1851 
1852   /**
1853    * Close both the region {@code r} and it's underlying WAL. For use in tests.
1854    */
1855   public static void closeRegionAndWAL(final Region r) throws IOException {
1856     closeRegionAndWAL((HRegion)r);
1857   }
1858 
1859   /**
1860    * Close both the HRegion {@code r} and it's underlying WAL. For use in tests.
1861    */
1862   public static void closeRegionAndWAL(final HRegion r) throws IOException {
1863     if (r == null) return;
1864     r.close();
1865     if (r.getWAL() == null) return;
1866     r.getWAL().close();
1867   }
1868 
1869   /**
1870    * Modify a table, synchronous. Waiting logic similar to that of {@code admin.rb#alter_status}.
1871    */
1872   @SuppressWarnings("serial")
1873   public static void modifyTableSync(Admin admin, HTableDescriptor desc)
1874       throws IOException, InterruptedException {
1875     admin.modifyTable(desc.getTableName(), desc);
1876     Pair<Integer, Integer> status = new Pair<Integer, Integer>() {{
1877       setFirst(0);
1878       setSecond(0);
1879     }};
1880     int i = 0;
1881     do {
1882       status = admin.getAlterStatus(desc.getTableName());
1883       if (status.getSecond() != 0) {
1884         LOG.debug(status.getSecond() - status.getFirst() + "/" + status.getSecond()
1885           + " regions updated.");
1886         Thread.sleep(1 * 1000l);
1887       } else {
1888         LOG.debug("All regions updated.");
1889         break;
1890       }
1891     } while (status.getFirst() != 0 && i++ < 500);
1892     if (status.getFirst() != 0) {
1893       throw new IOException("Failed to update all regions even after 500 seconds.");
1894     }
1895   }
1896 
1897   /**
1898    * Set the number of Region replicas.
1899    */
1900   public static void setReplicas(Admin admin, TableName table, int replicaCount)
1901       throws IOException, InterruptedException {
1902     admin.disableTable(table);
1903     HTableDescriptor desc = admin.getTableDescriptor(table);
1904     desc.setRegionReplication(replicaCount);
1905     admin.modifyTable(desc.getTableName(), desc);
1906     admin.enableTable(table);
1907   }
1908 
1909   /**
1910    * Drop an existing table
1911    * @param tableName existing table
1912    */
1913   public void deleteTable(String tableName) throws IOException {
1914     deleteTable(TableName.valueOf(tableName));
1915   }
1916 
1917   /**
1918    * Drop an existing table
1919    * @param tableName existing table
1920    */
1921   public void deleteTable(byte[] tableName) throws IOException {
1922     deleteTable(TableName.valueOf(tableName));
1923   }
1924 
1925   /**
1926    * Drop an existing table
1927    * @param tableName existing table
1928    */
1929   public void deleteTable(TableName tableName) throws IOException {
1930     try {
1931       getHBaseAdmin().disableTable(tableName);
1932     } catch (TableNotEnabledException e) {
1933       LOG.debug("Table: " + tableName + " already disabled, so just deleting it.");
1934     }
1935     getHBaseAdmin().deleteTable(tableName);
1936   }
1937 
1938   /**
1939    * Drop an existing table
1940    * @param tableName existing table
1941    */
1942   public void deleteTableIfAny(TableName tableName) throws IOException {
1943     try {
1944       deleteTable(tableName);
1945     } catch (TableNotFoundException e) {
1946       // ignore
1947     }
1948   }
1949 
1950   // ==========================================================================
1951   // Canned table and table descriptor creation
1952   // TODO replace HBaseTestCase
1953 
1954   public final static byte [] fam1 = Bytes.toBytes("colfamily11");
1955   public final static byte [] fam2 = Bytes.toBytes("colfamily21");
1956   public final static byte [] fam3 = Bytes.toBytes("colfamily31");
1957   public static final byte[][] COLUMNS = {fam1, fam2, fam3};
1958   private static final int MAXVERSIONS = 3;
1959 
1960   public static final char FIRST_CHAR = 'a';
1961   public static final char LAST_CHAR = 'z';
1962   public static final byte [] START_KEY_BYTES = {FIRST_CHAR, FIRST_CHAR, FIRST_CHAR};
1963   public static final String START_KEY = new String(START_KEY_BYTES, HConstants.UTF8_CHARSET);
1964 
1965   /**
1966    * Create a table of name <code>name</code> with {@link COLUMNS} for
1967    * families.
1968    * @param name Name to give table.
1969    * @param versions How many versions to allow per column.
1970    * @return Column descriptor.
1971    */
1972   public HTableDescriptor createTableDescriptor(final String name,
1973       final int minVersions, final int versions, final int ttl, KeepDeletedCells keepDeleted) {
1974     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(name));
1975     for (byte[] cfName : new byte[][]{ fam1, fam2, fam3 }) {
1976       htd.addFamily(new HColumnDescriptor(cfName)
1977           .setMinVersions(minVersions)
1978           .setMaxVersions(versions)
1979           .setKeepDeletedCells(keepDeleted)
1980           .setBlockCacheEnabled(false)
1981           .setTimeToLive(ttl)
1982       );
1983     }
1984     return htd;
1985   }
1986 
1987   /**
1988    * Create a table of name <code>name</code> with {@link COLUMNS} for
1989    * families.
1990    * @param name Name to give table.
1991    * @return Column descriptor.
1992    */
1993   public HTableDescriptor createTableDescriptor(final String name) {
1994     return createTableDescriptor(name,  HColumnDescriptor.DEFAULT_MIN_VERSIONS,
1995         MAXVERSIONS, HConstants.FOREVER, HColumnDescriptor.DEFAULT_KEEP_DELETED);
1996   }
1997 
1998   /**
1999    * Create an HRegion. Be sure to call {@link HBaseTestingUtility#closeRegion(Region)}
2000    * when you're finished with it.
2001    */
2002   public HRegion createHRegion(
2003       final HRegionInfo info,
2004       final Path rootDir,
2005       final Configuration conf,
2006       final HTableDescriptor htd) throws IOException {
2007     return HRegion.createHRegion(info, rootDir, conf, htd);
2008   }
2009 
2010   public HTableDescriptor createTableDescriptor(final TableName tableName,
2011       byte[] family) {
2012     return createTableDescriptor(tableName, new byte[][] {family}, 1);
2013   }
2014 
2015   public HTableDescriptor createTableDescriptor(final TableName tableName,
2016       byte[][] families, int maxVersions) {
2017     HTableDescriptor desc = new HTableDescriptor(tableName);
2018     for (byte[] family : families) {
2019       HColumnDescriptor hcd = new HColumnDescriptor(family)
2020           .setMaxVersions(maxVersions);
2021       desc.addFamily(hcd);
2022     }
2023     return desc;
2024   }
2025 
2026   /**
2027    * Create an HRegion that writes to the local tmp dirs
2028    * @param desc
2029    * @param startKey
2030    * @param endKey
2031    * @return
2032    * @throws IOException
2033    */
2034   public HRegion createLocalHRegion(HTableDescriptor desc, byte [] startKey,
2035       byte [] endKey)
2036   throws IOException {
2037     HRegionInfo hri = new HRegionInfo(desc.getTableName(), startKey, endKey);
2038     return createLocalHRegion(hri, desc);
2039   }
2040 
2041   /**
2042    * Create an HRegion that writes to the local tmp dirs
2043    * @param info
2044    * @param desc
2045    * @return
2046    * @throws IOException
2047    */
2048   public HRegion createLocalHRegion(HRegionInfo info, HTableDescriptor desc) throws IOException {
2049     return HRegion.createHRegion(info, getDataTestDir(), getConfiguration(), desc);
2050   }
2051 
2052   /**
2053    * Create an HRegion that writes to the local tmp dirs with specified wal
2054    * @param info regioninfo
2055    * @param desc table descriptor
2056    * @param wal wal for this region.
2057    * @return created hregion
2058    * @throws IOException
2059    */
2060   public HRegion createLocalHRegion(HRegionInfo info, HTableDescriptor desc, WAL wal)
2061       throws IOException {
2062     return HRegion.createHRegion(info, getDataTestDir(), getConfiguration(), desc, wal);
2063   }
2064 
2065   /**
2066    * @param tableName
2067    * @param startKey
2068    * @param stopKey
2069    * @param callingMethod
2070    * @param conf
2071    * @param isReadOnly
2072    * @param families
2073    * @throws IOException
2074    * @return A region on which you must call
2075    *         {@link HRegion#closeHRegion(HRegion)} when done.
2076    */
2077   public HRegion createLocalHRegion(byte[] tableName, byte[] startKey, byte[] stopKey,
2078       String callingMethod, Configuration conf, boolean isReadOnly, Durability durability,
2079       WAL wal, byte[]... families) throws IOException {
2080     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
2081     htd.setReadOnly(isReadOnly);
2082     for (byte[] family : families) {
2083       HColumnDescriptor hcd = new HColumnDescriptor(family);
2084       // Set default to be three versions.
2085       hcd.setMaxVersions(Integer.MAX_VALUE);
2086       htd.addFamily(hcd);
2087     }
2088     htd.setDurability(durability);
2089     HRegionInfo info = new HRegionInfo(htd.getTableName(), startKey, stopKey, false);
2090     return createLocalHRegion(info, htd, wal);
2091   }
2092   //
2093   // ==========================================================================
2094 
2095   /**
2096    * Provide an existing table name to truncate.
2097    * Scans the table and issues a delete for each row read.
2098    * @param tableName existing table
2099    * @return HTable to that new table
2100    * @throws IOException
2101    */
2102   public HTable deleteTableData(byte[] tableName) throws IOException {
2103     return deleteTableData(TableName.valueOf(tableName));
2104   }
2105 
2106   /**
2107    * Provide an existing table name to truncate.
2108    * Scans the table and issues a delete for each row read.
2109    * @param tableName existing table
2110    * @return HTable to that new table
2111    * @throws IOException
2112    */
2113   public HTable deleteTableData(TableName tableName) throws IOException {
2114     HTable table = new HTable(getConfiguration(), tableName);
2115     Scan scan = new Scan();
2116     ResultScanner resScan = table.getScanner(scan);
2117     for(Result res : resScan) {
2118       Delete del = new Delete(res.getRow());
2119       table.delete(del);
2120     }
2121     resScan = table.getScanner(scan);
2122     resScan.close();
2123     return table;
2124   }
2125 
2126   /**
2127    * Truncate a table using the admin command.
2128    * Effectively disables, deletes, and recreates the table.
2129    * @param tableName table which must exist.
2130    * @param preserveRegions keep the existing split points
2131    * @return HTable for the new table
2132    */
2133   public HTable truncateTable(final TableName tableName, final boolean preserveRegions)
2134       throws IOException {
2135     Admin admin = getHBaseAdmin();
2136     if (!admin.isTableDisabled(tableName)) {
2137       admin.disableTable(tableName);
2138     }
2139     admin.truncateTable(tableName, preserveRegions);
2140     return new HTable(getConfiguration(), tableName);
2141   }
2142 
2143   /**
2144    * Truncate a table using the admin command.
2145    * Effectively disables, deletes, and recreates the table.
2146    * For previous behavior of issuing row deletes, see
2147    * deleteTableData.
2148    * Expressly does not preserve regions of existing table.
2149    * @param tableName table which must exist.
2150    * @return HTable for the new table
2151    */
2152   public HTable truncateTable(final TableName tableName) throws IOException {
2153     return truncateTable(tableName, false);
2154   }
2155 
2156   /**
2157    * Truncate a table using the admin command.
2158    * Effectively disables, deletes, and recreates the table.
2159    * @param tableName table which must exist.
2160    * @param preserveRegions keep the existing split points
2161    * @return HTable for the new table
2162    */
2163   public HTable truncateTable(final byte[] tableName, final boolean preserveRegions)
2164       throws IOException {
2165     return truncateTable(TableName.valueOf(tableName), preserveRegions);
2166   }
2167 
2168   /**
2169    * Truncate a table using the admin command.
2170    * Effectively disables, deletes, and recreates the table.
2171    * For previous behavior of issuing row deletes, see
2172    * deleteTableData.
2173    * Expressly does not preserve regions of existing table.
2174    * @param tableName table which must exist.
2175    * @return HTable for the new table
2176    */
2177   public HTable truncateTable(final byte[] tableName) throws IOException {
2178     return truncateTable(tableName, false);
2179   }
2180 
2181   /**
2182    * Load table with rows from 'aaa' to 'zzz'.
2183    * @param t Table
2184    * @param f Family
2185    * @return Count of rows loaded.
2186    * @throws IOException
2187    */
2188   public int loadTable(final Table t, final byte[] f) throws IOException {
2189     return loadTable(t, new byte[][] {f});
2190   }
2191 
2192   /**
2193    * Load table with rows from 'aaa' to 'zzz'.
2194    * @param t Table
2195    * @param f Family
2196    * @return Count of rows loaded.
2197    * @throws IOException
2198    */
2199   public int loadTable(final Table t, final byte[] f, boolean writeToWAL) throws IOException {
2200     return loadTable(t, new byte[][] {f}, null, writeToWAL);
2201   }
2202 
2203   /**
2204    * Load table of multiple column families with rows from 'aaa' to 'zzz'.
2205    * @param t Table
2206    * @param f Array of Families to load
2207    * @return Count of rows loaded.
2208    * @throws IOException
2209    */
2210   public int loadTable(final Table t, final byte[][] f) throws IOException {
2211     return loadTable(t, f, null);
2212   }
2213 
2214   /**
2215    * Load table of multiple column families with rows from 'aaa' to 'zzz'.
2216    * @param t Table
2217    * @param f Array of Families to load
2218    * @param value the values of the cells. If null is passed, the row key is used as value
2219    * @return Count of rows loaded.
2220    * @throws IOException
2221    */
2222   public int loadTable(final Table t, final byte[][] f, byte[] value) throws IOException {
2223     return loadTable(t, f, value, true);
2224   }
2225 
2226   /**
2227    * Load table of multiple column families with rows from 'aaa' to 'zzz'.
2228    * @param t Table
2229    * @param f Array of Families to load
2230    * @param value the values of the cells. If null is passed, the row key is used as value
2231    * @return Count of rows loaded.
2232    * @throws IOException
2233    */
2234   public int loadTable(final Table t, final byte[][] f, byte[] value, boolean writeToWAL) throws IOException {
2235     List<Put> puts = new ArrayList<>();
2236     for (byte[] row : HBaseTestingUtility.ROWS) {
2237       Put put = new Put(row);
2238       put.setDurability(writeToWAL ? Durability.USE_DEFAULT : Durability.SKIP_WAL);
2239       for (int i = 0; i < f.length; i++) {
2240         put.add(f[i], f[i], value != null ? value : row);
2241       }
2242       puts.add(put);
2243     }
2244     t.put(puts);
2245     return puts.size();
2246   }
2247 
2248   /** A tracker for tracking and validating table rows
2249    * generated with {@link HBaseTestingUtility#loadTable(HTable, byte[])}
2250    */
2251   public static class SeenRowTracker {
2252     int dim = 'z' - 'a' + 1;
2253     int[][][] seenRows = new int[dim][dim][dim]; //count of how many times the row is seen
2254     byte[] startRow;
2255     byte[] stopRow;
2256 
2257     public SeenRowTracker(byte[] startRow, byte[] stopRow) {
2258       this.startRow = startRow;
2259       this.stopRow = stopRow;
2260     }
2261 
2262     void reset() {
2263       for (byte[] row : ROWS) {
2264         seenRows[i(row[0])][i(row[1])][i(row[2])] = 0;
2265       }
2266     }
2267 
2268     int i(byte b) {
2269       return b - 'a';
2270     }
2271 
2272     public void addRow(byte[] row) {
2273       seenRows[i(row[0])][i(row[1])][i(row[2])]++;
2274     }
2275 
2276     /** Validate that all the rows between startRow and stopRow are seen exactly once, and
2277      * all other rows none
2278      */
2279     public void validate() {
2280       for (byte b1 = 'a'; b1 <= 'z'; b1++) {
2281         for (byte b2 = 'a'; b2 <= 'z'; b2++) {
2282           for (byte b3 = 'a'; b3 <= 'z'; b3++) {
2283             int count = seenRows[i(b1)][i(b2)][i(b3)];
2284             int expectedCount = 0;
2285             if (Bytes.compareTo(new byte[] {b1,b2,b3}, startRow) >= 0
2286                 && Bytes.compareTo(new byte[] {b1,b2,b3}, stopRow) < 0) {
2287               expectedCount = 1;
2288             }
2289             if (count != expectedCount) {
2290               String row = new String(new byte[] {b1,b2,b3});
2291               throw new RuntimeException("Row:" + row + " has a seen count of " + count + " instead of " + expectedCount);
2292             }
2293           }
2294         }
2295       }
2296     }
2297   }
2298 
2299   public int loadRegion(final HRegion r, final byte[] f) throws IOException {
2300     return loadRegion(r, f, false);
2301   }
2302 
2303   public int loadRegion(final Region r, final byte[] f) throws IOException {
2304     return loadRegion((HRegion)r, f);
2305   }
2306 
2307   /**
2308    * Load region with rows from 'aaa' to 'zzz'.
2309    * @param r Region
2310    * @param f Family
2311    * @param flush flush the cache if true
2312    * @return Count of rows loaded.
2313    * @throws IOException
2314    */
2315   public int loadRegion(final HRegion r, final byte[] f, final boolean flush)
2316   throws IOException {
2317     byte[] k = new byte[3];
2318     int rowCount = 0;
2319     for (byte b1 = 'a'; b1 <= 'z'; b1++) {
2320       for (byte b2 = 'a'; b2 <= 'z'; b2++) {
2321         for (byte b3 = 'a'; b3 <= 'z'; b3++) {
2322           k[0] = b1;
2323           k[1] = b2;
2324           k[2] = b3;
2325           Put put = new Put(k);
2326           put.setDurability(Durability.SKIP_WAL);
2327           put.add(f, null, k);
2328           if (r.getWAL() == null) {
2329             put.setDurability(Durability.SKIP_WAL);
2330           }
2331           int preRowCount = rowCount;
2332           int pause = 10;
2333           int maxPause = 1000;
2334           while (rowCount == preRowCount) {
2335             try {
2336               r.put(put);
2337               rowCount++;
2338             } catch (RegionTooBusyException e) {
2339               pause = (pause * 2 >= maxPause) ? maxPause : pause * 2;
2340               Threads.sleep(pause);
2341             }
2342           }
2343         }
2344       }
2345       if (flush) {
2346         r.flush(true);
2347       }
2348     }
2349     return rowCount;
2350   }
2351 
2352   public void loadNumericRows(final Table t, final byte[] f, int startRow, int endRow)
2353       throws IOException {
2354     for (int i = startRow; i < endRow; i++) {
2355       byte[] data = Bytes.toBytes(String.valueOf(i));
2356       Put put = new Put(data);
2357       put.add(f, null, data);
2358       t.put(put);
2359     }
2360   }
2361 
2362   public void verifyNumericRows(Table table, final byte[] f, int startRow, int endRow,
2363       int replicaId)
2364       throws IOException {
2365     for (int i = startRow; i < endRow; i++) {
2366       String failMsg = "Failed verification of row :" + i;
2367       byte[] data = Bytes.toBytes(String.valueOf(i));
2368       Get get = new Get(data);
2369       get.setReplicaId(replicaId);
2370       get.setConsistency(Consistency.TIMELINE);
2371       Result result = table.get(get);
2372       assertTrue(failMsg, result.containsColumn(f, null));
2373       assertEquals(failMsg, result.getColumnCells(f, null).size(), 1);
2374       Cell cell = result.getColumnLatestCell(f, null);
2375       assertTrue(failMsg,
2376         Bytes.equals(data, 0, data.length, cell.getValueArray(), cell.getValueOffset(),
2377           cell.getValueLength()));
2378     }
2379   }
2380 
2381   public void verifyNumericRows(Region region, final byte[] f, int startRow, int endRow)
2382       throws IOException {
2383     verifyNumericRows((HRegion)region, f, startRow, endRow);
2384   }
2385 
2386   public void verifyNumericRows(HRegion region, final byte[] f, int startRow, int endRow)
2387       throws IOException {
2388     verifyNumericRows(region, f, startRow, endRow, true);
2389   }
2390 
2391   public void verifyNumericRows(Region region, final byte[] f, int startRow, int endRow,
2392       final boolean present) throws IOException {
2393     verifyNumericRows((HRegion)region, f, startRow, endRow, present);
2394   }
2395 
2396   public void verifyNumericRows(HRegion region, final byte[] f, int startRow, int endRow,
2397       final boolean present) throws IOException {
2398     for (int i = startRow; i < endRow; i++) {
2399       String failMsg = "Failed verification of row :" + i;
2400       byte[] data = Bytes.toBytes(String.valueOf(i));
2401       Result result = region.get(new Get(data));
2402 
2403       boolean hasResult = result != null && !result.isEmpty();
2404       assertEquals(failMsg + result, present, hasResult);
2405       if (!present) continue;
2406 
2407       assertTrue(failMsg, result.containsColumn(f, null));
2408       assertEquals(failMsg, result.getColumnCells(f, null).size(), 1);
2409       Cell cell = result.getColumnLatestCell(f, null);
2410       assertTrue(failMsg,
2411         Bytes.equals(data, 0, data.length, cell.getValueArray(), cell.getValueOffset(),
2412           cell.getValueLength()));
2413     }
2414   }
2415 
2416   public void deleteNumericRows(final HTable t, final byte[] f, int startRow, int endRow)
2417       throws IOException {
2418     for (int i = startRow; i < endRow; i++) {
2419       byte[] data = Bytes.toBytes(String.valueOf(i));
2420       Delete delete = new Delete(data);
2421       delete.deleteFamily(f);
2422       t.delete(delete);
2423     }
2424   }
2425 
2426   /**
2427    * Return the number of rows in the given table.
2428    */
2429   public int countRows(final Table table) throws IOException {
2430     Scan scan = new Scan();
2431     ResultScanner results = table.getScanner(scan);
2432     int count = 0;
2433     for (@SuppressWarnings("unused") Result res : results) {
2434       count++;
2435     }
2436     results.close();
2437     return count;
2438   }
2439 
2440   public int countRows(final Table table, final byte[]... families) throws IOException {
2441     Scan scan = new Scan();
2442     for (byte[] family: families) {
2443       scan.addFamily(family);
2444     }
2445     ResultScanner results = table.getScanner(scan);
2446     int count = 0;
2447     for (@SuppressWarnings("unused") Result res : results) {
2448       count++;
2449     }
2450     results.close();
2451     return count;
2452   }
2453 
2454   /**
2455    * Return the number of rows in the given table.
2456    */
2457   public int countRows(final TableName tableName) throws IOException {
2458     Table table = getConnection().getTable(tableName);
2459     try {
2460       return countRows(table);
2461     } finally {
2462       table.close();
2463     }
2464   }
2465 
2466   /**
2467    * Return an md5 digest of the entire contents of a table.
2468    */
2469   public String checksumRows(final Table table) throws Exception {
2470     Scan scan = new Scan();
2471     ResultScanner results = table.getScanner(scan);
2472     MessageDigest digest = MessageDigest.getInstance("MD5");
2473     for (Result res : results) {
2474       digest.update(res.getRow());
2475     }
2476     results.close();
2477     return digest.toString();
2478   }
2479 
2480   /** All the row values for the data loaded by {@link #loadTable(HTable, byte[])} */
2481   public static final byte[][] ROWS = new byte[(int) Math.pow('z' - 'a' + 1, 3)][3]; // ~52KB
2482   static {
2483     int i = 0;
2484     for (byte b1 = 'a'; b1 <= 'z'; b1++) {
2485       for (byte b2 = 'a'; b2 <= 'z'; b2++) {
2486         for (byte b3 = 'a'; b3 <= 'z'; b3++) {
2487           ROWS[i][0] = b1;
2488           ROWS[i][1] = b2;
2489           ROWS[i][2] = b3;
2490           i++;
2491         }
2492       }
2493     }
2494   }
2495 
2496   public static final byte[][] KEYS = {
2497     HConstants.EMPTY_BYTE_ARRAY, Bytes.toBytes("bbb"),
2498     Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"),
2499     Bytes.toBytes("fff"), Bytes.toBytes("ggg"), Bytes.toBytes("hhh"),
2500     Bytes.toBytes("iii"), Bytes.toBytes("jjj"), Bytes.toBytes("kkk"),
2501     Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"),
2502     Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"),
2503     Bytes.toBytes("rrr"), Bytes.toBytes("sss"), Bytes.toBytes("ttt"),
2504     Bytes.toBytes("uuu"), Bytes.toBytes("vvv"), Bytes.toBytes("www"),
2505     Bytes.toBytes("xxx"), Bytes.toBytes("yyy")
2506   };
2507 
2508   public static final byte[][] KEYS_FOR_HBA_CREATE_TABLE = {
2509       Bytes.toBytes("bbb"),
2510       Bytes.toBytes("ccc"), Bytes.toBytes("ddd"), Bytes.toBytes("eee"),
2511       Bytes.toBytes("fff"), Bytes.toBytes("ggg"), Bytes.toBytes("hhh"),
2512       Bytes.toBytes("iii"), Bytes.toBytes("jjj"), Bytes.toBytes("kkk"),
2513       Bytes.toBytes("lll"), Bytes.toBytes("mmm"), Bytes.toBytes("nnn"),
2514       Bytes.toBytes("ooo"), Bytes.toBytes("ppp"), Bytes.toBytes("qqq"),
2515       Bytes.toBytes("rrr"), Bytes.toBytes("sss"), Bytes.toBytes("ttt"),
2516       Bytes.toBytes("uuu"), Bytes.toBytes("vvv"), Bytes.toBytes("www"),
2517       Bytes.toBytes("xxx"), Bytes.toBytes("yyy"), Bytes.toBytes("zzz")
2518   };
2519 
2520   /**
2521    * Create rows in hbase:meta for regions of the specified table with the specified
2522    * start keys.  The first startKey should be a 0 length byte array if you
2523    * want to form a proper range of regions.
2524    * @param conf
2525    * @param htd
2526    * @param startKeys
2527    * @return list of region info for regions added to meta
2528    * @throws IOException
2529    */
2530   public List<HRegionInfo> createMultiRegionsInMeta(final Configuration conf,
2531       final HTableDescriptor htd, byte [][] startKeys)
2532   throws IOException {
2533     Table meta = new HTable(conf, TableName.META_TABLE_NAME);
2534     Arrays.sort(startKeys, Bytes.BYTES_COMPARATOR);
2535     List<HRegionInfo> newRegions = new ArrayList<HRegionInfo>(startKeys.length);
2536     // add custom ones
2537     for (int i = 0; i < startKeys.length; i++) {
2538       int j = (i + 1) % startKeys.length;
2539       HRegionInfo hri = new HRegionInfo(htd.getTableName(), startKeys[i],
2540           startKeys[j]);
2541       MetaTableAccessor.addRegionToMeta(meta, hri);
2542       newRegions.add(hri);
2543     }
2544 
2545     meta.close();
2546     return newRegions;
2547   }
2548 
2549   /**
2550    * Returns all rows from the hbase:meta table.
2551    *
2552    * @throws IOException When reading the rows fails.
2553    */
2554   public List<byte[]> getMetaTableRows() throws IOException {
2555     // TODO: Redo using MetaTableAccessor class
2556     Table t = new HTable(new Configuration(this.conf), TableName.META_TABLE_NAME);
2557     List<byte[]> rows = new ArrayList<byte[]>();
2558     ResultScanner s = t.getScanner(new Scan());
2559     for (Result result : s) {
2560       LOG.info("getMetaTableRows: row -> " +
2561         Bytes.toStringBinary(result.getRow()));
2562       rows.add(result.getRow());
2563     }
2564     s.close();
2565     t.close();
2566     return rows;
2567   }
2568 
2569   /**
2570    * Returns all rows from the hbase:meta table for a given user table
2571    *
2572    * @throws IOException When reading the rows fails.
2573    */
2574   public List<byte[]> getMetaTableRows(TableName tableName) throws IOException {
2575     // TODO: Redo using MetaTableAccessor.
2576     Table t = new HTable(new Configuration(this.conf), TableName.META_TABLE_NAME);
2577     List<byte[]> rows = new ArrayList<byte[]>();
2578     ResultScanner s = t.getScanner(new Scan());
2579     for (Result result : s) {
2580       HRegionInfo info = HRegionInfo.getHRegionInfo(result);
2581       if (info == null) {
2582         LOG.error("No region info for row " + Bytes.toString(result.getRow()));
2583         // TODO figure out what to do for this new hosed case.
2584         continue;
2585       }
2586 
2587       if (info.getTable().equals(tableName)) {
2588         LOG.info("getMetaTableRows: row -> " +
2589             Bytes.toStringBinary(result.getRow()) + info);
2590         rows.add(result.getRow());
2591       }
2592     }
2593     s.close();
2594     t.close();
2595     return rows;
2596   }
2597 
2598   /**
2599    * Tool to get the reference to the region server object that holds the
2600    * region of the specified user table.
2601    * It first searches for the meta rows that contain the region of the
2602    * specified table, then gets the index of that RS, and finally retrieves
2603    * the RS's reference.
2604    * @param tableName user table to lookup in hbase:meta
2605    * @return region server that holds it, null if the row doesn't exist
2606    * @throws IOException
2607    * @throws InterruptedException
2608    */
2609   public HRegionServer getRSForFirstRegionInTable(TableName tableName)
2610       throws IOException, InterruptedException {
2611     List<byte[]> metaRows = getMetaTableRows(tableName);
2612     if (metaRows == null || metaRows.isEmpty()) {
2613       return null;
2614     }
2615     LOG.debug("Found " + metaRows.size() + " rows for table " +
2616       tableName);
2617     byte [] firstrow = metaRows.get(0);
2618     LOG.debug("FirstRow=" + Bytes.toString(firstrow));
2619     long pause = getConfiguration().getLong(HConstants.HBASE_CLIENT_PAUSE,
2620       HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
2621     int numRetries = getConfiguration().getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
2622       HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
2623     RetryCounter retrier = new RetryCounter(numRetries+1, (int)pause, TimeUnit.MICROSECONDS);
2624     while(retrier.shouldRetry()) {
2625       int index = getMiniHBaseCluster().getServerWith(firstrow);
2626       if (index != -1) {
2627         return getMiniHBaseCluster().getRegionServerThreads().get(index).getRegionServer();
2628       }
2629       // Came back -1.  Region may not be online yet.  Sleep a while.
2630       retrier.sleepUntilNextRetry();
2631     }
2632     return null;
2633   }
2634 
2635   /**
2636    * Starts a <code>MiniMRCluster</code> with a default number of
2637    * <code>TaskTracker</code>'s.
2638    *
2639    * @throws IOException When starting the cluster fails.
2640    */
2641   public MiniMRCluster startMiniMapReduceCluster() throws IOException {
2642     startMiniMapReduceCluster(2);
2643     return mrCluster;
2644   }
2645 
2646   /**
2647    * Tasktracker has a bug where changing the hadoop.log.dir system property
2648    * will not change its internal static LOG_DIR variable.
2649    */
2650   private void forceChangeTaskLogDir() {
2651     Field logDirField;
2652     try {
2653       logDirField = TaskLog.class.getDeclaredField("LOG_DIR");
2654       logDirField.setAccessible(true);
2655 
2656       Field modifiersField = Field.class.getDeclaredField("modifiers");
2657       modifiersField.setAccessible(true);
2658       modifiersField.setInt(logDirField, logDirField.getModifiers() & ~Modifier.FINAL);
2659 
2660       logDirField.set(null, new File(hadoopLogDir, "userlogs"));
2661     } catch (SecurityException e) {
2662       throw new RuntimeException(e);
2663     } catch (NoSuchFieldException e) {
2664       // TODO Auto-generated catch block
2665       throw new RuntimeException(e);
2666     } catch (IllegalArgumentException e) {
2667       throw new RuntimeException(e);
2668     } catch (IllegalAccessException e) {
2669       throw new RuntimeException(e);
2670     }
2671   }
2672 
2673   /**
2674    * Starts a <code>MiniMRCluster</code>. Call {@link #setFileSystemURI(String)} to use a different
2675    * filesystem.
2676    * @param servers  The number of <code>TaskTracker</code>'s to start.
2677    * @throws IOException When starting the cluster fails.
2678    */
2679   private void startMiniMapReduceCluster(final int servers) throws IOException {
2680     if (mrCluster != null) {
2681       throw new IllegalStateException("MiniMRCluster is already running");
2682     }
2683     LOG.info("Starting mini mapreduce cluster...");
2684     setupClusterTestDir();
2685     createDirsAndSetProperties();
2686 
2687     forceChangeTaskLogDir();
2688 
2689     //// hadoop2 specific settings
2690     // Tests were failing because this process used 6GB of virtual memory and was getting killed.
2691     // we up the VM usable so that processes don't get killed.
2692     conf.setFloat("yarn.nodemanager.vmem-pmem-ratio", 8.0f);
2693 
2694     // Tests were failing due to MAPREDUCE-4880 / MAPREDUCE-4607 against hadoop 2.0.2-alpha and
2695     // this avoids the problem by disabling speculative task execution in tests.
2696     conf.setBoolean("mapreduce.map.speculative", false);
2697     conf.setBoolean("mapreduce.reduce.speculative", false);
2698     ////
2699 
2700     // Allow the user to override FS URI for this map-reduce cluster to use.
2701     mrCluster = new MiniMRCluster(servers,
2702       FS_URI != null ? FS_URI : FileSystem.get(conf).getUri().toString(), 1,
2703       null, null, new JobConf(this.conf));
2704     JobConf jobConf = MapreduceTestingShim.getJobConf(mrCluster);
2705     if (jobConf == null) {
2706       jobConf = mrCluster.createJobConf();
2707     }
2708 
2709     jobConf.set("mapreduce.cluster.local.dir",
2710       conf.get("mapreduce.cluster.local.dir")); //Hadoop MiniMR overwrites this while it should not
2711     LOG.info("Mini mapreduce cluster started");
2712 
2713     // In hadoop2, YARN/MR2 starts a mini cluster with its own conf instance and updates settings.
2714     // Our HBase MR jobs need several of these settings in order to properly run.  So we copy the
2715     // necessary config properties here.  YARN-129 required adding a few properties.
2716     conf.set("mapreduce.jobtracker.address", jobConf.get("mapreduce.jobtracker.address"));
2717     // this for mrv2 support; mr1 ignores this
2718     conf.set("mapreduce.framework.name", "yarn");
2719     conf.setBoolean("yarn.is.minicluster", true);
2720     String rmAddress = jobConf.get("yarn.resourcemanager.address");
2721     if (rmAddress != null) {
2722       conf.set("yarn.resourcemanager.address", rmAddress);
2723     }
2724     String historyAddress = jobConf.get("mapreduce.jobhistory.address");
2725     if (historyAddress != null) {
2726       conf.set("mapreduce.jobhistory.address", historyAddress);
2727     }
2728     String schedulerAddress =
2729       jobConf.get("yarn.resourcemanager.scheduler.address");
2730     if (schedulerAddress != null) {
2731       conf.set("yarn.resourcemanager.scheduler.address", schedulerAddress);
2732     }
2733     String mrJobHistoryWebappAddress =
2734       jobConf.get("mapreduce.jobhistory.webapp.address");
2735     if (mrJobHistoryWebappAddress != null) {
2736       conf.set("mapreduce.jobhistory.webapp.address", mrJobHistoryWebappAddress);
2737     }
2738     String yarnRMWebappAddress =
2739       jobConf.get("yarn.resourcemanager.webapp.address");
2740     if (yarnRMWebappAddress != null) {
2741       conf.set("yarn.resourcemanager.webapp.address", yarnRMWebappAddress);
2742     }
2743   }
2744 
2745   /**
2746    * Stops the previously started <code>MiniMRCluster</code>.
2747    */
2748   public void shutdownMiniMapReduceCluster() {
2749     if (mrCluster != null) {
2750       LOG.info("Stopping mini mapreduce cluster...");
2751       mrCluster.shutdown();
2752       mrCluster = null;
2753       LOG.info("Mini mapreduce cluster stopped");
2754     }
2755     // Restore configuration to point to local jobtracker
2756     conf.set("mapreduce.jobtracker.address", "local");
2757   }
2758 
2759   /**
2760    * Create a stubbed out RegionServerService, mainly for getting FS.
2761    */
2762   public RegionServerServices createMockRegionServerService() throws IOException {
2763     return createMockRegionServerService((ServerName)null);
2764   }
2765 
2766   /**
2767    * Create a stubbed out RegionServerService, mainly for getting FS.
2768    * This version is used by TestTokenAuthentication
2769    */
2770   public RegionServerServices createMockRegionServerService(RpcServerInterface rpc) throws IOException {
2771     final MockRegionServerServices rss = new MockRegionServerServices(getZooKeeperWatcher());
2772     rss.setFileSystem(getTestFileSystem());
2773     rss.setRpcServer(rpc);
2774     return rss;
2775   }
2776 
2777   /**
2778    * Create a stubbed out RegionServerService, mainly for getting FS.
2779    * This version is used by TestOpenRegionHandler
2780    */
2781   public RegionServerServices createMockRegionServerService(ServerName name) throws IOException {
2782     final MockRegionServerServices rss = new MockRegionServerServices(getZooKeeperWatcher(), name);
2783     rss.setFileSystem(getTestFileSystem());
2784     return rss;
2785   }
2786 
2787   /**
2788    * Switches the logger for the given class to DEBUG level.
2789    *
2790    * @param clazz  The class for which to switch to debug logging.
2791    */
2792   public void enableDebug(Class<?> clazz) {
2793     Log l = LogFactory.getLog(clazz);
2794     if (l instanceof Log4JLogger) {
2795       ((Log4JLogger) l).getLogger().setLevel(org.apache.log4j.Level.DEBUG);
2796     } else if (l instanceof Jdk14Logger) {
2797       ((Jdk14Logger) l).getLogger().setLevel(java.util.logging.Level.ALL);
2798     }
2799   }
2800 
2801   /**
2802    * Expire the Master's session
2803    * @throws Exception
2804    */
2805   public void expireMasterSession() throws Exception {
2806     HMaster master = getMiniHBaseCluster().getMaster();
2807     expireSession(master.getZooKeeper(), false);
2808   }
2809 
2810   /**
2811    * Expire a region server's session
2812    * @param index which RS
2813    * @throws Exception
2814    */
2815   public void expireRegionServerSession(int index) throws Exception {
2816     HRegionServer rs = getMiniHBaseCluster().getRegionServer(index);
2817     expireSession(rs.getZooKeeper(), false);
2818     decrementMinRegionServerCount();
2819   }
2820 
2821   private void decrementMinRegionServerCount() {
2822     // decrement the count for this.conf, for newly spwaned master
2823     // this.hbaseCluster shares this configuration too
2824     decrementMinRegionServerCount(getConfiguration());
2825 
2826     // each master thread keeps a copy of configuration
2827     for (MasterThread master : getHBaseCluster().getMasterThreads()) {
2828       decrementMinRegionServerCount(master.getMaster().getConfiguration());
2829     }
2830   }
2831 
2832   private void decrementMinRegionServerCount(Configuration conf) {
2833     int currentCount = conf.getInt(
2834         ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART, -1);
2835     if (currentCount != -1) {
2836       conf.setInt(ServerManager.WAIT_ON_REGIONSERVERS_MINTOSTART,
2837           Math.max(currentCount - 1, 1));
2838     }
2839   }
2840 
2841   public void expireSession(ZooKeeperWatcher nodeZK) throws Exception {
2842    expireSession(nodeZK, false);
2843   }
2844 
2845   @Deprecated
2846   public void expireSession(ZooKeeperWatcher nodeZK, Server server)
2847     throws Exception {
2848     expireSession(nodeZK, false);
2849   }
2850 
2851   /**
2852    * Expire a ZooKeeper session as recommended in ZooKeeper documentation
2853    * http://wiki.apache.org/hadoop/ZooKeeper/FAQ#A4
2854    * There are issues when doing this:
2855    * [1] http://www.mail-archive.com/dev@zookeeper.apache.org/msg01942.html
2856    * [2] https://issues.apache.org/jira/browse/ZOOKEEPER-1105
2857    *
2858    * @param nodeZK - the ZK watcher to expire
2859    * @param checkStatus - true to check if we can create an HTable with the
2860    *                    current configuration.
2861    */
2862   public void expireSession(ZooKeeperWatcher nodeZK, boolean checkStatus)
2863     throws Exception {
2864     Configuration c = new Configuration(this.conf);
2865     String quorumServers = ZKConfig.getZKQuorumServersString(c);
2866     ZooKeeper zk = nodeZK.getRecoverableZooKeeper().getZooKeeper();
2867     byte[] password = zk.getSessionPasswd();
2868     long sessionID = zk.getSessionId();
2869 
2870     // Expiry seems to be asynchronous (see comment from P. Hunt in [1]),
2871     //  so we create a first watcher to be sure that the
2872     //  event was sent. We expect that if our watcher receives the event
2873     //  other watchers on the same machine will get is as well.
2874     // When we ask to close the connection, ZK does not close it before
2875     //  we receive all the events, so don't have to capture the event, just
2876     //  closing the connection should be enough.
2877     ZooKeeper monitor = new ZooKeeper(quorumServers,
2878       1000, new org.apache.zookeeper.Watcher(){
2879       @Override
2880       public void process(WatchedEvent watchedEvent) {
2881         LOG.info("Monitor ZKW received event="+watchedEvent);
2882       }
2883     } , sessionID, password);
2884 
2885     // Making it expire
2886     ZooKeeper newZK = new ZooKeeper(quorumServers,
2887         1000, EmptyWatcher.instance, sessionID, password);
2888 
2889     //ensure that we have connection to the server before closing down, otherwise
2890     //the close session event will be eaten out before we start CONNECTING state
2891     long start = System.currentTimeMillis();
2892     while (newZK.getState() != States.CONNECTED
2893          && System.currentTimeMillis() - start < 1000) {
2894        Thread.sleep(1);
2895     }
2896     newZK.close();
2897     LOG.info("ZK Closed Session 0x" + Long.toHexString(sessionID));
2898 
2899     // Now closing & waiting to be sure that the clients get it.
2900     monitor.close();
2901 
2902     if (checkStatus) {
2903       new HTable(new Configuration(conf), TableName.META_TABLE_NAME).close();
2904     }
2905   }
2906 
2907   /**
2908    * Get the Mini HBase cluster.
2909    *
2910    * @return hbase cluster
2911    * @see #getHBaseClusterInterface()
2912    */
2913   public MiniHBaseCluster getHBaseCluster() {
2914     return getMiniHBaseCluster();
2915   }
2916 
2917   /**
2918    * Returns the HBaseCluster instance.
2919    * <p>Returned object can be any of the subclasses of HBaseCluster, and the
2920    * tests referring this should not assume that the cluster is a mini cluster or a
2921    * distributed one. If the test only works on a mini cluster, then specific
2922    * method {@link #getMiniHBaseCluster()} can be used instead w/o the
2923    * need to type-cast.
2924    */
2925   public HBaseCluster getHBaseClusterInterface() {
2926     //implementation note: we should rename this method as #getHBaseCluster(),
2927     //but this would require refactoring 90+ calls.
2928     return hbaseCluster;
2929   }
2930 
2931   /**
2932    * Get a Connection to the cluster.
2933    * Not thread-safe (This class needs a lot of work to make it thread-safe).
2934    * @return A Connection that can be shared. Don't close. Will be closed on shutdown of cluster.
2935    * @throws IOException
2936    */
2937   public Connection getConnection() throws IOException {
2938     if (this.connection == null) {
2939       this.connection = ConnectionFactory.createConnection(this.conf);
2940     }
2941     return this.connection;
2942   }
2943 
2944   /**
2945    * Returns a Admin instance.
2946    * This instance is shared between HBaseTestingUtility instance users.
2947    * Closing it has no effect, it will be closed automatically when the
2948    * cluster shutdowns
2949    *
2950    * @return An Admin instance.
2951    * @throws IOException
2952    */
2953   public synchronized HBaseAdmin getHBaseAdmin()
2954   throws IOException {
2955     if (hbaseAdmin == null){
2956       this.hbaseAdmin = new HBaseAdminForTests(getConnection());
2957     }
2958     return hbaseAdmin;
2959   }
2960 
2961   private HBaseAdminForTests hbaseAdmin = null;
2962   private static class HBaseAdminForTests extends HBaseAdmin {
2963     public HBaseAdminForTests(Connection connection) throws MasterNotRunningException,
2964         ZooKeeperConnectionException, IOException {
2965       super(connection);
2966     }
2967 
2968     @Override
2969     public synchronized void close() throws IOException {
2970       LOG.warn("close() called on HBaseAdmin instance returned from " +
2971         "HBaseTestingUtility.getHBaseAdmin()");
2972     }
2973 
2974     private synchronized void close0() throws IOException {
2975       super.close();
2976     }
2977   }
2978 
2979   /**
2980    * Returns a ZooKeeperWatcher instance.
2981    * This instance is shared between HBaseTestingUtility instance users.
2982    * Don't close it, it will be closed automatically when the
2983    * cluster shutdowns
2984    *
2985    * @return The ZooKeeperWatcher instance.
2986    * @throws IOException
2987    */
2988   public synchronized ZooKeeperWatcher getZooKeeperWatcher()
2989     throws IOException {
2990     if (zooKeeperWatcher == null) {
2991       zooKeeperWatcher = new ZooKeeperWatcher(conf, "testing utility",
2992         new Abortable() {
2993         @Override public void abort(String why, Throwable e) {
2994           throw new RuntimeException("Unexpected abort in HBaseTestingUtility:"+why, e);
2995         }
2996         @Override public boolean isAborted() {return false;}
2997       });
2998     }
2999     return zooKeeperWatcher;
3000   }
3001   private ZooKeeperWatcher zooKeeperWatcher;
3002 
3003 
3004 
3005   /**
3006    * Closes the named region.
3007    *
3008    * @param regionName  The region to close.
3009    * @throws IOException
3010    */
3011   public void closeRegion(String regionName) throws IOException {
3012     closeRegion(Bytes.toBytes(regionName));
3013   }
3014 
3015   /**
3016    * Closes the named region.
3017    *
3018    * @param regionName  The region to close.
3019    * @throws IOException
3020    */
3021   public void closeRegion(byte[] regionName) throws IOException {
3022     getHBaseAdmin().closeRegion(regionName, null);
3023   }
3024 
3025   /**
3026    * Closes the region containing the given row.
3027    *
3028    * @param row  The row to find the containing region.
3029    * @param table  The table to find the region.
3030    * @throws IOException
3031    */
3032   public void closeRegionByRow(String row, RegionLocator table) throws IOException {
3033     closeRegionByRow(Bytes.toBytes(row), table);
3034   }
3035 
3036   /**
3037    * Closes the region containing the given row.
3038    *
3039    * @param row  The row to find the containing region.
3040    * @param table  The table to find the region.
3041    * @throws IOException
3042    */
3043   public void closeRegionByRow(byte[] row, RegionLocator table) throws IOException {
3044     HRegionLocation hrl = table.getRegionLocation(row);
3045     closeRegion(hrl.getRegionInfo().getRegionName());
3046   }
3047 
3048   /*
3049    * Retrieves a splittable region randomly from tableName
3050    *
3051    * @param tableName name of table
3052    * @param maxAttempts maximum number of attempts, unlimited for value of -1
3053    * @return the HRegion chosen, null if none was found within limit of maxAttempts
3054    */
3055   public HRegion getSplittableRegion(TableName tableName, int maxAttempts) {
3056     List<HRegion> regions = getHBaseCluster().getRegions(tableName);
3057     int regCount = regions.size();
3058     Set<Integer> attempted = new HashSet<Integer>();
3059     int idx;
3060     int attempts = 0;
3061     do {
3062       regions = getHBaseCluster().getRegions(tableName);
3063       if (regCount != regions.size()) {
3064         // if there was region movement, clear attempted Set
3065         attempted.clear();
3066       }
3067       regCount = regions.size();
3068       // There are chances that before we get the region for the table from an RS the region may
3069       // be going for CLOSE.  This may be because online schema change is enabled
3070       if (regCount > 0) {
3071         idx = random.nextInt(regCount);
3072         // if we have just tried this region, there is no need to try again
3073         if (attempted.contains(idx))
3074           continue;
3075         try {
3076           regions.get(idx).checkSplit();
3077           return regions.get(idx);
3078         } catch (Exception ex) {
3079           LOG.warn("Caught exception", ex);
3080           attempted.add(idx);
3081         }
3082       }
3083       attempts++;
3084     } while (maxAttempts == -1 || attempts < maxAttempts);
3085     return null;
3086   }
3087 
3088   public MiniZooKeeperCluster getZkCluster() {
3089     return zkCluster;
3090   }
3091 
3092   public void setZkCluster(MiniZooKeeperCluster zkCluster) {
3093     this.passedZkCluster = true;
3094     this.zkCluster = zkCluster;
3095     conf.setInt(HConstants.ZOOKEEPER_CLIENT_PORT, zkCluster.getClientPort());
3096   }
3097 
3098   public MiniDFSCluster getDFSCluster() {
3099     return dfsCluster;
3100   }
3101 
3102   public void setDFSCluster(MiniDFSCluster cluster) throws IllegalStateException, IOException {
3103     setDFSCluster(cluster, true);
3104   }
3105 
3106   /**
3107    * Set the MiniDFSCluster
3108    * @param cluster cluster to use
3109    * @param requireDown require the that cluster not be "up" (MiniDFSCluster#isClusterUp) before
3110    * it is set.
3111    * @throws IllegalStateException if the passed cluster is up when it is required to be down
3112    * @throws IOException if the FileSystem could not be set from the passed dfs cluster
3113    */
3114   public void setDFSCluster(MiniDFSCluster cluster, boolean requireDown)
3115       throws IllegalStateException, IOException {
3116     if (dfsCluster != null && requireDown && dfsCluster.isClusterUp()) {
3117       throw new IllegalStateException("DFSCluster is already running! Shut it down first.");
3118     }
3119     this.dfsCluster = cluster;
3120     this.setFs();
3121   }
3122 
3123   public FileSystem getTestFileSystem() throws IOException {
3124     return HFileSystem.get(conf);
3125   }
3126 
3127   /**
3128    * Wait until all regions in a table have been assigned.  Waits default timeout before giving up
3129    * (30 seconds).
3130    * @param table Table to wait on.
3131    * @throws InterruptedException
3132    * @throws IOException
3133    */
3134   public void waitTableAvailable(TableName table)
3135       throws InterruptedException, IOException {
3136     waitTableAvailable(table.getName(), 30000);
3137   }
3138 
3139   public void waitTableAvailable(TableName table, long timeoutMillis)
3140       throws InterruptedException, IOException {
3141     waitFor(timeoutMillis, predicateTableAvailable(table));
3142   }
3143 
3144   public String explainTableAvailability(TableName tableName) throws IOException {
3145     String msg = explainTableState(tableName) + ",";
3146     if (getHBaseCluster().getMaster().isAlive()) {
3147       Map<HRegionInfo, ServerName> assignments =
3148           getHBaseCluster().getMaster().getAssignmentManager().getRegionStates()
3149               .getRegionAssignments();
3150       final List<Pair<HRegionInfo, ServerName>> metaLocations =
3151           MetaTableAccessor
3152               .getTableRegionsAndLocations(getZooKeeperWatcher(), connection, tableName);
3153       for (Pair<HRegionInfo, ServerName> metaLocation : metaLocations) {
3154         HRegionInfo hri = metaLocation.getFirst();
3155         ServerName sn = metaLocation.getSecond();
3156         if (!assignments.containsKey(hri)) {
3157           msg += ", region " + hri
3158               + " not assigned, but found in meta, it expected to be on " + sn;
3159 
3160         } else if (sn == null) {
3161           msg += ",  region " + hri
3162               + " assigned,  but has no server in meta";
3163         } else if (!sn.equals(assignments.get(hri))) {
3164           msg += ",  region " + hri
3165               + " assigned,  but has different servers in meta and AM ( " +
3166               sn + " <> " + assignments.get(hri);
3167         }
3168       }
3169     }
3170     return msg;
3171   }
3172 
3173   public String explainTableState(TableName tableName) throws IOException {
3174     try {
3175       if (getHBaseAdmin().isTableEnabled(tableName))
3176         return "table enabled in zk";
3177       else if (getHBaseAdmin().isTableDisabled(tableName))
3178         return "table disabled in zk";
3179       else
3180         return "table in uknown state";
3181     } catch (TableNotFoundException e) {
3182       return "table not exists";
3183     }
3184   }
3185 
3186   /**
3187    * Wait until all regions in a table have been assigned
3188    * @param table Table to wait on.
3189    * @param timeoutMillis Timeout.
3190    * @throws InterruptedException
3191    * @throws IOException
3192    */
3193   public void waitTableAvailable(byte[] table, long timeoutMillis)
3194   throws InterruptedException, IOException {
3195     waitTableAvailable(getHBaseAdmin(), table, timeoutMillis);
3196   }
3197 
3198   public void waitTableAvailable(Admin admin, byte[] table, long timeoutMillis)
3199   throws InterruptedException, IOException {
3200     long startWait = System.currentTimeMillis();
3201     while (!admin.isTableAvailable(TableName.valueOf(table))) {
3202       assertTrue("Timed out waiting for table to become available " +
3203         Bytes.toStringBinary(table),
3204         System.currentTimeMillis() - startWait < timeoutMillis);
3205       Thread.sleep(200);
3206     }
3207   }
3208 
3209   /**
3210    * Waits for a table to be 'enabled'.  Enabled means that table is set as 'enabled' and the
3211    * regions have been all assigned.  Will timeout after default period (30 seconds)
3212    * @see #waitTableAvailable(byte[])
3213    * @param table Table to wait on.
3214    * @param table
3215    * @throws InterruptedException
3216    * @throws IOException
3217    */
3218   public void waitTableEnabled(TableName table)
3219       throws InterruptedException, IOException {
3220     waitTableEnabled(table, 30000);
3221   }
3222 
3223   /**
3224    * Waits for a table to be 'enabled'.  Enabled means that table is set as 'enabled' and the
3225    * regions have been all assigned.
3226    * @see #waitTableAvailable(byte[])
3227    * @param table Table to wait on.
3228    * @param timeoutMillis Time to wait on it being marked enabled.
3229    * @throws InterruptedException
3230    * @throws IOException
3231    */
3232   public void waitTableEnabled(byte[] table, long timeoutMillis)
3233   throws InterruptedException, IOException {
3234     waitTableEnabled(TableName.valueOf(table), timeoutMillis);
3235   }
3236 
3237   public void waitTableEnabled(TableName table, long timeoutMillis)
3238       throws IOException {
3239     waitFor(timeoutMillis, predicateTableEnabled(table));
3240   }
3241 
3242   /**
3243    * Waits for a table to be 'disabled'.  Disabled means that table is set as 'disabled'
3244    * Will timeout after default period (30 seconds)
3245    * @param table Table to wait on.
3246    * @throws InterruptedException
3247    * @throws IOException
3248    */
3249   public void waitTableDisabled(byte[] table)
3250       throws InterruptedException, IOException {
3251     waitTableDisabled(getHBaseAdmin(), table, 30000);
3252   }
3253 
3254   public void waitTableDisabled(Admin admin, byte[] table)
3255       throws InterruptedException, IOException {
3256     waitTableDisabled(admin, table, 30000);
3257   }
3258 
3259   /**
3260    * Waits for a table to be 'disabled'.  Disabled means that table is set as 'disabled'
3261    * @param table Table to wait on.
3262    * @param timeoutMillis Time to wait on it being marked disabled.
3263    * @throws InterruptedException
3264    * @throws IOException
3265    */
3266   public void waitTableDisabled(byte[] table, long timeoutMillis)
3267       throws InterruptedException, IOException {
3268     waitTableDisabled(getHBaseAdmin(), table, timeoutMillis);
3269   }
3270 
3271   public void waitTableDisabled(Admin admin, byte[] table, long timeoutMillis)
3272       throws InterruptedException, IOException {
3273     TableName tableName = TableName.valueOf(table);
3274     long startWait = System.currentTimeMillis();
3275     while (!admin.isTableDisabled(tableName)) {
3276       assertTrue("Timed out waiting for table to become disabled " +
3277               Bytes.toStringBinary(table),
3278           System.currentTimeMillis() - startWait < timeoutMillis);
3279       Thread.sleep(200);
3280     }
3281   }
3282 
3283   /**
3284    * Make sure that at least the specified number of region servers
3285    * are running
3286    * @param num minimum number of region servers that should be running
3287    * @return true if we started some servers
3288    * @throws IOException
3289    */
3290   public boolean ensureSomeRegionServersAvailable(final int num)
3291       throws IOException {
3292     boolean startedServer = false;
3293     MiniHBaseCluster hbaseCluster = getMiniHBaseCluster();
3294     for (int i=hbaseCluster.getLiveRegionServerThreads().size(); i<num; ++i) {
3295       LOG.info("Started new server=" + hbaseCluster.startRegionServer());
3296       startedServer = true;
3297     }
3298 
3299     return startedServer;
3300   }
3301 
3302 
3303   /**
3304    * Make sure that at least the specified number of region servers
3305    * are running. We don't count the ones that are currently stopping or are
3306    * stopped.
3307    * @param num minimum number of region servers that should be running
3308    * @return true if we started some servers
3309    * @throws IOException
3310    */
3311   public boolean ensureSomeNonStoppedRegionServersAvailable(final int num)
3312     throws IOException {
3313     boolean startedServer = ensureSomeRegionServersAvailable(num);
3314 
3315     int nonStoppedServers = 0;
3316     for (JVMClusterUtil.RegionServerThread rst :
3317       getMiniHBaseCluster().getRegionServerThreads()) {
3318 
3319       HRegionServer hrs = rst.getRegionServer();
3320       if (hrs.isStopping() || hrs.isStopped()) {
3321         LOG.info("A region server is stopped or stopping:"+hrs);
3322       } else {
3323         nonStoppedServers++;
3324       }
3325     }
3326     for (int i=nonStoppedServers; i<num; ++i) {
3327       LOG.info("Started new server=" + getMiniHBaseCluster().startRegionServer());
3328       startedServer = true;
3329     }
3330     return startedServer;
3331   }
3332 
3333 
3334   /**
3335    * This method clones the passed <code>c</code> configuration setting a new
3336    * user into the clone.  Use it getting new instances of FileSystem.  Only
3337    * works for DistributedFileSystem w/o Kerberos.
3338    * @param c Initial configuration
3339    * @param differentiatingSuffix Suffix to differentiate this user from others.
3340    * @return A new configuration instance with a different user set into it.
3341    * @throws IOException
3342    */
3343   public static User getDifferentUser(final Configuration c,
3344     final String differentiatingSuffix)
3345   throws IOException {
3346     FileSystem currentfs = FileSystem.get(c);
3347     if (!(currentfs instanceof DistributedFileSystem) || User.isHBaseSecurityEnabled(c)) {
3348       return User.getCurrent();
3349     }
3350     // Else distributed filesystem.  Make a new instance per daemon.  Below
3351     // code is taken from the AppendTestUtil over in hdfs.
3352     String username = User.getCurrent().getName() +
3353       differentiatingSuffix;
3354     User user = User.createUserForTesting(c, username,
3355         new String[]{"supergroup"});
3356     return user;
3357   }
3358 
3359   public static NavigableSet<String> getAllOnlineRegions(MiniHBaseCluster cluster)
3360       throws IOException {
3361     NavigableSet<String> online = new TreeSet<String>();
3362     for (RegionServerThread rst : cluster.getLiveRegionServerThreads()) {
3363       try {
3364         for (HRegionInfo region :
3365             ProtobufUtil.getOnlineRegions(rst.getRegionServer().getRSRpcServices())) {
3366           online.add(region.getRegionNameAsString());
3367         }
3368       } catch (RegionServerStoppedException e) {
3369         // That's fine.
3370       }
3371     }
3372     for (MasterThread mt : cluster.getLiveMasterThreads()) {
3373       try {
3374         for (HRegionInfo region :
3375             ProtobufUtil.getOnlineRegions(mt.getMaster().getRSRpcServices())) {
3376           online.add(region.getRegionNameAsString());
3377         }
3378       } catch (RegionServerStoppedException e) {
3379         // That's fine.
3380       } catch (ServerNotRunningYetException e) {
3381         // That's fine.
3382       }
3383     }
3384     return online;
3385   }
3386 
3387   /**
3388    * Set maxRecoveryErrorCount in DFSClient.  In 0.20 pre-append its hard-coded to 5 and
3389    * makes tests linger.  Here is the exception you'll see:
3390    * <pre>
3391    * 2010-06-15 11:52:28,511 WARN  [DataStreamer for file /hbase/.logs/wal.1276627923013 block blk_928005470262850423_1021] hdfs.DFSClient$DFSOutputStream(2657): Error Recovery for block blk_928005470262850423_1021 failed  because recovery from primary datanode 127.0.0.1:53683 failed 4 times.  Pipeline was 127.0.0.1:53687, 127.0.0.1:53683. Will retry...
3392    * </pre>
3393    * @param stream A DFSClient.DFSOutputStream.
3394    * @param max
3395    * @throws NoSuchFieldException
3396    * @throws SecurityException
3397    * @throws IllegalAccessException
3398    * @throws IllegalArgumentException
3399    */
3400   public static void setMaxRecoveryErrorCount(final OutputStream stream,
3401       final int max) {
3402     try {
3403       Class<?> [] clazzes = DFSClient.class.getDeclaredClasses();
3404       for (Class<?> clazz: clazzes) {
3405         String className = clazz.getSimpleName();
3406         if (className.equals("DFSOutputStream")) {
3407           if (clazz.isInstance(stream)) {
3408             Field maxRecoveryErrorCountField =
3409               stream.getClass().getDeclaredField("maxRecoveryErrorCount");
3410             maxRecoveryErrorCountField.setAccessible(true);
3411             maxRecoveryErrorCountField.setInt(stream, max);
3412             break;
3413           }
3414         }
3415       }
3416     } catch (Exception e) {
3417       LOG.info("Could not set max recovery field", e);
3418     }
3419   }
3420 
3421   /**
3422    * Wait until all regions for a table in hbase:meta have a non-empty
3423    * info:server, up to a configuable timeout value (default is 60 seconds)
3424    * This means all regions have been deployed,
3425    * master has been informed and updated hbase:meta with the regions deployed
3426    * server.
3427    * @param tableName the table name
3428    * @throws IOException
3429    */
3430   public void waitUntilAllRegionsAssigned(final TableName tableName) throws IOException {
3431     waitUntilAllRegionsAssigned(
3432       tableName,
3433       this.conf.getLong("hbase.client.sync.wait.timeout.msec", 60000));
3434   }
3435 
3436   /**
3437    * Wait until all regions for a table in hbase:meta have a non-empty
3438    * info:server, or until timeout.  This means all regions have been deployed,
3439    * master has been informed and updated hbase:meta with the regions deployed
3440    * server.
3441    * @param tableName the table name
3442    * @param timeout timeout, in milliseconds
3443    * @throws IOException
3444    */
3445   public void waitUntilAllRegionsAssigned(final TableName tableName, final long timeout)
3446       throws IOException {
3447     final Table meta = new HTable(getConfiguration(), TableName.META_TABLE_NAME);
3448     try {
3449       waitFor(timeout, 200, true, new Predicate<IOException>() {
3450         @Override
3451         public boolean evaluate() throws IOException {
3452           boolean allRegionsAssigned = true;
3453           Scan scan = new Scan();
3454           scan.addFamily(HConstants.CATALOG_FAMILY);
3455           ResultScanner s = meta.getScanner(scan);
3456           try {
3457             Result r;
3458             while ((r = s.next()) != null) {
3459               byte[] b = r.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
3460               HRegionInfo info = HRegionInfo.parseFromOrNull(b);
3461               if (info != null && info.getTable().equals(tableName)) {
3462                 b = r.getValue(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER);
3463                 allRegionsAssigned &= (b != null);
3464               }
3465             }
3466           } finally {
3467             s.close();
3468           }
3469           return allRegionsAssigned;
3470         }
3471       });
3472     } finally {
3473       meta.close();
3474     }
3475 
3476     // check from the master state if we are using a mini cluster
3477     if (!getHBaseClusterInterface().isDistributedCluster()) {
3478       // So, all regions are in the meta table but make sure master knows of the assignments before
3479       // returing -- sometimes this can lag.
3480       HMaster master = getHBaseCluster().getMaster();
3481       final RegionStates states = master.getAssignmentManager().getRegionStates();
3482       waitFor(timeout, 200, new ExplainingPredicate<IOException>() {
3483         @Override
3484         public String explainFailure() throws IOException {
3485           return explainTableAvailability(tableName);
3486         }
3487 
3488         @Override
3489         public boolean evaluate() throws IOException {
3490           List<HRegionInfo> hris = states.getRegionsOfTable(tableName);
3491           return hris != null && !hris.isEmpty();
3492         }
3493       });
3494     }
3495   }
3496 
3497   /**
3498    * Do a small get/scan against one store. This is required because store
3499    * has no actual methods of querying itself, and relies on StoreScanner.
3500    */
3501   public static List<Cell> getFromStoreFile(HStore store,
3502                                                 Get get) throws IOException {
3503     Scan scan = new Scan(get);
3504     InternalScanner scanner = (InternalScanner) store.getScanner(scan,
3505         scan.getFamilyMap().get(store.getFamily().getName()),
3506         // originally MultiVersionConcurrencyControl.resetThreadReadPoint() was called to set
3507         // readpoint 0.
3508         0);
3509 
3510     List<Cell> result = new ArrayList<Cell>();
3511     scanner.next(result);
3512     if (!result.isEmpty()) {
3513       // verify that we are on the row we want:
3514       Cell kv = result.get(0);
3515       if (!CellUtil.matchingRow(kv, get.getRow())) {
3516         result.clear();
3517       }
3518     }
3519     scanner.close();
3520     return result;
3521   }
3522 
3523   /**
3524    * Create region split keys between startkey and endKey
3525    *
3526    * @param startKey
3527    * @param endKey
3528    * @param numRegions the number of regions to be created. it has to be greater than 3.
3529    * @return
3530    */
3531   public byte[][] getRegionSplitStartKeys(byte[] startKey, byte[] endKey, int numRegions){
3532     assertTrue(numRegions>3);
3533     byte [][] tmpSplitKeys = Bytes.split(startKey, endKey, numRegions - 3);
3534     byte [][] result = new byte[tmpSplitKeys.length+1][];
3535     System.arraycopy(tmpSplitKeys, 0, result, 1, tmpSplitKeys.length);
3536     result[0] = HConstants.EMPTY_BYTE_ARRAY;
3537     return result;
3538   }
3539 
3540   /**
3541    * Do a small get/scan against one store. This is required because store
3542    * has no actual methods of querying itself, and relies on StoreScanner.
3543    */
3544   public static List<Cell> getFromStoreFile(HStore store,
3545                                                 byte [] row,
3546                                                 NavigableSet<byte[]> columns
3547                                                 ) throws IOException {
3548     Get get = new Get(row);
3549     Map<byte[], NavigableSet<byte[]>> s = get.getFamilyMap();
3550     s.put(store.getFamily().getName(), columns);
3551 
3552     return getFromStoreFile(store,get);
3553   }
3554 
3555   /**
3556    * Gets a ZooKeeperWatcher.
3557    * @param TEST_UTIL
3558    */
3559   public static ZooKeeperWatcher getZooKeeperWatcher(
3560       HBaseTestingUtility TEST_UTIL) throws ZooKeeperConnectionException,
3561       IOException {
3562     ZooKeeperWatcher zkw = new ZooKeeperWatcher(TEST_UTIL.getConfiguration(),
3563         "unittest", new Abortable() {
3564           boolean aborted = false;
3565 
3566           @Override
3567           public void abort(String why, Throwable e) {
3568             aborted = true;
3569             throw new RuntimeException("Fatal ZK error, why=" + why, e);
3570           }
3571 
3572           @Override
3573           public boolean isAborted() {
3574             return aborted;
3575           }
3576         });
3577     return zkw;
3578   }
3579 
3580   /**
3581    * Creates a znode with OPENED state.
3582    * @param TEST_UTIL
3583    * @param region
3584    * @param serverName
3585    * @return
3586    * @throws IOException
3587    * @throws org.apache.hadoop.hbase.ZooKeeperConnectionException
3588    * @throws KeeperException
3589    * @throws NodeExistsException
3590    */
3591   public static ZooKeeperWatcher createAndForceNodeToOpenedState(
3592       HBaseTestingUtility TEST_UTIL, Region region,
3593       ServerName serverName) throws ZooKeeperConnectionException,
3594       IOException, KeeperException, NodeExistsException {
3595     return createAndForceNodeToOpenedState(TEST_UTIL, (HRegion)region, serverName);
3596   }
3597 
3598   /**
3599    * Creates a znode with OPENED state.
3600    * @param TEST_UTIL
3601    * @param region
3602    * @param serverName
3603    * @return
3604    * @throws IOException
3605    * @throws org.apache.hadoop.hbase.ZooKeeperConnectionException
3606    * @throws KeeperException
3607    * @throws NodeExistsException
3608    */
3609   public static ZooKeeperWatcher createAndForceNodeToOpenedState(
3610       HBaseTestingUtility TEST_UTIL, HRegion region,
3611       ServerName serverName) throws ZooKeeperConnectionException,
3612       IOException, KeeperException, NodeExistsException {
3613     ZooKeeperWatcher zkw = getZooKeeperWatcher(TEST_UTIL);
3614     ZKAssign.createNodeOffline(zkw, region.getRegionInfo(), serverName);
3615     int version = ZKAssign.transitionNodeOpening(zkw, region
3616         .getRegionInfo(), serverName);
3617     ZKAssign.transitionNodeOpened(zkw, region.getRegionInfo(), serverName,
3618         version);
3619     return zkw;
3620   }
3621 
3622   public static void assertKVListsEqual(String additionalMsg,
3623       final List<? extends Cell> expected,
3624       final List<? extends Cell> actual) {
3625     final int eLen = expected.size();
3626     final int aLen = actual.size();
3627     final int minLen = Math.min(eLen, aLen);
3628 
3629     int i;
3630     for (i = 0; i < minLen
3631         && KeyValue.COMPARATOR.compare(expected.get(i), actual.get(i)) == 0;
3632         ++i) {}
3633 
3634     if (additionalMsg == null) {
3635       additionalMsg = "";
3636     }
3637     if (!additionalMsg.isEmpty()) {
3638       additionalMsg = ". " + additionalMsg;
3639     }
3640 
3641     if (eLen != aLen || i != minLen) {
3642       throw new AssertionError(
3643           "Expected and actual KV arrays differ at position " + i + ": " +
3644           safeGetAsStr(expected, i) + " (length " + eLen +") vs. " +
3645           safeGetAsStr(actual, i) + " (length " + aLen + ")" + additionalMsg);
3646     }
3647   }
3648 
3649   public static <T> String safeGetAsStr(List<T> lst, int i) {
3650     if (0 <= i && i < lst.size()) {
3651       return lst.get(i).toString();
3652     } else {
3653       return "<out_of_range>";
3654     }
3655   }
3656 
3657   public String getClusterKey() {
3658     return conf.get(HConstants.ZOOKEEPER_QUORUM) + ":"
3659         + conf.get(HConstants.ZOOKEEPER_CLIENT_PORT) + ":"
3660         + conf.get(HConstants.ZOOKEEPER_ZNODE_PARENT,
3661             HConstants.DEFAULT_ZOOKEEPER_ZNODE_PARENT);
3662   }
3663 
3664   /** Creates a random table with the given parameters */
3665   public HTable createRandomTable(String tableName,
3666       final Collection<String> families,
3667       final int maxVersions,
3668       final int numColsPerRow,
3669       final int numFlushes,
3670       final int numRegions,
3671       final int numRowsPerFlush)
3672       throws IOException, InterruptedException {
3673 
3674     LOG.info("\n\nCreating random table " + tableName + " with " + numRegions +
3675         " regions, " + numFlushes + " storefiles per region, " +
3676         numRowsPerFlush + " rows per flush, maxVersions=" +  maxVersions +
3677         "\n");
3678 
3679     final Random rand = new Random(tableName.hashCode() * 17L + 12938197137L);
3680     final int numCF = families.size();
3681     final byte[][] cfBytes = new byte[numCF][];
3682     {
3683       int cfIndex = 0;
3684       for (String cf : families) {
3685         cfBytes[cfIndex++] = Bytes.toBytes(cf);
3686       }
3687     }
3688 
3689     final int actualStartKey = 0;
3690     final int actualEndKey = Integer.MAX_VALUE;
3691     final int keysPerRegion = (actualEndKey - actualStartKey) / numRegions;
3692     final int splitStartKey = actualStartKey + keysPerRegion;
3693     final int splitEndKey = actualEndKey - keysPerRegion;
3694     final String keyFormat = "%08x";
3695     final HTable table = createTable(tableName, cfBytes,
3696         maxVersions,
3697         Bytes.toBytes(String.format(keyFormat, splitStartKey)),
3698         Bytes.toBytes(String.format(keyFormat, splitEndKey)),
3699         numRegions);
3700 
3701     if (hbaseCluster != null) {
3702       getMiniHBaseCluster().flushcache(TableName.META_TABLE_NAME);
3703     }
3704 
3705     for (int iFlush = 0; iFlush < numFlushes; ++iFlush) {
3706       for (int iRow = 0; iRow < numRowsPerFlush; ++iRow) {
3707         final byte[] row = Bytes.toBytes(String.format(keyFormat,
3708             actualStartKey + rand.nextInt(actualEndKey - actualStartKey)));
3709 
3710         Put put = new Put(row);
3711         Delete del = new Delete(row);
3712         for (int iCol = 0; iCol < numColsPerRow; ++iCol) {
3713           final byte[] cf = cfBytes[rand.nextInt(numCF)];
3714           final long ts = rand.nextInt();
3715           final byte[] qual = Bytes.toBytes("col" + iCol);
3716           if (rand.nextBoolean()) {
3717             final byte[] value = Bytes.toBytes("value_for_row_" + iRow +
3718                 "_cf_" + Bytes.toStringBinary(cf) + "_col_" + iCol + "_ts_" +
3719                 ts + "_random_" + rand.nextLong());
3720             put.add(cf, qual, ts, value);
3721           } else if (rand.nextDouble() < 0.8) {
3722             del.deleteColumn(cf, qual, ts);
3723           } else {
3724             del.deleteColumns(cf, qual, ts);
3725           }
3726         }
3727 
3728         if (!put.isEmpty()) {
3729           table.put(put);
3730         }
3731 
3732         if (!del.isEmpty()) {
3733           table.delete(del);
3734         }
3735       }
3736       LOG.info("Initiating flush #" + iFlush + " for table " + tableName);
3737       table.flushCommits();
3738       if (hbaseCluster != null) {
3739         getMiniHBaseCluster().flushcache(table.getName());
3740       }
3741     }
3742 
3743     return table;
3744   }
3745 
3746   private static final int MIN_RANDOM_PORT = 0xc000;
3747   private static final int MAX_RANDOM_PORT = 0xfffe;
3748   private static Random random = new Random();
3749 
3750   /**
3751    * Returns a random port. These ports cannot be registered with IANA and are
3752    * intended for dynamic allocation (see http://bit.ly/dynports).
3753    */
3754   public static int randomPort() {
3755     return MIN_RANDOM_PORT
3756         + random.nextInt(MAX_RANDOM_PORT - MIN_RANDOM_PORT);
3757   }
3758 
3759   /**
3760    * Returns a random free port and marks that port as taken. Not thread-safe. Expected to be
3761    * called from single-threaded test setup code/
3762    */
3763   public static int randomFreePort() {
3764     int port = 0;
3765     do {
3766       port = randomPort();
3767       if (takenRandomPorts.contains(port)) {
3768         port = 0;
3769         continue;
3770       }
3771       takenRandomPorts.add(port);
3772 
3773       try {
3774         ServerSocket sock = new ServerSocket(port);
3775         sock.close();
3776       } catch (IOException ex) {
3777         port = 0;
3778       }
3779     } while (port == 0);
3780     return port;
3781   }
3782 
3783 
3784   public static String randomMultiCastAddress() {
3785     return "226.1.1." + random.nextInt(254);
3786   }
3787 
3788 
3789 
3790   public static void waitForHostPort(String host, int port)
3791       throws IOException {
3792     final int maxTimeMs = 10000;
3793     final int maxNumAttempts = maxTimeMs / HConstants.SOCKET_RETRY_WAIT_MS;
3794     IOException savedException = null;
3795     LOG.info("Waiting for server at " + host + ":" + port);
3796     for (int attempt = 0; attempt < maxNumAttempts; ++attempt) {
3797       try {
3798         Socket sock = new Socket(InetAddress.getByName(host), port);
3799         sock.close();
3800         savedException = null;
3801         LOG.info("Server at " + host + ":" + port + " is available");
3802         break;
3803       } catch (UnknownHostException e) {
3804         throw new IOException("Failed to look up " + host, e);
3805       } catch (IOException e) {
3806         savedException = e;
3807       }
3808       Threads.sleepWithoutInterrupt(HConstants.SOCKET_RETRY_WAIT_MS);
3809     }
3810 
3811     if (savedException != null) {
3812       throw savedException;
3813     }
3814   }
3815 
3816   /**
3817    * Creates a pre-split table for load testing. If the table already exists,
3818    * logs a warning and continues.
3819    * @return the number of regions the table was split into
3820    */
3821   public static int createPreSplitLoadTestTable(Configuration conf,
3822       TableName tableName, byte[] columnFamily, Algorithm compression,
3823       DataBlockEncoding dataBlockEncoding) throws IOException {
3824     return createPreSplitLoadTestTable(conf, tableName,
3825       columnFamily, compression, dataBlockEncoding, DEFAULT_REGIONS_PER_SERVER, 1,
3826       Durability.USE_DEFAULT);
3827   }
3828   /**
3829    * Creates a pre-split table for load testing. If the table already exists,
3830    * logs a warning and continues.
3831    * @return the number of regions the table was split into
3832    */
3833   public static int createPreSplitLoadTestTable(Configuration conf,
3834       TableName tableName, byte[] columnFamily, Algorithm compression,
3835       DataBlockEncoding dataBlockEncoding, int numRegionsPerServer, int regionReplication,
3836       Durability durability)
3837           throws IOException {
3838     HTableDescriptor desc = new HTableDescriptor(tableName);
3839     desc.setDurability(durability);
3840     desc.setRegionReplication(regionReplication);
3841     HColumnDescriptor hcd = new HColumnDescriptor(columnFamily);
3842     hcd.setDataBlockEncoding(dataBlockEncoding);
3843     hcd.setCompressionType(compression);
3844     return createPreSplitLoadTestTable(conf, desc, hcd, numRegionsPerServer);
3845   }
3846 
3847   /**
3848    * Creates a pre-split table for load testing. If the table already exists,
3849    * logs a warning and continues.
3850    * @return the number of regions the table was split into
3851    */
3852   public static int createPreSplitLoadTestTable(Configuration conf,
3853       TableName tableName, byte[][] columnFamilies, Algorithm compression,
3854       DataBlockEncoding dataBlockEncoding, int numRegionsPerServer, int regionReplication,
3855       Durability durability)
3856           throws IOException {
3857     HTableDescriptor desc = new HTableDescriptor(tableName);
3858     desc.setDurability(durability);
3859     desc.setRegionReplication(regionReplication);
3860     HColumnDescriptor[] hcds = new HColumnDescriptor[columnFamilies.length];
3861     for (int i = 0; i < columnFamilies.length; i++) {
3862       HColumnDescriptor hcd = new HColumnDescriptor(columnFamilies[i]);
3863       hcd.setDataBlockEncoding(dataBlockEncoding);
3864       hcd.setCompressionType(compression);
3865       hcds[i] = hcd;
3866     }
3867     return createPreSplitLoadTestTable(conf, desc, hcds, numRegionsPerServer);
3868   }
3869 
3870   /**
3871    * Creates a pre-split table for load testing. If the table already exists,
3872    * logs a warning and continues.
3873    * @return the number of regions the table was split into
3874    */
3875   public static int createPreSplitLoadTestTable(Configuration conf,
3876       HTableDescriptor desc, HColumnDescriptor hcd) throws IOException {
3877     return createPreSplitLoadTestTable(conf, desc, hcd, DEFAULT_REGIONS_PER_SERVER);
3878   }
3879 
3880   /**
3881    * Creates a pre-split table for load testing. If the table already exists,
3882    * logs a warning and continues.
3883    * @return the number of regions the table was split into
3884    */
3885   public static int createPreSplitLoadTestTable(Configuration conf,
3886       HTableDescriptor desc, HColumnDescriptor hcd, int numRegionsPerServer) throws IOException {
3887     return createPreSplitLoadTestTable(conf, desc, new HColumnDescriptor[] {hcd},
3888         numRegionsPerServer);
3889   }
3890 
3891   /**
3892    * Creates a pre-split table for load testing. If the table already exists,
3893    * logs a warning and continues.
3894    * @return the number of regions the table was split into
3895    */
3896   public static int createPreSplitLoadTestTable(Configuration conf,
3897       HTableDescriptor desc, HColumnDescriptor[] hcds, int numRegionsPerServer) throws IOException {
3898     for (HColumnDescriptor hcd : hcds) {
3899       if (!desc.hasFamily(hcd.getName())) {
3900         desc.addFamily(hcd);
3901       }
3902     }
3903 
3904     int totalNumberOfRegions = 0;
3905     Connection unmanagedConnection = ConnectionFactory.createConnection(conf);
3906     Admin admin = unmanagedConnection.getAdmin();
3907 
3908     try {
3909       // create a table a pre-splits regions.
3910       // The number of splits is set as:
3911       //    region servers * regions per region server).
3912       int numberOfServers = admin.getClusterStatus().getServers().size();
3913       if (numberOfServers == 0) {
3914         throw new IllegalStateException("No live regionservers");
3915       }
3916 
3917       totalNumberOfRegions = numberOfServers * numRegionsPerServer;
3918       LOG.info("Number of live regionservers: " + numberOfServers + ", " +
3919           "pre-splitting table into " + totalNumberOfRegions + " regions " +
3920           "(regions per server: " + numRegionsPerServer + ")");
3921 
3922       byte[][] splits = new RegionSplitter.HexStringSplit().split(
3923           totalNumberOfRegions);
3924 
3925       admin.createTable(desc, splits);
3926     } catch (MasterNotRunningException e) {
3927       LOG.error("Master not running", e);
3928       throw new IOException(e);
3929     } catch (TableExistsException e) {
3930       LOG.warn("Table " + desc.getTableName() +
3931           " already exists, continuing");
3932     } finally {
3933       admin.close();
3934       unmanagedConnection.close();
3935     }
3936     return totalNumberOfRegions;
3937   }
3938 
3939   public static int getMetaRSPort(Configuration conf) throws IOException {
3940     try (Connection c = ConnectionFactory.createConnection();
3941         RegionLocator locator = c.getRegionLocator(TableName.META_TABLE_NAME)) {
3942       return locator.getRegionLocation(Bytes.toBytes("")).getPort();
3943     }
3944   }
3945 
3946   /**
3947    *  Due to async racing issue, a region may not be in
3948    *  the online region list of a region server yet, after
3949    *  the assignment znode is deleted and the new assignment
3950    *  is recorded in master.
3951    */
3952   public void assertRegionOnServer(
3953       final HRegionInfo hri, final ServerName server,
3954       final long timeout) throws IOException, InterruptedException {
3955     long timeoutTime = System.currentTimeMillis() + timeout;
3956     while (true) {
3957       List<HRegionInfo> regions = getHBaseAdmin().getOnlineRegions(server);
3958       if (regions.contains(hri)) return;
3959       long now = System.currentTimeMillis();
3960       if (now > timeoutTime) break;
3961       Thread.sleep(10);
3962     }
3963     fail("Could not find region " + hri.getRegionNameAsString()
3964       + " on server " + server);
3965   }
3966 
3967   /**
3968    * Check to make sure the region is open on the specified
3969    * region server, but not on any other one.
3970    */
3971   public void assertRegionOnlyOnServer(
3972       final HRegionInfo hri, final ServerName server,
3973       final long timeout) throws IOException, InterruptedException {
3974     long timeoutTime = System.currentTimeMillis() + timeout;
3975     while (true) {
3976       List<HRegionInfo> regions = getHBaseAdmin().getOnlineRegions(server);
3977       if (regions.contains(hri)) {
3978         List<JVMClusterUtil.RegionServerThread> rsThreads =
3979           getHBaseCluster().getLiveRegionServerThreads();
3980         for (JVMClusterUtil.RegionServerThread rsThread: rsThreads) {
3981           HRegionServer rs = rsThread.getRegionServer();
3982           if (server.equals(rs.getServerName())) {
3983             continue;
3984           }
3985           Collection<Region> hrs = rs.getOnlineRegionsLocalContext();
3986           for (Region r: hrs) {
3987             assertTrue("Region should not be double assigned",
3988               r.getRegionInfo().getRegionId() != hri.getRegionId());
3989           }
3990         }
3991         return; // good, we are happy
3992       }
3993       long now = System.currentTimeMillis();
3994       if (now > timeoutTime) break;
3995       Thread.sleep(10);
3996     }
3997     fail("Could not find region " + hri.getRegionNameAsString()
3998       + " on server " + server);
3999   }
4000 
4001   public HRegion createTestRegion(String tableName, HColumnDescriptor hcd)
4002       throws IOException {
4003     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
4004     htd.addFamily(hcd);
4005     HRegionInfo info =
4006         new HRegionInfo(TableName.valueOf(tableName), null, null, false);
4007     HRegion region =
4008         HRegion.createHRegion(info, getDataTestDir(), getConfiguration(), htd);
4009     return region;
4010   }
4011 
4012   public void setFileSystemURI(String fsURI) {
4013     FS_URI = fsURI;
4014   }
4015 
4016   /**
4017    * Wrapper method for {@link Waiter#waitFor(Configuration, long, Predicate)}.
4018    */
4019   public <E extends Exception> long waitFor(long timeout, Predicate<E> predicate)
4020       throws E {
4021     return Waiter.waitFor(this.conf, timeout, predicate);
4022   }
4023 
4024   /**
4025    * Wrapper method for {@link Waiter#waitFor(Configuration, long, long, Predicate)}.
4026    */
4027   public <E extends Exception> long waitFor(long timeout, long interval, Predicate<E> predicate)
4028       throws E {
4029     return Waiter.waitFor(this.conf, timeout, interval, predicate);
4030   }
4031 
4032   /**
4033    * Wrapper method for {@link Waiter#waitFor(Configuration, long, long, boolean, Predicate)}.
4034    */
4035   public <E extends Exception> long waitFor(long timeout, long interval,
4036       boolean failIfTimeout, Predicate<E> predicate) throws E {
4037     return Waiter.waitFor(this.conf, timeout, interval, failIfTimeout, predicate);
4038   }
4039 
4040   /**
4041    * Returns a {@link Predicate} for checking that there are no regions in transition in master
4042    */
4043   public ExplainingPredicate<IOException> predicateNoRegionsInTransition() {
4044     return new ExplainingPredicate<IOException>() {
4045       @Override
4046       public String explainFailure() throws IOException {
4047         final RegionStates regionStates = getMiniHBaseCluster().getMaster()
4048             .getAssignmentManager().getRegionStates();
4049         return "found in transition: " + regionStates.getRegionsInTransition().toString();
4050       }
4051 
4052       @Override
4053       public boolean evaluate() throws IOException {
4054         HMaster master = getMiniHBaseCluster().getMaster();
4055         if (master == null) return false;
4056         AssignmentManager am = master.getAssignmentManager();
4057         if (am == null) return false;
4058         final RegionStates regionStates = am.getRegionStates();
4059         return !regionStates.isRegionsInTransition();
4060       }
4061     };
4062   }
4063 
4064   /**
4065    * Returns a {@link Predicate} for checking that table is enabled
4066    */
4067   public Waiter.Predicate<IOException> predicateTableEnabled(final TableName tableName) {
4068     return new ExplainingPredicate<IOException>() {
4069       @Override
4070       public String explainFailure() throws IOException {
4071         return explainTableState(tableName);
4072       }
4073 
4074       @Override
4075       public boolean evaluate() throws IOException {
4076         return getHBaseAdmin().tableExists(tableName) && getHBaseAdmin().isTableEnabled(tableName);
4077       }
4078     };
4079   }
4080 
4081   /**
4082    * Returns a {@link Predicate} for checking that table is enabled
4083    */
4084   public Waiter.Predicate<IOException> predicateTableDisabled(final TableName tableName) {
4085     return new ExplainingPredicate<IOException>() {
4086       @Override
4087       public String explainFailure() throws IOException {
4088         return explainTableState(tableName);
4089       }
4090 
4091       @Override
4092       public boolean evaluate() throws IOException {
4093         return getHBaseAdmin().isTableDisabled(tableName);
4094       }
4095     };
4096   }
4097 
4098   /**
4099    * Returns a {@link Predicate} for checking that table is enabled
4100    */
4101   public Waiter.Predicate<IOException> predicateTableAvailable(final TableName tableName) {
4102     return new ExplainingPredicate<IOException>() {
4103       @Override
4104       public String explainFailure() throws IOException {
4105         return explainTableAvailability(tableName);
4106       }
4107 
4108       @Override
4109       public boolean evaluate() throws IOException {
4110         boolean tableAvailable = getHBaseAdmin().isTableAvailable(tableName);
4111         if (tableAvailable) {
4112           try {
4113             Canary.sniff(getHBaseAdmin(), tableName);
4114           } catch (Exception e) {
4115             throw new IOException("Canary sniff failed for table " + tableName, e);
4116           }
4117         }
4118         return tableAvailable;
4119       }
4120     };
4121   }
4122 
4123   /**
4124    * Wait until no regions in transition.
4125    * @param timeout How long to wait.
4126    * @throws IOException
4127    */
4128   public void waitUntilNoRegionsInTransition(final long timeout) throws IOException {
4129     waitFor(timeout, predicateNoRegionsInTransition());
4130   }
4131 
4132   /**
4133    * Wait until no regions in transition. (time limit 15min)
4134    * @throws IOException
4135    */
4136   public void waitUntilNoRegionsInTransition() throws IOException {
4137     waitUntilNoRegionsInTransition(15 * 60000);
4138   }
4139 
4140   /**
4141    * Wait until labels is ready in VisibilityLabelsCache.
4142    * @param timeoutMillis
4143    * @param labels
4144    */
4145   public void waitLabelAvailable(long timeoutMillis, final String... labels) {
4146     final VisibilityLabelsCache labelsCache = VisibilityLabelsCache.get();
4147     waitFor(timeoutMillis, new Waiter.ExplainingPredicate<RuntimeException>() {
4148 
4149       @Override
4150       public boolean evaluate() {
4151         for (String label : labels) {
4152           if (labelsCache.getLabelOrdinal(label) == 0) {
4153             return false;
4154           }
4155         }
4156         return true;
4157       }
4158 
4159       @Override
4160       public String explainFailure() {
4161         for (String label : labels) {
4162           if (labelsCache.getLabelOrdinal(label) == 0) {
4163             return label + " is not available yet";
4164           }
4165         }
4166         return "";
4167       }
4168     });
4169   }
4170 
4171   /**
4172    * Create a set of column descriptors with the combination of compression,
4173    * encoding, bloom codecs available.
4174    * @return the list of column descriptors
4175    */
4176   public static List<HColumnDescriptor> generateColumnDescriptors() {
4177     return generateColumnDescriptors("");
4178   }
4179 
4180   /**
4181    * Create a set of column descriptors with the combination of compression,
4182    * encoding, bloom codecs available.
4183    * @param prefix family names prefix
4184    * @return the list of column descriptors
4185    */
4186   public static List<HColumnDescriptor> generateColumnDescriptors(final String prefix) {
4187     List<HColumnDescriptor> htds = new ArrayList<HColumnDescriptor>();
4188     long familyId = 0;
4189     for (Compression.Algorithm compressionType: getSupportedCompressionAlgorithms()) {
4190       for (DataBlockEncoding encodingType: DataBlockEncoding.values()) {
4191         for (BloomType bloomType: BloomType.values()) {
4192           String name = String.format("%s-cf-!@#&-%d!@#", prefix, familyId);
4193           HColumnDescriptor htd = new HColumnDescriptor(name);
4194           htd.setCompressionType(compressionType);
4195           htd.setDataBlockEncoding(encodingType);
4196           htd.setBloomFilterType(bloomType);
4197           htds.add(htd);
4198           familyId++;
4199         }
4200       }
4201     }
4202     return htds;
4203   }
4204 
4205   /**
4206    * Get supported compression algorithms.
4207    * @return supported compression algorithms.
4208    */
4209   public static Compression.Algorithm[] getSupportedCompressionAlgorithms() {
4210     String[] allAlgos = HFile.getSupportedCompressionAlgorithms();
4211     List<Compression.Algorithm> supportedAlgos = new ArrayList<Compression.Algorithm>();
4212     for (String algoName : allAlgos) {
4213       try {
4214         Compression.Algorithm algo = Compression.getCompressionAlgorithmByName(algoName);
4215         algo.getCompressor();
4216         supportedAlgos.add(algo);
4217       } catch (Throwable t) {
4218         // this algo is not available
4219       }
4220     }
4221     return supportedAlgos.toArray(new Algorithm[supportedAlgos.size()]);
4222   }
4223 
4224 
4225   /**
4226    * Sets up {@link MiniKdc} for testing security.
4227    * Uses {@link HBaseKerberosUtils} to set the given keytab file as
4228    * {@link HBaseKerberosUtils#KRB_KEYTAB_FILE}.
4229    */
4230   public MiniKdc setupMiniKdc(File keytabFile) throws Exception {
4231     Properties conf = MiniKdc.createConf();
4232     conf.put(MiniKdc.DEBUG, true);
4233     MiniKdc kdc = null;
4234     File dir = null;
4235     // There is time lag between selecting a port and trying to bind with it. It's possible that
4236     // another service captures the port in between which'll result in BindException.
4237     boolean bindException;
4238     int numTries = 0;
4239     do {
4240       try {
4241         bindException = false;
4242         dir = new File(getDataTestDir("kdc").toUri().getPath());
4243         kdc = new MiniKdc(conf, dir);
4244         kdc.start();
4245       } catch (BindException e) {
4246         FileUtils.deleteDirectory(dir);  // clean directory
4247         numTries++;
4248         if (numTries == 3) {
4249           LOG.error("Failed setting up MiniKDC. Tried " + numTries + " times.");
4250           throw e;
4251         }
4252         LOG.error("BindException encountered when setting up MiniKdc. Trying again.");
4253         bindException = true;
4254       }
4255     } while (bindException);
4256     HBaseKerberosUtils.setKeytabFileForTesting(keytabFile.getAbsolutePath());
4257     return kdc;
4258   }
4259 }