View Javadoc

1   /**
2    * Copyright 2007 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase;
21  
22  import java.io.IOException;
23  import java.security.PrivilegedExceptionAction;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.conf.Configuration;
31  import org.apache.hadoop.hbase.client.HBaseAdmin;
32  import org.apache.hadoop.hbase.regionserver.HRegionServer;
33  import org.apache.hadoop.hbase.security.User;
34  import org.apache.hadoop.hbase.util.Bytes;
35  import org.apache.hadoop.hbase.util.JVMClusterUtil.RegionServerThread;
36  import org.apache.hadoop.hbase.util.Threads;
37  
38  import java.util.concurrent.CopyOnWriteArrayList;
39  import org.apache.hadoop.hbase.master.HMaster;
40  import org.apache.hadoop.hbase.util.JVMClusterUtil;
41  
42  /**
43   * This class creates a single process HBase cluster. One thread is created for
44   * a master and one per region server.
45   *
46   * Call {@link #startup()} to start the cluster running and {@link #shutdown()}
47   * to close it all down. {@link #join} the cluster is you want to wait on
48   * shutdown completion.
49   *
50   * <p>Runs master on port 60000 by default.  Because we can't just kill the
51   * process -- not till HADOOP-1700 gets fixed and even then.... -- we need to
52   * be able to find the master with a remote client to run shutdown.  To use a
53   * port other than 60000, set the hbase.master to a value of 'local:PORT':
54   * that is 'local', not 'localhost', and the port number the master should use
55   * instead of 60000.
56   *
57   */
58  public class LocalHBaseCluster {
59    static final Log LOG = LogFactory.getLog(LocalHBaseCluster.class);
60    private final List<JVMClusterUtil.MasterThread> masterThreads =
61      new CopyOnWriteArrayList<JVMClusterUtil.MasterThread>();
62    private final List<JVMClusterUtil.RegionServerThread> regionThreads =
63      new CopyOnWriteArrayList<JVMClusterUtil.RegionServerThread>();
64    private final static int DEFAULT_NO = 1;
65    /** local mode */
66    public static final String LOCAL = "local";
67    /** 'local:' */
68    public static final String LOCAL_COLON = LOCAL + ":";
69    private final Configuration conf;
70    private final Class<? extends HMaster> masterClass;
71    private final Class<? extends HRegionServer> regionServerClass;
72  
73    /**
74     * Constructor.
75     * @param conf
76     * @throws IOException
77     */
78    public LocalHBaseCluster(final Configuration conf)
79    throws IOException {
80      this(conf, DEFAULT_NO);
81    }
82  
83    /**
84     * Constructor.
85     * @param conf Configuration to use.  Post construction has the master's
86     * address.
87     * @param noRegionServers Count of regionservers to start.
88     * @throws IOException
89     */
90    public LocalHBaseCluster(final Configuration conf, final int noRegionServers)
91    throws IOException {
92      this(conf, 1, noRegionServers, getMasterImplementation(conf),
93          getRegionServerImplementation(conf));
94    }
95  
96    /**
97     * Constructor.
98     * @param conf Configuration to use.  Post construction has the active master
99     * address.
100    * @param noMasters Count of masters to start.
101    * @param noRegionServers Count of regionservers to start.
102    * @throws IOException
103    */
104   public LocalHBaseCluster(final Configuration conf, final int noMasters,
105       final int noRegionServers)
106   throws IOException {
107     this(conf, noMasters, noRegionServers, getMasterImplementation(conf),
108         getRegionServerImplementation(conf));
109   }
110 
111   @SuppressWarnings("unchecked")
112   private static Class<? extends HRegionServer> getRegionServerImplementation(final Configuration conf) {
113     return (Class<? extends HRegionServer>)conf.getClass(HConstants.REGION_SERVER_IMPL,
114        HRegionServer.class);
115   }
116 
117   @SuppressWarnings("unchecked")
118   private static Class<? extends HMaster> getMasterImplementation(final Configuration conf) {
119     return (Class<? extends HMaster>)conf.getClass(HConstants.MASTER_IMPL,
120        HMaster.class);
121   }
122 
123   /**
124    * Constructor.
125    * @param conf Configuration to use.  Post construction has the master's
126    * address.
127    * @param noMasters Count of masters to start.
128    * @param noRegionServers Count of regionservers to start.
129    * @param masterClass
130    * @param regionServerClass
131    * @throws IOException
132    */
133   @SuppressWarnings("unchecked")
134   public LocalHBaseCluster(final Configuration conf, final int noMasters,
135     final int noRegionServers, final Class<? extends HMaster> masterClass,
136     final Class<? extends HRegionServer> regionServerClass)
137   throws IOException {
138     this.conf = conf;
139     // Always have masters and regionservers come up on port '0' so we don't
140     // clash over default ports.
141     conf.set(HConstants.MASTER_PORT, "0");
142     conf.set(HConstants.REGIONSERVER_PORT, "0");
143     // Start the HMasters.
144     this.masterClass =
145       (Class<? extends HMaster>)conf.getClass(HConstants.MASTER_IMPL,
146           masterClass);
147     for (int i = 0; i < noMasters; i++) {
148       addMaster(new Configuration(conf), i);
149     }
150     // Start the HRegionServers.
151     this.regionServerClass =
152       (Class<? extends HRegionServer>)conf.getClass(HConstants.REGION_SERVER_IMPL,
153        regionServerClass);
154 
155     for (int i = 0; i < noRegionServers; i++) {
156       addRegionServer(new Configuration(conf), i);
157     }
158   }
159 
160   public JVMClusterUtil.RegionServerThread addRegionServer()
161       throws IOException {
162     return addRegionServer(new Configuration(conf), this.regionThreads.size());
163   }
164 
165   public JVMClusterUtil.RegionServerThread addRegionServer(
166       Configuration config, final int index)
167   throws IOException {
168     // Create each regionserver with its own Configuration instance so each has
169     // its HConnection instance rather than share (see HBASE_INSTANCES down in
170     // the guts of HConnectionManager.
171     JVMClusterUtil.RegionServerThread rst =
172       JVMClusterUtil.createRegionServerThread(config,
173           this.regionServerClass, index);
174     this.regionThreads.add(rst);
175     return rst;
176   }
177 
178   public JVMClusterUtil.RegionServerThread addRegionServer(
179       final Configuration config, final int index, User user)
180   throws IOException, InterruptedException {
181     return user.runAs(
182         new PrivilegedExceptionAction<JVMClusterUtil.RegionServerThread>() {
183           public JVMClusterUtil.RegionServerThread run() throws Exception {
184             return addRegionServer(config, index);
185           }
186         });
187   }
188 
189   public JVMClusterUtil.MasterThread addMaster() throws IOException {
190     return addMaster(new Configuration(conf), this.masterThreads.size());
191   }
192 
193   public JVMClusterUtil.MasterThread addMaster(Configuration c, final int index)
194   throws IOException {
195     // Create each master with its own Configuration instance so each has
196     // its HConnection instance rather than share (see HBASE_INSTANCES down in
197     // the guts of HConnectionManager.
198     JVMClusterUtil.MasterThread mt =
199       JVMClusterUtil.createMasterThread(c,
200         this.masterClass, index);
201     this.masterThreads.add(mt);
202     return mt;
203   }
204 
205   public JVMClusterUtil.MasterThread addMaster(
206       final Configuration c, final int index, User user)
207   throws IOException, InterruptedException {
208     return user.runAs(
209         new PrivilegedExceptionAction<JVMClusterUtil.MasterThread>() {
210           public JVMClusterUtil.MasterThread run() throws Exception {
211             return addMaster(c, index);
212           }
213         });
214   }
215 
216   /**
217    * @param serverNumber
218    * @return region server
219    */
220   public HRegionServer getRegionServer(int serverNumber) {
221     return regionThreads.get(serverNumber).getRegionServer();
222   }
223 
224   /**
225    * @return Read-only list of region server threads.
226    */
227   public List<JVMClusterUtil.RegionServerThread> getRegionServers() {
228     return Collections.unmodifiableList(this.regionThreads);
229   }
230 
231   /**
232    * @return List of running servers (Some servers may have been killed or
233    * aborted during lifetime of cluster; these servers are not included in this
234    * list).
235    */
236   public List<JVMClusterUtil.RegionServerThread> getLiveRegionServers() {
237     List<JVMClusterUtil.RegionServerThread> liveServers =
238       new ArrayList<JVMClusterUtil.RegionServerThread>();
239     List<RegionServerThread> list = getRegionServers();
240     for (JVMClusterUtil.RegionServerThread rst: list) {
241       if (rst.isAlive()) liveServers.add(rst);
242       else LOG.info("Not alive " + rst.getName());
243     }
244     return liveServers;
245   }
246 
247   /**
248    * Wait for the specified region server to stop
249    * Removes this thread from list of running threads.
250    * @param serverNumber
251    * @return Name of region server that just went down.
252    */
253   public String waitOnRegionServer(int serverNumber) {
254     JVMClusterUtil.RegionServerThread regionServerThread =
255       this.regionThreads.remove(serverNumber);
256     while (regionServerThread.isAlive()) {
257       try {
258         LOG.info("Waiting on " +
259           regionServerThread.getRegionServer().toString());
260         regionServerThread.join();
261       } catch (InterruptedException e) {
262         e.printStackTrace();
263       }
264     }
265     return regionServerThread.getName();
266   }
267 
268   /**
269    * Wait for the specified region server to stop
270    * Removes this thread from list of running threads.
271    * @param rst
272    * @return Name of region server that just went down.
273    */
274   public String waitOnRegionServer(JVMClusterUtil.RegionServerThread rst) {
275     while (rst.isAlive()) {
276       try {
277         LOG.info("Waiting on " +
278           rst.getRegionServer().toString());
279         rst.join();
280       } catch (InterruptedException e) {
281         e.printStackTrace();
282       }
283     }
284     for (int i=0;i<regionThreads.size();i++) {
285       if (regionThreads.get(i) == rst) {
286         regionThreads.remove(i);
287         break;
288       }
289     }
290     return rst.getName();
291   }
292 
293   /**
294    * @param serverNumber
295    * @return the HMaster thread
296    */
297   public HMaster getMaster(int serverNumber) {
298     return masterThreads.get(serverNumber).getMaster();
299   }
300 
301   /**
302    * Gets the current active master, if available.  If no active master, returns
303    * null.
304    * @return the HMaster for the active master
305    */
306   public HMaster getActiveMaster() {
307     for (JVMClusterUtil.MasterThread mt : masterThreads) {
308       if (mt.getMaster().isActiveMaster()) {
309         return mt.getMaster();
310       }
311     }
312     return null;
313   }
314 
315   /**
316    * @return Read-only list of master threads.
317    */
318   public List<JVMClusterUtil.MasterThread> getMasters() {
319     return Collections.unmodifiableList(this.masterThreads);
320   }
321 
322   /**
323    * @return List of running master servers (Some servers may have been killed
324    * or aborted during lifetime of cluster; these servers are not included in
325    * this list).
326    */
327   public List<JVMClusterUtil.MasterThread> getLiveMasters() {
328     List<JVMClusterUtil.MasterThread> liveServers =
329       new ArrayList<JVMClusterUtil.MasterThread>();
330     List<JVMClusterUtil.MasterThread> list = getMasters();
331     for (JVMClusterUtil.MasterThread mt: list) {
332       if (mt.isAlive()) {
333         liveServers.add(mt);
334       }
335     }
336     return liveServers;
337   }
338 
339   /**
340    * Wait for the specified master to stop
341    * Removes this thread from list of running threads.
342    * @param serverNumber
343    * @return Name of master that just went down.
344    */
345   public String waitOnMaster(int serverNumber) {
346     JVMClusterUtil.MasterThread masterThread =
347       this.masterThreads.remove(serverNumber);
348     while (masterThread.isAlive()) {
349       try {
350         LOG.info("Waiting on " +
351           masterThread.getMaster().getServerName().toString());
352         masterThread.join();
353       } catch (InterruptedException e) {
354         e.printStackTrace();
355       }
356     }
357     return masterThread.getName();
358   }
359 
360   /**
361    * Wait for the specified master to stop
362    * Removes this thread from list of running threads.
363    * @param masterThread
364    * @return Name of master that just went down.
365    */
366   public String waitOnMaster(JVMClusterUtil.MasterThread masterThread) {
367     while (masterThread.isAlive()) {
368       try {
369         LOG.info("Waiting on " +
370           masterThread.getMaster().getServerName().toString());
371         masterThread.join();
372       } catch (InterruptedException e) {
373         e.printStackTrace();
374       }
375     }
376     for (int i=0;i<masterThreads.size();i++) {
377       if (masterThreads.get(i) == masterThread) {
378         masterThreads.remove(i);
379         break;
380       }
381     }
382     return masterThread.getName();
383   }
384 
385   /**
386    * Wait for Mini HBase Cluster to shut down.
387    * Presumes you've already called {@link #shutdown()}.
388    */
389   public void join() {
390     if (this.regionThreads != null) {
391       for(Thread t: this.regionThreads) {
392         if (t.isAlive()) {
393           try {
394             Threads.threadDumpingIsAlive(t);
395           } catch (InterruptedException e) {
396             LOG.debug("Interrupted", e);
397           }
398         }
399       }
400     }
401     if (this.masterThreads != null) {
402       for (Thread t : this.masterThreads) {
403         if (t.isAlive()) {
404           try {
405             Threads.threadDumpingIsAlive(t);
406           } catch (InterruptedException e) {
407             LOG.debug("Interrupted", e);
408           }
409         }
410       }
411     }
412   }
413 
414   /**
415    * Start the cluster.
416    */
417   public void startup() throws IOException {
418     JVMClusterUtil.startup(this.masterThreads, this.regionThreads);
419   }
420 
421   /**
422    * Shut down the mini HBase cluster
423    */
424   public void shutdown() {
425     JVMClusterUtil.shutdown(this.masterThreads, this.regionThreads);
426   }
427 
428   /**
429    * @param c Configuration to check.
430    * @return True if a 'local' address in hbase.master value.
431    */
432   public static boolean isLocal(final Configuration c) {
433     final String mode = c.get(HConstants.CLUSTER_DISTRIBUTED);
434     return mode == null || mode.equals(HConstants.CLUSTER_IS_LOCAL);
435   }
436 
437   /**
438    * Test things basically work.
439    * @param args
440    * @throws IOException
441    */
442   public static void main(String[] args) throws IOException {
443     Configuration conf = HBaseConfiguration.create();
444     LocalHBaseCluster cluster = new LocalHBaseCluster(conf);
445     cluster.startup();
446     HBaseAdmin admin = new HBaseAdmin(conf);
447     HTableDescriptor htd =
448       new HTableDescriptor(Bytes.toBytes(cluster.getClass().getName()));
449     admin.createTable(htd);
450     cluster.shutdown();
451   }
452 }