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.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.conf.Configuration;
30  import org.apache.hadoop.hbase.client.HBaseAdmin;
31  import org.apache.hadoop.hbase.regionserver.HRegionServer;
32  import org.apache.hadoop.hbase.util.Bytes;
33  import java.util.concurrent.CopyOnWriteArrayList;
34  import org.apache.hadoop.hbase.master.HMaster;
35  import org.apache.hadoop.hbase.util.JVMClusterUtil;
36  
37  /**
38   * This class creates a single process HBase cluster. One thread is created for
39   * a master and one per region server.
40   *
41   * Call {@link #startup()} to start the cluster running and {@link #shutdown()}
42   * to close it all down. {@link #join} the cluster is you want to wait on
43   * shutdown completion.
44   *
45   * <p>Runs master on port 60000 by default.  Because we can't just kill the
46   * process -- not till HADOOP-1700 gets fixed and even then.... -- we need to
47   * be able to find the master with a remote client to run shutdown.  To use a
48   * port other than 60000, set the hbase.master to a value of 'local:PORT':
49   * that is 'local', not 'localhost', and the port number the master should use
50   * instead of 60000.
51   *
52   * <p>To make 'local' mode more responsive, make values such as
53   * <code>hbase.regionserver.msginterval</code>,
54   * <code>hbase.master.meta.thread.rescanfrequency</code>, and
55   * <code>hbase.server.thread.wakefrequency</code> a second or less.
56   */
57  public class LocalHBaseCluster {
58    static final Log LOG = LogFactory.getLog(LocalHBaseCluster.class);
59    private final HMaster master;
60    private final List<JVMClusterUtil.RegionServerThread> regionThreads;
61    private final static int DEFAULT_NO = 1;
62    /** local mode */
63    public static final String LOCAL = "local";
64    /** 'local:' */
65    public static final String LOCAL_COLON = LOCAL + ":";
66    private final Configuration conf;
67    private final Class<? extends HRegionServer> regionServerClass;
68  
69    /**
70     * Constructor.
71     * @param conf
72     * @throws IOException
73     */
74    public LocalHBaseCluster(final Configuration conf)
75    throws IOException {
76      this(conf, DEFAULT_NO);
77    }
78  
79    /**
80     * Constructor.
81     * @param conf Configuration to use.  Post construction has the master's
82     * address.
83     * @param noRegionServers Count of regionservers to start.
84     * @throws IOException
85     */
86    public LocalHBaseCluster(final Configuration conf, final int noRegionServers)
87    throws IOException {
88      this(conf, noRegionServers, HMaster.class, getRegionServerImplementation(conf));
89    }
90  
91    @SuppressWarnings("unchecked")
92    private static Class<? extends HRegionServer> getRegionServerImplementation(final Configuration conf) {
93      return (Class<? extends HRegionServer>)conf.getClass(HConstants.REGION_SERVER_IMPL,
94         HRegionServer.class);
95    }
96  
97    /**
98     * Constructor.
99     * @param conf Configuration to use.  Post construction has the master's
100    * address.
101    * @param noRegionServers Count of regionservers to start.
102    * @param masterClass
103    * @throws IOException
104    */
105   @SuppressWarnings("unchecked")
106   public LocalHBaseCluster(final Configuration conf,
107     final int noRegionServers, final Class<? extends HMaster> masterClass,
108     final Class<? extends HRegionServer> regionServerClass)
109   throws IOException {
110     this.conf = conf;
111     // Create the master
112     this.master = HMaster.constructMaster(masterClass, conf);
113     // Start the HRegionServers.  Always have region servers come up on
114     // port '0' so there won't be clashes over default port as unit tests
115     // start/stop ports at different times during the life of the test.
116     conf.set(HConstants.REGIONSERVER_PORT, "0");
117     this.regionThreads =
118       new CopyOnWriteArrayList<JVMClusterUtil.RegionServerThread>();
119     this.regionServerClass =
120       (Class<? extends HRegionServer>)conf.getClass(HConstants.REGION_SERVER_IMPL,
121        regionServerClass);
122     for (int i = 0; i < noRegionServers; i++) {
123       addRegionServer(i);
124     }
125   }
126 
127   public JVMClusterUtil.RegionServerThread addRegionServer() throws IOException {
128     return addRegionServer(this.regionThreads.size());
129   }
130 
131   public JVMClusterUtil.RegionServerThread addRegionServer(final int index)
132   throws IOException {
133     JVMClusterUtil.RegionServerThread rst = JVMClusterUtil.createRegionServerThread(this.conf,
134         this.regionServerClass, index);
135     this.regionThreads.add(rst);
136     return rst;
137   }
138 
139   /**
140    * @param serverNumber
141    * @return region server
142    */
143   public HRegionServer getRegionServer(int serverNumber) {
144     return regionThreads.get(serverNumber).getRegionServer();
145   }
146 
147   /**
148    * @return the HMaster thread
149    */
150   public HMaster getMaster() {
151     return this.master;
152   }
153 
154   /**
155    * @return Read-only list of region server threads.
156    */
157   public List<JVMClusterUtil.RegionServerThread> getRegionServers() {
158     return Collections.unmodifiableList(this.regionThreads);
159   }
160 
161   /**
162    * @return List of running servers (Some servers may have been killed or
163    * aborted during lifetime of cluster; these servers are not included in this
164    * list).
165    */
166   public List<JVMClusterUtil.RegionServerThread> getLiveRegionServers() {
167     List<JVMClusterUtil.RegionServerThread> liveServers =
168       new ArrayList<JVMClusterUtil.RegionServerThread>();
169     for (JVMClusterUtil.RegionServerThread rst: getRegionServers()) {
170       if (rst.isAlive()) liveServers.add(rst);
171     }
172     return liveServers;
173   }
174 
175   /**
176    * Wait for the specified region server to stop
177    * Removes this thread from list of running threads.
178    * @param serverNumber
179    * @return Name of region server that just went down.
180    */
181   public String waitOnRegionServer(int serverNumber) {
182     JVMClusterUtil.RegionServerThread regionServerThread =
183       this.regionThreads.remove(serverNumber);
184     while (regionServerThread.isAlive()) {
185       try {
186         LOG.info("Waiting on " +
187           regionServerThread.getRegionServer().getHServerInfo().toString());
188         regionServerThread.join();
189       } catch (InterruptedException e) {
190         e.printStackTrace();
191       } catch (IOException e) {
192         e.printStackTrace();
193       }
194     }
195     return regionServerThread.getName();
196   }
197 
198   /**
199    * Wait for Mini HBase Cluster to shut down.
200    * Presumes you've already called {@link #shutdown()}.
201    */
202   public void join() {
203     if (this.regionThreads != null) {
204         for(Thread t: this.regionThreads) {
205           if (t.isAlive()) {
206             try {
207               t.join();
208           } catch (InterruptedException e) {
209             // continue
210           }
211         }
212       }
213     }
214     if (this.master != null && this.master.isAlive()) {
215       try {
216         this.master.join();
217       } catch(InterruptedException e) {
218         // continue
219       }
220     }
221   }
222 
223   /**
224    * Start the cluster.
225    */
226   public void startup() {
227     JVMClusterUtil.startup(this.master, this.regionThreads);
228   }
229 
230   /**
231    * Shut down the mini HBase cluster
232    */
233   public void shutdown() {
234     JVMClusterUtil.shutdown(this.master, this.regionThreads);
235   }
236 
237   /**
238    * @param c Configuration to check.
239    * @return True if a 'local' address in hbase.master value.
240    */
241   public static boolean isLocal(final Configuration c) {
242     final String mode = c.get(HConstants.CLUSTER_DISTRIBUTED);
243     return mode == null || mode.equals(HConstants.CLUSTER_IS_LOCAL);
244   }
245 
246   /**
247    * Test things basically work.
248    * @param args
249    * @throws IOException
250    */
251   public static void main(String[] args) throws IOException {
252     Configuration conf = HBaseConfiguration.create();
253     LocalHBaseCluster cluster = new LocalHBaseCluster(conf);
254     cluster.startup();
255     HBaseAdmin admin = new HBaseAdmin(conf);
256     HTableDescriptor htd =
257       new HTableDescriptor(Bytes.toBytes(cluster.getClass().getName()));
258     admin.createTable(htd);
259     cluster.shutdown();
260   }
261 }