View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase;
20  
21  import java.io.IOException;
22  import java.security.PrivilegedAction;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.classification.InterfaceStability;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.fs.FileSystem;
32  import org.apache.hadoop.hbase.master.HMaster;
33  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
34  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ClientService;
35  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MasterService;
36  import org.apache.hadoop.hbase.protobuf.generated.RegionServerStatusProtos.RegionServerStartupResponse;
37  import org.apache.hadoop.hbase.regionserver.HRegion;
38  import org.apache.hadoop.hbase.regionserver.HRegionServer;
39  import org.apache.hadoop.hbase.regionserver.Region;
40  import org.apache.hadoop.hbase.security.User;
41  import org.apache.hadoop.hbase.test.MetricsAssertHelper;
42  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
43  import org.apache.hadoop.hbase.util.JVMClusterUtil;
44  import org.apache.hadoop.hbase.util.JVMClusterUtil.MasterThread;
45  import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
46  import org.apache.hadoop.hbase.util.Threads;
47  
48  /**
49   * This class creates a single process HBase cluster.
50   * each server.  The master uses the 'default' FileSystem.  The RegionServers,
51   * if we are running on DistributedFilesystem, create a FileSystem instance
52   * each and will close down their instance on the way out.
53   */
54  @InterfaceAudience.Public
55  @InterfaceStability.Evolving
56  public class MiniHBaseCluster extends HBaseCluster {
57    private static final Log LOG = LogFactory.getLog(MiniHBaseCluster.class.getName());
58    public LocalHBaseCluster hbaseCluster;
59    private static int index;
60  
61    /**
62     * Start a MiniHBaseCluster.
63     * @param conf Configuration to be used for cluster
64     * @param numRegionServers initial number of region servers to start.
65     * @throws IOException
66     */
67    public MiniHBaseCluster(Configuration conf, int numRegionServers)
68    throws IOException, InterruptedException {
69      this(conf, 1, numRegionServers);
70    }
71  
72    /**
73     * Start a MiniHBaseCluster.
74     * @param conf Configuration to be used for cluster
75     * @param numMasters initial number of masters to start.
76     * @param numRegionServers initial number of region servers to start.
77     * @throws IOException
78     */
79    public MiniHBaseCluster(Configuration conf, int numMasters, int numRegionServers)
80        throws IOException, InterruptedException {
81      this(conf, numMasters, numRegionServers, null, null);
82    }
83  
84    public MiniHBaseCluster(Configuration conf, int numMasters, int numRegionServers,
85           Class<? extends HMaster> masterClass,
86           Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
87        throws IOException, InterruptedException {
88      super(conf);
89      if (conf.getBoolean(LocalHBaseCluster.ASSIGN_RANDOM_PORTS, true)) {
90        conf.set(HConstants.MASTER_PORT, "0");
91        if (conf.getInt(HConstants.MASTER_INFO_PORT, 0) != -1) {
92          conf.set(HConstants.MASTER_INFO_PORT, "0");
93        }
94      }
95  
96      // Hadoop 2
97      CompatibilityFactory.getInstance(MetricsAssertHelper.class).init();
98  
99      init(numMasters, numRegionServers, masterClass, regionserverClass);
100     this.initialClusterStatus = getClusterStatus();
101   }
102 
103   public Configuration getConfiguration() {
104     return this.conf;
105   }
106 
107   /**
108    * Subclass so can get at protected methods (none at moment).  Also, creates
109    * a FileSystem instance per instantiation.  Adds a shutdown own FileSystem
110    * on the way out. Shuts down own Filesystem only, not All filesystems as
111    * the FileSystem system exit hook does.
112    */
113   public static class MiniHBaseClusterRegionServer extends HRegionServer {
114     private Thread shutdownThread = null;
115     private User user = null;
116 
117     public MiniHBaseClusterRegionServer(Configuration conf, CoordinatedStateManager cp)
118         throws IOException, InterruptedException {
119       super(conf, cp);
120       this.user = User.getCurrent();
121     }
122 
123     /*
124      * @param c
125      * @param currentfs We return this if we did not make a new one.
126      * @param uniqueName Same name used to help identify the created fs.
127      * @return A new fs instance if we are up on DistributeFileSystem.
128      * @throws IOException
129      */
130 
131     @Override
132     protected void handleReportForDutyResponse(
133         final RegionServerStartupResponse c) throws IOException {
134       super.handleReportForDutyResponse(c);
135       // Run this thread to shutdown our filesystem on way out.
136       this.shutdownThread = new SingleFileSystemShutdownThread(getFileSystem());
137     }
138 
139     @Override
140     public void run() {
141       try {
142         this.user.runAs(new PrivilegedAction<Object>(){
143           public Object run() {
144             runRegionServer();
145             return null;
146           }
147         });
148       } catch (Throwable t) {
149         LOG.error("Exception in run", t);
150       } finally {
151         // Run this on the way out.
152         if (this.shutdownThread != null) {
153           this.shutdownThread.start();
154           Threads.shutdown(this.shutdownThread, 30000);
155         }
156       }
157     }
158 
159     private void runRegionServer() {
160       super.run();
161     }
162 
163     @Override
164     public void kill() {
165       super.kill();
166     }
167 
168     @Override
169     public void abort(final String reason, final Throwable cause) {
170       this.user.runAs(new PrivilegedAction<Object>() {
171         public Object run() {
172           abortRegionServer(reason, cause);
173           return null;
174         }
175       });
176     }
177 
178     private void abortRegionServer(String reason, Throwable cause) {
179       super.abort(reason, cause);
180     }
181   }
182 
183   /**
184    * Alternate shutdown hook.
185    * Just shuts down the passed fs, not all as default filesystem hook does.
186    */
187   static class SingleFileSystemShutdownThread extends Thread {
188     private final FileSystem fs;
189     SingleFileSystemShutdownThread(final FileSystem fs) {
190       super("Shutdown of " + fs);
191       this.fs = fs;
192     }
193     @Override
194     public void run() {
195       try {
196         LOG.info("Hook closing fs=" + this.fs);
197         this.fs.close();
198       } catch (NullPointerException npe) {
199         LOG.debug("Need to fix these: " + npe.toString());
200       } catch (IOException e) {
201         LOG.warn("Running hook", e);
202       }
203     }
204   }
205 
206   private void init(final int nMasterNodes, final int nRegionNodes,
207                  Class<? extends HMaster> masterClass,
208                  Class<? extends MiniHBaseCluster.MiniHBaseClusterRegionServer> regionserverClass)
209   throws IOException, InterruptedException {
210     try {
211       if (masterClass == null){
212         masterClass =  HMaster.class;
213       }
214       if (regionserverClass == null){
215         regionserverClass = MiniHBaseCluster.MiniHBaseClusterRegionServer.class;
216       }
217 
218       // start up a LocalHBaseCluster
219       hbaseCluster = new LocalHBaseCluster(conf, nMasterNodes, 0,
220           masterClass, regionserverClass);
221 
222       // manually add the regionservers as other users
223       for (int i=0; i<nRegionNodes; i++) {
224         Configuration rsConf = HBaseConfiguration.create(conf);
225         User user = HBaseTestingUtility.getDifferentUser(rsConf,
226             ".hfs."+index++);
227         hbaseCluster.addRegionServer(rsConf, i, user);
228       }
229 
230       hbaseCluster.startup();
231     } catch (IOException e) {
232       shutdown();
233       throw e;
234     } catch (Throwable t) {
235       LOG.error("Error starting cluster", t);
236       shutdown();
237       throw new IOException("Shutting down", t);
238     }
239   }
240 
241   @Override
242   public void startRegionServer(String hostname, int port) throws IOException {
243     this.startRegionServer();
244   }
245 
246   @Override
247   public void killRegionServer(ServerName serverName) throws IOException {
248     HRegionServer server = getRegionServer(getRegionServerIndex(serverName));
249     if (server instanceof MiniHBaseClusterRegionServer) {
250       LOG.info("Killing " + server.toString());
251       ((MiniHBaseClusterRegionServer) server).kill();
252     } else {
253       abortRegionServer(getRegionServerIndex(serverName));
254     }
255   }
256 
257   @Override
258   public void stopRegionServer(ServerName serverName) throws IOException {
259     stopRegionServer(getRegionServerIndex(serverName));
260   }
261 
262   @Override
263   public void suspendRegionServer(ServerName serverName) throws IOException {
264     suspendRegionServer(getRegionServerIndex(serverName));
265   }
266 
267   @Override
268   public void resumeRegionServer(ServerName serverName) throws IOException {
269     resumeRegionServer(getRegionServerIndex(serverName));
270   }
271 
272   @Override
273   public void waitForRegionServerToStop(ServerName serverName, long timeout) throws IOException {
274     //ignore timeout for now
275     waitOnRegionServer(getRegionServerIndex(serverName));
276   }
277 
278   @Override
279   public void startZkNode(String hostname, int port) throws IOException {
280     LOG.warn("Starting zookeeper nodes on mini cluster is not supported");
281   }
282 
283   @Override
284   public void killZkNode(ServerName serverName) throws IOException {
285     LOG.warn("Aborting zookeeper nodes on mini cluster is not supported");
286   }
287 
288   @Override
289   public void stopZkNode(ServerName serverName) throws IOException {
290     LOG.warn("Stopping zookeeper nodes on mini cluster is not supported");
291   }
292 
293   @Override
294   public void waitForZkNodeToStart(ServerName serverName, long timeout) throws IOException {
295     LOG.warn("Waiting for zookeeper nodes to start on mini cluster is not supported");
296   }
297 
298   @Override
299   public void waitForZkNodeToStop(ServerName serverName, long timeout) throws IOException {
300     LOG.warn("Waiting for zookeeper nodes to stop on mini cluster is not supported");
301   }
302 
303   @Override
304   public void startDataNode(ServerName serverName) throws IOException {
305     LOG.warn("Starting datanodes on mini cluster is not supported");
306   }
307 
308   @Override
309   public void killDataNode(ServerName serverName) throws IOException {
310     LOG.warn("Aborting datanodes on mini cluster is not supported");
311   }
312 
313   @Override
314   public void stopDataNode(ServerName serverName) throws IOException {
315     LOG.warn("Stopping datanodes on mini cluster is not supported");
316   }
317 
318   @Override
319   public void waitForDataNodeToStart(ServerName serverName, long timeout) throws IOException {
320     LOG.warn("Waiting for datanodes to start on mini cluster is not supported");
321   }
322 
323   @Override
324   public void waitForDataNodeToStop(ServerName serverName, long timeout) throws IOException {
325     LOG.warn("Waiting for datanodes to stop on mini cluster is not supported");
326   }
327 
328   @Override
329   public void startNameNode(ServerName serverName) throws IOException {
330     LOG.warn("Starting namenodes on mini cluster is not supported");
331   }
332 
333   @Override
334   public void killNameNode(ServerName serverName) throws IOException {
335     LOG.warn("Aborting namenodes on mini cluster is not supported");
336   }
337 
338   @Override
339   public void stopNameNode(ServerName serverName) throws IOException {
340     LOG.warn("Stopping namenodes on mini cluster is not supported");
341   }
342 
343   @Override
344   public void waitForNameNodeToStart(ServerName serverName, long timeout) throws IOException {
345     LOG.warn("Waiting for namenodes to start on mini cluster is not supported");
346   }
347 
348   @Override
349   public void waitForNameNodeToStop(ServerName serverName, long timeout) throws IOException {
350     LOG.warn("Waiting for namenodes to stop on mini cluster is not supported");
351   }
352 
353   @Override
354   public void startMaster(String hostname, int port) throws IOException {
355     this.startMaster();
356   }
357 
358   @Override
359   public void killMaster(ServerName serverName) throws IOException {
360     abortMaster(getMasterIndex(serverName));
361   }
362 
363   @Override
364   public void stopMaster(ServerName serverName) throws IOException {
365     stopMaster(getMasterIndex(serverName));
366   }
367 
368   @Override
369   public void waitForMasterToStop(ServerName serverName, long timeout) throws IOException {
370     //ignore timeout for now
371     waitOnMaster(getMasterIndex(serverName));
372   }
373 
374   /**
375    * Starts a region server thread running
376    *
377    * @throws IOException
378    * @return New RegionServerThread
379    */
380   public JVMClusterUtil.RegionServerThread startRegionServer()
381       throws IOException {
382     final Configuration newConf = HBaseConfiguration.create(conf);
383     User rsUser =
384         HBaseTestingUtility.getDifferentUser(newConf, ".hfs."+index++);
385     JVMClusterUtil.RegionServerThread t =  null;
386     try {
387       t = hbaseCluster.addRegionServer(
388           newConf, hbaseCluster.getRegionServers().size(), rsUser);
389       t.start();
390       t.waitForServerOnline();
391     } catch (InterruptedException ie) {
392       throw new IOException("Interrupted adding regionserver to cluster", ie);
393     }
394     return t;
395   }
396 
397   /**
398    * Starts a region server thread and waits until its processed by master. Throws an exception
399    * when it can't start a region server or when the region server is not processed by master
400    * within the timeout.
401    *
402    * @return New RegionServerThread
403    */
404   public JVMClusterUtil.RegionServerThread startRegionServerAndWait(long timeout)
405     throws IOException {
406 
407     JVMClusterUtil.RegionServerThread t =  startRegionServer();
408     ServerName rsServerName = t.getRegionServer().getServerName();
409 
410     long start = EnvironmentEdgeManager.currentTime();
411     while ((EnvironmentEdgeManager.currentTime() - start) < timeout) {
412       ClusterStatus clusterStatus = getMaster().getClusterStatus();
413       if (clusterStatus != null && clusterStatus.getLiveServersLoad().containsKey(rsServerName)) {
414         return t;
415       }
416       Threads.sleep(100);
417     }
418     if (t.getRegionServer().isOnline()) {
419       throw new IOException("RS: " + rsServerName + " online, but not processed by master");
420     } else {
421       throw new IOException("RS: " + rsServerName + " is offline");
422     }
423   }
424 
425   /**
426    * Cause a region server to exit doing basic clean up only on its way out.
427    * @param serverNumber  Used as index into a list.
428    */
429   public String abortRegionServer(int serverNumber) {
430     HRegionServer server = getRegionServer(serverNumber);
431     LOG.info("Aborting " + server.toString());
432     server.abort("Aborting for tests", new Exception("Trace info"));
433     return server.toString();
434   }
435 
436   /**
437    * Shut down the specified region server cleanly
438    *
439    * @param serverNumber  Used as index into a list.
440    * @return the region server that was stopped
441    */
442   public JVMClusterUtil.RegionServerThread stopRegionServer(int serverNumber) {
443     return stopRegionServer(serverNumber, true);
444   }
445 
446   /**
447    * Shut down the specified region server cleanly
448    *
449    * @param serverNumber  Used as index into a list.
450    * @param shutdownFS True is we are to shutdown the filesystem as part of this
451    * regionserver's shutdown.  Usually we do but you do not want to do this if
452    * you are running multiple regionservers in a test and you shut down one
453    * before end of the test.
454    * @return the region server that was stopped
455    */
456   public JVMClusterUtil.RegionServerThread stopRegionServer(int serverNumber,
457       final boolean shutdownFS) {
458     JVMClusterUtil.RegionServerThread server =
459       hbaseCluster.getRegionServers().get(serverNumber);
460     LOG.info("Stopping " + server.toString());
461     server.getRegionServer().stop("Stopping rs " + serverNumber);
462     return server;
463   }
464 
465   /**
466    * Suspend the specified region server
467    * @param serverNumber Used as index into a list.
468    * @return server instance that was suspended.
469    */
470   public JVMClusterUtil.RegionServerThread suspendRegionServer(int serverNumber) {
471     JVMClusterUtil.RegionServerThread server =
472         hbaseCluster.getRegionServers().get(serverNumber);
473     LOG.info("Suspending " + server.toString());
474     server.suspend();
475     return server;
476   }
477 
478   /**
479    * Resume the specified region server
480    * @param serverNumber Used as index into a list.
481    * @return server instance that was resumed.
482    */
483   public JVMClusterUtil.RegionServerThread resumeRegionServer(int serverNumber) {
484     JVMClusterUtil.RegionServerThread server =
485         hbaseCluster.getRegionServers().get(serverNumber);
486     LOG.info("Resuming " + server.toString());
487     server.resume();
488     return server;
489   }
490 
491   /**
492    * Wait for the specified region server to stop. Removes this thread from list
493    * of running threads.
494    * @param serverNumber
495    * @return Name of region server that just went down.
496    */
497   public String waitOnRegionServer(final int serverNumber) {
498     return this.hbaseCluster.waitOnRegionServer(serverNumber);
499   }
500 
501 
502   /**
503    * Starts a master thread running
504    *
505    * @throws IOException
506    * @return New RegionServerThread
507    */
508   public JVMClusterUtil.MasterThread startMaster() throws IOException {
509     Configuration c = HBaseConfiguration.create(conf);
510     User user =
511         HBaseTestingUtility.getDifferentUser(c, ".hfs."+index++);
512 
513     JVMClusterUtil.MasterThread t = null;
514     try {
515       t = hbaseCluster.addMaster(c, hbaseCluster.getMasters().size(), user);
516       t.start();
517     } catch (InterruptedException ie) {
518       throw new IOException("Interrupted adding master to cluster", ie);
519     }
520     return t;
521   }
522 
523   /**
524    * Returns the current active master, if available.
525    * @return the active HMaster, null if none is active.
526    */
527   public MasterService.BlockingInterface getMasterAdminService() {
528     return this.hbaseCluster.getActiveMaster().getMasterRpcServices();
529   }
530 
531   /**
532    * Returns the current active master, if available.
533    * @return the active HMaster, null if none is active.
534    */
535   public HMaster getMaster() {
536     return this.hbaseCluster.getActiveMaster();
537   }
538 
539   /**
540    * Returns the current active master thread, if available.
541    * @return the active MasterThread, null if none is active.
542    */
543   public MasterThread getMasterThread() {
544     for (MasterThread mt: hbaseCluster.getLiveMasters()) {
545       if (mt.getMaster().isActiveMaster()) {
546         return mt;
547       }
548     }
549     return null;
550   }
551 
552   /**
553    * Returns the master at the specified index, if available.
554    * @return the active HMaster, null if none is active.
555    */
556   public HMaster getMaster(final int serverNumber) {
557     return this.hbaseCluster.getMaster(serverNumber);
558   }
559 
560   /**
561    * Cause a master to exit without shutting down entire cluster.
562    * @param serverNumber  Used as index into a list.
563    */
564   public String abortMaster(int serverNumber) {
565     HMaster server = getMaster(serverNumber);
566     LOG.info("Aborting " + server.toString());
567     server.abort("Aborting for tests", new Exception("Trace info"));
568     return server.toString();
569   }
570 
571   /**
572    * Shut down the specified master cleanly
573    *
574    * @param serverNumber  Used as index into a list.
575    * @return the region server that was stopped
576    */
577   public JVMClusterUtil.MasterThread stopMaster(int serverNumber) {
578     return stopMaster(serverNumber, true);
579   }
580 
581   /**
582    * Shut down the specified master cleanly
583    *
584    * @param serverNumber  Used as index into a list.
585    * @param shutdownFS True is we are to shutdown the filesystem as part of this
586    * master's shutdown.  Usually we do but you do not want to do this if
587    * you are running multiple master in a test and you shut down one
588    * before end of the test.
589    * @return the master that was stopped
590    */
591   public JVMClusterUtil.MasterThread stopMaster(int serverNumber,
592       final boolean shutdownFS) {
593     JVMClusterUtil.MasterThread server =
594       hbaseCluster.getMasters().get(serverNumber);
595     LOG.info("Stopping " + server.toString());
596     server.getMaster().stop("Stopping master " + serverNumber);
597     return server;
598   }
599 
600   /**
601    * Wait for the specified master to stop. Removes this thread from list
602    * of running threads.
603    * @param serverNumber
604    * @return Name of master that just went down.
605    */
606   public String waitOnMaster(final int serverNumber) {
607     return this.hbaseCluster.waitOnMaster(serverNumber);
608   }
609 
610   /**
611    * Blocks until there is an active master and that master has completed
612    * initialization.
613    *
614    * @return true if an active master becomes available.  false if there are no
615    *         masters left.
616    * @throws InterruptedException
617    */
618   public boolean waitForActiveAndReadyMaster(long timeout) throws IOException {
619     List<JVMClusterUtil.MasterThread> mts;
620     long start = System.currentTimeMillis();
621     while (!(mts = getMasterThreads()).isEmpty()
622         && (System.currentTimeMillis() - start) < timeout) {
623       for (JVMClusterUtil.MasterThread mt : mts) {
624         if (mt.getMaster().isActiveMaster() && mt.getMaster().isInitialized()) {
625           return true;
626         }
627       }
628 
629       Threads.sleep(100);
630     }
631     return false;
632   }
633 
634   /**
635    * @return List of master threads.
636    */
637   public List<JVMClusterUtil.MasterThread> getMasterThreads() {
638     return this.hbaseCluster.getMasters();
639   }
640 
641   /**
642    * @return List of live master threads (skips the aborted and the killed)
643    */
644   public List<JVMClusterUtil.MasterThread> getLiveMasterThreads() {
645     return this.hbaseCluster.getLiveMasters();
646   }
647 
648   /**
649    * Wait for Mini HBase Cluster to shut down.
650    */
651   public void join() {
652     this.hbaseCluster.join();
653   }
654 
655   /**
656    * Shut down the mini HBase cluster
657    * @throws IOException
658    */
659   @SuppressWarnings("deprecation")
660   public void shutdown() throws IOException {
661     if (this.hbaseCluster != null) {
662       this.hbaseCluster.shutdown();
663     }
664   }
665 
666   @Override
667   public void close() throws IOException {
668   }
669 
670   @Override
671   public ClusterStatus getClusterStatus() throws IOException {
672     HMaster master = getMaster();
673     return master == null ? null : master.getClusterStatus();
674   }
675 
676   /**
677    * Call flushCache on all regions on all participating regionservers.
678    * @throws IOException
679    */
680   public void flushcache() throws IOException {
681     for (JVMClusterUtil.RegionServerThread t:
682         this.hbaseCluster.getRegionServers()) {
683       for(Region r: t.getRegionServer().getOnlineRegionsLocalContext()) {
684         r.flush(true);
685       }
686     }
687   }
688 
689   /**
690    * Call flushCache on all regions of the specified table.
691    * @throws IOException
692    */
693   public void flushcache(TableName tableName) throws IOException {
694     for (JVMClusterUtil.RegionServerThread t:
695         this.hbaseCluster.getRegionServers()) {
696       for(Region r: t.getRegionServer().getOnlineRegionsLocalContext()) {
697         if(r.getTableDesc().getTableName().equals(tableName)) {
698           r.flush(true);
699         }
700       }
701     }
702   }
703 
704   /**
705    * Call flushCache on all regions on all participating regionservers.
706    * @throws IOException
707    */
708   public void compact(boolean major) throws IOException {
709     for (JVMClusterUtil.RegionServerThread t:
710         this.hbaseCluster.getRegionServers()) {
711       for(Region r: t.getRegionServer().getOnlineRegionsLocalContext()) {
712         r.compact(major);
713       }
714     }
715   }
716 
717   /**
718    * Call flushCache on all regions of the specified table.
719    * @throws IOException
720    */
721   public void compact(TableName tableName, boolean major) throws IOException {
722     for (JVMClusterUtil.RegionServerThread t:
723         this.hbaseCluster.getRegionServers()) {
724       for(Region r: t.getRegionServer().getOnlineRegionsLocalContext()) {
725         if(r.getTableDesc().getTableName().equals(tableName)) {
726           r.compact(major);
727         }
728       }
729     }
730   }
731 
732   /**
733    * @return List of region server threads.
734    */
735   public List<JVMClusterUtil.RegionServerThread> getRegionServerThreads() {
736     return this.hbaseCluster.getRegionServers();
737   }
738 
739   /**
740    * @return List of live region server threads (skips the aborted and the killed)
741    */
742   public List<JVMClusterUtil.RegionServerThread> getLiveRegionServerThreads() {
743     return this.hbaseCluster.getLiveRegionServers();
744   }
745 
746   /**
747    * Grab a numbered region server of your choice.
748    * @param serverNumber
749    * @return region server
750    */
751   public HRegionServer getRegionServer(int serverNumber) {
752     return hbaseCluster.getRegionServer(serverNumber);
753   }
754 
755   public HRegionServer getRegionServer(ServerName serverName) {
756     for (RegionServerThread t : hbaseCluster.getRegionServers()) {
757       HRegionServer r = t.getRegionServer();
758       if (r.getServerName().equals(serverName)) {
759         return r;
760       }
761     }
762     return null;
763   }
764 
765   public List<HRegion> getRegions(byte[] tableName) {
766     return getRegions(TableName.valueOf(tableName));
767   }
768 
769   public List<HRegion> getRegions(TableName tableName) {
770     List<HRegion> ret = new ArrayList<HRegion>();
771     for (JVMClusterUtil.RegionServerThread rst : getRegionServerThreads()) {
772       HRegionServer hrs = rst.getRegionServer();
773       for (Region region : hrs.getOnlineRegionsLocalContext()) {
774         if (region.getTableDesc().getTableName().equals(tableName)) {
775           ret.add((HRegion)region);
776         }
777       }
778     }
779     return ret;
780   }
781 
782   /**
783    * @return Index into List of {@link MiniHBaseCluster#getRegionServerThreads()}
784    * of HRS carrying regionName. Returns -1 if none found.
785    */
786   public int getServerWithMeta() {
787     return getServerWith(HRegionInfo.FIRST_META_REGIONINFO.getRegionName());
788   }
789 
790   /**
791    * Get the location of the specified region
792    * @param regionName Name of the region in bytes
793    * @return Index into List of {@link MiniHBaseCluster#getRegionServerThreads()}
794    * of HRS carrying hbase:meta. Returns -1 if none found.
795    */
796   public int getServerWith(byte[] regionName) {
797     int index = -1;
798     int count = 0;
799     for (JVMClusterUtil.RegionServerThread rst: getRegionServerThreads()) {
800       HRegionServer hrs = rst.getRegionServer();
801       if (!hrs.isStopped()) {
802         Region region = hrs.getOnlineRegion(regionName);
803         if (region != null) {
804           index = count;
805           break;
806         }
807       }
808       count++;
809     }
810     return index;
811   }
812 
813   @Override
814   public ServerName getServerHoldingRegion(final TableName tn, byte[] regionName)
815   throws IOException {
816     // Assume there is only one master thread which is the active master.
817     // If there are multiple master threads, the backup master threads
818     // should hold some regions. Please refer to #countServedRegions
819     // to see how we find out all regions.
820     HMaster master = getMaster();
821     Region region = master.getOnlineRegion(regionName);
822     if (region != null) {
823       return master.getServerName();
824     }
825     int index = getServerWith(regionName);
826     if (index < 0) {
827       return null;
828     }
829     return getRegionServer(index).getServerName();
830   }
831 
832   /**
833    * Counts the total numbers of regions being served by the currently online
834    * region servers by asking each how many regions they have.  Does not look
835    * at hbase:meta at all.  Count includes catalog tables.
836    * @return number of regions being served by all region servers
837    */
838   public long countServedRegions() {
839     long count = 0;
840     for (JVMClusterUtil.RegionServerThread rst : getLiveRegionServerThreads()) {
841       count += rst.getRegionServer().getNumberOfOnlineRegions();
842     }
843     for (JVMClusterUtil.MasterThread mt : getLiveMasterThreads()) {
844       count += mt.getMaster().getNumberOfOnlineRegions();
845     }
846     return count;
847   }
848 
849   /**
850    * Do a simulated kill all masters and regionservers. Useful when it is
851    * impossible to bring the mini-cluster back for clean shutdown.
852    */
853   public void killAll() {
854     for (RegionServerThread rst : getRegionServerThreads()) {
855       rst.getRegionServer().abort("killAll");
856     }
857     for (MasterThread masterThread : getMasterThreads()) {
858       masterThread.getMaster().abort("killAll", new Throwable());
859     }
860   }
861 
862   @Override
863   public void waitUntilShutDown() {
864     this.hbaseCluster.join();
865   }
866 
867   public List<HRegion> findRegionsForTable(TableName tableName) {
868     ArrayList<HRegion> ret = new ArrayList<HRegion>();
869     for (JVMClusterUtil.RegionServerThread rst : getRegionServerThreads()) {
870       HRegionServer hrs = rst.getRegionServer();
871       for (Region region : hrs.getOnlineRegions(tableName)) {
872         if (region.getTableDesc().getTableName().equals(tableName)) {
873           ret.add((HRegion)region);
874         }
875       }
876     }
877     return ret;
878   }
879 
880 
881   protected int getRegionServerIndex(ServerName serverName) {
882     //we have a small number of region servers, this should be fine for now.
883     List<RegionServerThread> servers = getRegionServerThreads();
884     for (int i=0; i < servers.size(); i++) {
885       if (servers.get(i).getRegionServer().getServerName().equals(serverName)) {
886         return i;
887       }
888     }
889     return -1;
890   }
891 
892   protected int getMasterIndex(ServerName serverName) {
893     List<MasterThread> masters = getMasterThreads();
894     for (int i = 0; i < masters.size(); i++) {
895       if (masters.get(i).getMaster().getServerName().equals(serverName)) {
896         return i;
897       }
898     }
899     return -1;
900   }
901 
902   @Override
903   public AdminService.BlockingInterface getAdminProtocol(ServerName serverName) throws IOException {
904     return getRegionServer(getRegionServerIndex(serverName)).getRSRpcServices();
905   }
906 
907   @Override
908   public ClientService.BlockingInterface getClientProtocol(ServerName serverName)
909   throws IOException {
910     return getRegionServer(getRegionServerIndex(serverName)).getRSRpcServices();
911   }
912 }