View Javadoc

1   /**
2    * Copyright 2010 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.util;
21  
22  import java.io.IOException;
23  import java.lang.reflect.InvocationTargetException;
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.conf.Configuration;
29  import org.apache.hadoop.fs.FileSystem;
30  import org.apache.hadoop.hbase.master.HMaster;
31  import org.apache.hadoop.hbase.regionserver.HRegionServer;
32  import org.apache.hadoop.hbase.regionserver.ShutdownHook;
33  
34  /**
35   * Utility used running a cluster all in the one JVM.
36   */
37  public class JVMClusterUtil {
38    private static final Log LOG = LogFactory.getLog(JVMClusterUtil.class);
39  
40    /**
41     * Datastructure to hold RegionServer Thread and RegionServer instance
42     */
43    public static class RegionServerThread extends Thread {
44      private final HRegionServer regionServer;
45  
46      public RegionServerThread(final HRegionServer r, final int index) {
47        super(r, "RegionServer:" + index + ";" + r.getServerName());
48        this.regionServer = r;
49      }
50  
51      /** @return the region server */
52      public HRegionServer getRegionServer() {
53        return this.regionServer;
54      }
55  
56      /**
57       * Block until the region server has come online, indicating it is ready
58       * to be used.
59       */
60      public void waitForServerOnline() {
61        // The server is marked online after the init method completes inside of
62        // the HRS#run method.  HRS#init can fail for whatever region.  In those
63        // cases, we'll jump out of the run without setting online flag.  Check
64        // stopRequested so we don't wait here a flag that will never be flipped.
65        regionServer.waitForServerOnline();
66      }
67    }
68  
69    /**
70     * Creates a {@link RegionServerThread}.
71     * Call 'start' on the returned thread to make it run.
72     * @param c Configuration to use.
73     * @param hrsc Class to create.
74     * @param index Used distinguishing the object returned.
75     * @throws IOException
76     * @return Region server added.
77     */
78    public static JVMClusterUtil.RegionServerThread createRegionServerThread(
79        final Configuration c, final Class<? extends HRegionServer> hrsc,
80        final int index)
81    throws IOException {
82      HRegionServer server;
83      try {
84        server = hrsc.getConstructor(Configuration.class).newInstance(c);
85      } catch (InvocationTargetException ite) {
86        Throwable target = ite.getTargetException();
87        throw new RuntimeException("Failed construction of RegionServer: " +
88          hrsc.toString() + ((target.getCause() != null)?
89            target.getCause().getMessage(): ""), target);
90      } catch (Exception e) {
91        IOException ioe = new IOException();
92        ioe.initCause(e);
93        throw ioe;
94      }
95      return new JVMClusterUtil.RegionServerThread(server, index);
96    }
97  
98  
99    /**
100    * Datastructure to hold Master Thread and Master instance
101    */
102   public static class MasterThread extends Thread {
103     private final HMaster master;
104 
105     public MasterThread(final HMaster m, final int index) {
106       super(m, "Master:" + index + ";" + m.getServerName());
107       this.master = m;
108     }
109 
110     /** @return the master */
111     public HMaster getMaster() {
112       return this.master;
113     }
114   }
115 
116   /**
117    * Creates a {@link MasterThread}.
118    * Call 'start' on the returned thread to make it run.
119    * @param c Configuration to use.
120    * @param hmc Class to create.
121    * @param index Used distinguishing the object returned.
122    * @throws IOException
123    * @return Master added.
124    */
125   public static JVMClusterUtil.MasterThread createMasterThread(
126       final Configuration c, final Class<? extends HMaster> hmc,
127       final int index)
128   throws IOException {
129     HMaster server;
130     try {
131       server = hmc.getConstructor(Configuration.class).newInstance(c);
132     } catch (InvocationTargetException ite) {
133       Throwable target = ite.getTargetException();
134       throw new RuntimeException("Failed construction of Master: " +
135         hmc.toString() + ((target.getCause() != null)?
136           target.getCause().getMessage(): ""), target);
137     } catch (Exception e) {
138       IOException ioe = new IOException();
139       ioe.initCause(e);
140       throw ioe;
141     }
142     return new JVMClusterUtil.MasterThread(server, index);
143   }
144 
145   private static JVMClusterUtil.MasterThread findActiveMaster(
146     List<JVMClusterUtil.MasterThread> masters) {
147     for (JVMClusterUtil.MasterThread t : masters) {
148       if (t.master.isActiveMaster()) {
149         return t;
150       }
151     }
152 
153     return null;
154   }
155 
156   /**
157    * Start the cluster.  Waits until there is a primary master initialized
158    * and returns its address.
159    * @param masters
160    * @param regionservers
161    * @return Address to use contacting primary master.
162    */
163   public static String startup(final List<JVMClusterUtil.MasterThread> masters,
164       final List<JVMClusterUtil.RegionServerThread> regionservers) throws IOException {
165 
166     if (masters == null || masters.isEmpty()) {
167       return null;
168     }
169 
170     for (JVMClusterUtil.MasterThread t : masters) {
171       t.start();
172     }
173 
174     // Wait for an active master
175     //  having an active master before starting the region threads allows
176     //  then to succeed on their connection to master
177     long startTime = System.currentTimeMillis();
178     while (findActiveMaster(masters) == null) {
179       try {
180         Thread.sleep(100);
181       } catch (InterruptedException ignored) {
182       }
183       if (System.currentTimeMillis() > startTime + 30000) {
184         throw new RuntimeException("Master not active after 30 seconds");
185       }
186     }
187 
188     if (regionservers != null) {
189       for (JVMClusterUtil.RegionServerThread t: regionservers) {
190         HRegionServer hrs = t.getRegionServer();
191         ShutdownHook.install(hrs.getConfiguration(), FileSystem.get(hrs
192                 .getConfiguration()), hrs, t);
193         t.start();
194       }
195     }
196 
197     // Wait for an active master to be initialized (implies being master)
198     //  with this, when we return the cluster is complete
199     startTime = System.currentTimeMillis();
200     while (true) {
201       JVMClusterUtil.MasterThread t = findActiveMaster(masters);
202       if (t != null && t.master.isInitialized()) {
203         return t.master.getServerName().toString();
204       }
205       if (System.currentTimeMillis() > startTime + 200000) {
206         throw new RuntimeException("Master not initialized after 200 seconds");
207       }
208       try {
209         Thread.sleep(100);
210       } catch (InterruptedException ignored) {
211         // Keep waiting
212       }
213     }
214   }
215 
216   /**
217    * @param masters
218    * @param regionservers
219    */
220   public static void shutdown(final List<MasterThread> masters,
221       final List<RegionServerThread> regionservers) {
222     LOG.debug("Shutting down HBase Cluster");
223     if (masters != null) {
224       // Do backups first.
225       JVMClusterUtil.MasterThread activeMaster = null;
226       for (JVMClusterUtil.MasterThread t : masters) {
227         if (!t.master.isActiveMaster()) {
228           t.master.stopMaster();
229         } else {
230           activeMaster = t;
231         }
232       }
233       // Do active after.
234       if (activeMaster != null) activeMaster.master.shutdown();
235     }
236     // regionServerThreads can never be null because they are initialized when
237     // the class is constructed.
238       for(RegionServerThread t: regionservers) {
239         if (t.isAlive()) {
240           try {
241             t.getRegionServer().stop("Shutdown requested");
242             t.join();
243           } catch (InterruptedException e) {
244             // continue
245           }
246         }
247       }
248     if (masters != null) {
249       for (JVMClusterUtil.MasterThread t : masters) {
250         while (t.master.isAlive()) {
251           try {
252             // The below has been replaced to debug sometime hangs on end of
253             // tests.
254             // this.master.join():
255             Threads.threadDumpingIsAlive(t.master.getThread());
256           } catch(InterruptedException e) {
257             // continue
258           }
259         }
260       }
261     }
262     LOG.info("Shutdown of " +
263       ((masters != null) ? masters.size() : "0") + " master(s) and " +
264       ((regionservers != null) ? regionservers.size() : "0") +
265       " regionserver(s) complete");
266   }
267 }