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