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