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.client;
21  
22  import java.io.IOException;
23  import java.util.Arrays;
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.hbase.Abortable;
30  import org.apache.hadoop.hbase.ClusterStatus;
31  import org.apache.hadoop.hbase.HBaseConfiguration;
32  import org.apache.hadoop.hbase.HColumnDescriptor;
33  import org.apache.hadoop.hbase.HConstants;
34  import org.apache.hadoop.hbase.HRegionInfo;
35  import org.apache.hadoop.hbase.HRegionLocation;
36  import org.apache.hadoop.hbase.HServerAddress;
37  import org.apache.hadoop.hbase.HTableDescriptor;
38  import org.apache.hadoop.hbase.MasterNotRunningException;
39  import org.apache.hadoop.hbase.NotServingRegionException;
40  import org.apache.hadoop.hbase.RegionException;
41  import org.apache.hadoop.hbase.RemoteExceptionHandler;
42  import org.apache.hadoop.hbase.TableExistsException;
43  import org.apache.hadoop.hbase.UnknownRegionException;
44  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
45  import org.apache.hadoop.hbase.catalog.CatalogTracker;
46  import org.apache.hadoop.hbase.catalog.MetaReader;
47  import org.apache.hadoop.hbase.ipc.HMasterInterface;
48  import org.apache.hadoop.hbase.ipc.HRegionInterface;
49  import org.apache.hadoop.hbase.util.Bytes;
50  import org.apache.hadoop.hbase.util.Pair;
51  import org.apache.hadoop.ipc.RemoteException;
52  import org.apache.hadoop.util.StringUtils;
53  
54  /**
55   * Provides an interface to manage HBase database table metadata + general 
56   * administrative functions.  Use HBaseAdmin to create, drop, list, enable and 
57   * disable tables. Use it also to add and drop table column families. 
58   * 
59   * <p>See {@link HTable} to add, update, and delete data from an individual table.
60   * <p>Currently HBaseAdmin instances are not expected to be long-lived.  For
61   * example, an HBaseAdmin instance will not ride over a Master restart.
62   */
63  public class HBaseAdmin implements Abortable {
64    private final Log LOG = LogFactory.getLog(this.getClass().getName());
65  //  private final HConnection connection;
66    final HConnection connection;
67    private volatile Configuration conf;
68    private final long pause;
69    private final int numRetries;
70    // Some operations can take a long time such as disable of big table.
71    // numRetries is for 'normal' stuff... Mutliply by this factor when
72    // want to wait a long time.
73    private final int retryLongerMultiplier;
74  
75    /**
76     * Constructor
77     *
78     * @param conf Configuration object
79     * @throws MasterNotRunningException if the master is not running
80     * @throws ZooKeeperConnectionException if unable to connect to zookeeper
81     */
82    public HBaseAdmin(Configuration conf)
83    throws MasterNotRunningException, ZooKeeperConnectionException {
84      this.connection = HConnectionManager.getConnection(conf);
85      this.conf = conf;
86      this.pause = conf.getLong("hbase.client.pause", 1000);
87      this.numRetries = conf.getInt("hbase.client.retries.number", 10);
88      this.retryLongerMultiplier = conf.getInt("hbase.client.retries.longer.multiplier", 10);
89      this.connection.getMaster();
90    }
91  
92    /**
93     * @return A new CatalogTracker instance; call {@link #cleanupCatalogTracker(CatalogTracker)}
94     * to cleanup the returned catalog tracker.
95     * @throws ZooKeeperConnectionException
96     * @throws IOException
97     * @see #cleanupCatalogTracker(CatalogTracker);
98     */
99    private synchronized CatalogTracker getCatalogTracker()
100   throws ZooKeeperConnectionException, IOException {
101     CatalogTracker ct = null;
102     try {
103       HConnection connection =
104         HConnectionManager.getConnection(new Configuration(this.conf));
105       ct = new CatalogTracker(connection);
106       ct.start();
107     } catch (InterruptedException e) {
108       // Let it out as an IOE for now until we redo all so tolerate IEs
109       Thread.currentThread().interrupt();
110       throw new IOException("Interrupted", e);
111     }
112     return ct;
113   }
114 
115   private void cleanupCatalogTracker(final CatalogTracker ct) {
116     ct.stop();
117     HConnectionManager.deleteConnection(ct.getConnection().getConfiguration(), true);
118   }
119 
120   @Override
121   public void abort(String why, Throwable e) {
122     // Currently does nothing but throw the passed message and exception
123     throw new RuntimeException(why, e);
124   }
125 
126   /** @return HConnection used by this object. */
127   public HConnection getConnection() {
128     return connection;
129   }
130 
131   /**
132    * Get a connection to the currently set master.
133    * @return proxy connection to master server for this instance
134    * @throws MasterNotRunningException if the master is not running
135    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
136    */
137   public HMasterInterface getMaster()
138   throws MasterNotRunningException, ZooKeeperConnectionException {
139     return this.connection.getMaster();
140   }
141 
142   /** @return - true if the master server is running
143    * @throws ZooKeeperConnectionException
144    * @throws MasterNotRunningException */
145   public boolean isMasterRunning()
146   throws MasterNotRunningException, ZooKeeperConnectionException {
147     return this.connection.isMasterRunning();
148   }
149 
150   /**
151    * @param tableName Table to check.
152    * @return True if table exists already.
153    * @throws IOException 
154    */
155   public boolean tableExists(final String tableName)
156   throws IOException {
157     boolean b = false;
158     CatalogTracker ct = getCatalogTracker();
159     try {
160       b = MetaReader.tableExists(ct, tableName);
161     } finally {
162       cleanupCatalogTracker(ct);
163     }
164     return b;
165   }
166 
167   /**
168    * @param tableName Table to check.
169    * @return True if table exists already.
170    * @throws IOException 
171    */
172   public boolean tableExists(final byte [] tableName)
173   throws IOException {
174     return tableExists(Bytes.toString(tableName));
175   }
176 
177   /**
178    * List all the userspace tables.  In other words, scan the META table.
179    *
180    * If we wanted this to be really fast, we could implement a special
181    * catalog table that just contains table names and their descriptors.
182    * Right now, it only exists as part of the META table's region info.
183    *
184    * @return - returns an array of HTableDescriptors
185    * @throws IOException if a remote or network exception occurs
186    */
187   public HTableDescriptor[] listTables() throws IOException {
188     return this.connection.listTables();
189   }
190 
191 
192   /**
193    * Method for getting the tableDescriptor
194    * @param tableName as a byte []
195    * @return the tableDescriptor
196    * @throws IOException if a remote or network exception occurs
197    */
198   public HTableDescriptor getTableDescriptor(final byte [] tableName)
199   throws IOException {
200     return this.connection.getHTableDescriptor(tableName);
201   }
202 
203   private long getPauseTime(int tries) {
204     int triesCount = tries;
205     if (triesCount >= HConstants.RETRY_BACKOFF.length) {
206       triesCount = HConstants.RETRY_BACKOFF.length - 1;
207     }
208     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
209   }
210 
211   /**
212    * Creates a new table.
213    * Synchronous operation.
214    *
215    * @param desc table descriptor for table
216    *
217    * @throws IllegalArgumentException if the table name is reserved
218    * @throws MasterNotRunningException if master is not running
219    * @throws TableExistsException if table already exists (If concurrent
220    * threads, the table may have been created between test-for-existence
221    * and attempt-at-creation).
222    * @throws IOException if a remote or network exception occurs
223    */
224   public void createTable(HTableDescriptor desc)
225   throws IOException {
226     createTable(desc, null);
227   }
228 
229   /**
230    * Creates a new table with the specified number of regions.  The start key
231    * specified will become the end key of the first region of the table, and
232    * the end key specified will become the start key of the last region of the
233    * table (the first region has a null start key and the last region has a
234    * null end key).
235    *
236    * BigInteger math will be used to divide the key range specified into
237    * enough segments to make the required number of total regions.
238    *
239    * Synchronous operation.
240    *
241    * @param desc table descriptor for table
242    * @param startKey beginning of key range
243    * @param endKey end of key range
244    * @param numRegions the total number of regions to create
245    *
246    * @throws IllegalArgumentException if the table name is reserved
247    * @throws MasterNotRunningException if master is not running
248    * @throws TableExistsException if table already exists (If concurrent
249    * threads, the table may have been created between test-for-existence
250    * and attempt-at-creation).
251    * @throws IOException
252    */
253   public void createTable(HTableDescriptor desc, byte [] startKey,
254       byte [] endKey, int numRegions)
255   throws IOException {
256     HTableDescriptor.isLegalTableName(desc.getName());
257     if(numRegions < 3) {
258       throw new IllegalArgumentException("Must create at least three regions");
259     } else if(Bytes.compareTo(startKey, endKey) >= 0) {
260       throw new IllegalArgumentException("Start key must be smaller than end key");
261     }
262     byte [][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
263     if(splitKeys == null || splitKeys.length != numRegions - 1) {
264       throw new IllegalArgumentException("Unable to split key range into enough regions");
265     }
266     createTable(desc, splitKeys);
267   }
268 
269   /**
270    * Creates a new table with an initial set of empty regions defined by the
271    * specified split keys.  The total number of regions created will be the
272    * number of split keys plus one (the first region has a null start key and
273    * the last region has a null end key).
274    * Synchronous operation.
275    *
276    * @param desc table descriptor for table
277    * @param splitKeys array of split keys for the initial regions of the table
278    *
279    * @throws IllegalArgumentException if the table name is reserved
280    * @throws MasterNotRunningException if master is not running
281    * @throws TableExistsException if table already exists (If concurrent
282    * threads, the table may have been created between test-for-existence
283    * and attempt-at-creation).
284    * @throws IOException
285    */
286   public void createTable(HTableDescriptor desc, byte [][] splitKeys)
287   throws IOException {
288     HTableDescriptor.isLegalTableName(desc.getName());
289     if(splitKeys != null && splitKeys.length > 1) {
290       Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
291       // Verify there are no duplicate split keys
292       byte [] lastKey = null;
293       for(byte [] splitKey : splitKeys) {
294         if(lastKey != null && Bytes.equals(splitKey, lastKey)) {
295           throw new IllegalArgumentException("All split keys must be unique, " +
296             "found duplicate: " + Bytes.toStringBinary(splitKey) +
297             ", " + Bytes.toStringBinary(lastKey));
298         }
299         lastKey = splitKey;
300       }
301     }
302     createTableAsync(desc, splitKeys);
303     for (int tries = 0; tries < numRetries; tries++) {
304       try {
305         // Wait for new table to come on-line
306         connection.locateRegion(desc.getName(), HConstants.EMPTY_START_ROW);
307         break;
308 
309       } catch (RegionException e) {
310         if (tries == numRetries - 1) {
311           // Ran out of tries
312           throw e;
313         }
314       }
315       try {
316         Thread.sleep(getPauseTime(tries));
317       } catch (InterruptedException e) {
318         // Just continue; ignore the interruption.
319       }
320     }
321   }
322 
323   /**
324    * Creates a new table but does not block and wait for it to come online.
325    * Asynchronous operation.
326    *
327    * @param desc table descriptor for table
328    *
329    * @throws IllegalArgumentException Bad table name.
330    * @throws MasterNotRunningException if master is not running
331    * @throws TableExistsException if table already exists (If concurrent
332    * threads, the table may have been created between test-for-existence
333    * and attempt-at-creation).
334    * @throws IOException
335    */
336   public void createTableAsync(HTableDescriptor desc, byte [][] splitKeys)
337   throws IOException {
338     HTableDescriptor.isLegalTableName(desc.getName());
339     try {
340       getMaster().createTable(desc, splitKeys);
341     } catch (RemoteException e) {
342       throw e.unwrapRemoteException();
343     }
344   }
345 
346   /**
347    * Deletes a table.
348    * Synchronous operation.
349    *
350    * @param tableName name of table to delete
351    * @throws IOException if a remote or network exception occurs
352    */
353   public void deleteTable(final String tableName) throws IOException {
354     deleteTable(Bytes.toBytes(tableName));
355   }
356 
357   /**
358    * Deletes a table.
359    * Synchronous operation.
360    *
361    * @param tableName name of table to delete
362    * @throws IOException if a remote or network exception occurs
363    */
364   public void deleteTable(final byte [] tableName) throws IOException {
365     isMasterRunning();
366     HTableDescriptor.isLegalTableName(tableName);
367     HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName);
368     try {
369       getMaster().deleteTable(tableName);
370     } catch (RemoteException e) {
371       throw RemoteExceptionHandler.decodeRemoteException(e);
372     }
373     // Wait until all regions deleted
374     HRegionInterface server =
375       connection.getHRegionConnection(firstMetaServer.getServerAddress());
376     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
377       long scannerId = -1L;
378       try {
379 
380         Scan scan = MetaReader.getScanForTableName(tableName);
381         scan.addColumn(HConstants.CATALOG_FAMILY,
382             HConstants.REGIONINFO_QUALIFIER);
383         scannerId = server.openScanner(
384           firstMetaServer.getRegionInfo().getRegionName(), scan);
385         // Get a batch at a time.
386         Result values = server.next(scannerId);
387         if (values == null) {
388           break;
389         }
390       } catch (IOException ex) {
391         if(tries == numRetries - 1) {           // no more tries left
392           if (ex instanceof RemoteException) {
393             ex = RemoteExceptionHandler.decodeRemoteException((RemoteException) ex);
394           }
395           throw ex;
396         }
397       } finally {
398         if (scannerId != -1L) {
399           try {
400             server.close(scannerId);
401           } catch (Exception ex) {
402             LOG.warn(ex);
403           }
404         }
405       }
406       try {
407         Thread.sleep(getPauseTime(tries));
408       } catch (InterruptedException e) {
409         // continue
410       }
411     }
412     // Delete cached information to prevent clients from using old locations
413     this.connection.clearRegionCache(tableName);
414     LOG.info("Deleted " + Bytes.toString(tableName));
415   }
416 
417   public void enableTable(final String tableName)
418   throws IOException {
419     enableTable(Bytes.toBytes(tableName));
420   }
421 
422   /**
423    * Enable a table.  May timeout.  Use {@link #enableTableAsync(byte[])}
424    * and {@link #isTableEnabled(byte[])} instead.
425    * @param tableName name of the table
426    * @throws IOException if a remote or network exception occurs
427    * @see #isTableEnabled(byte[])
428    * @see #disableTable(byte[])
429    * @see #enableTableAsync(byte[])
430    */
431   public void enableTable(final byte [] tableName)
432   throws IOException {
433     enableTableAsync(tableName);
434  
435     // Wait until all regions are enabled
436     boolean enabled = false;
437     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
438       enabled = isTableEnabled(tableName);
439       if (enabled) {
440         break;
441       }
442       long sleep = getPauseTime(tries);
443       if (LOG.isDebugEnabled()) {
444         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
445           "enabled in " + Bytes.toString(tableName));
446       }
447       try {
448         Thread.sleep(sleep);
449       } catch (InterruptedException e) {
450         Thread.currentThread().interrupt();
451         // Do this conversion rather than let it out because do not want to
452         // change the method signature.
453         throw new IOException("Interrupted", e);
454       }
455     }
456     if (!enabled) {
457       throw new IOException("Unable to enable table " +
458         Bytes.toString(tableName));
459     }
460     LOG.info("Enabled table " + Bytes.toString(tableName));
461   }
462 
463   public void enableTableAsync(final String tableName)
464   throws IOException {
465     enableTableAsync(Bytes.toBytes(tableName));
466   }
467 
468   /**
469    * Brings a table on-line (enables it).  Method returns immediately though
470    * enable of table may take some time to complete, especially if the table
471    * is large (All regions are opened as part of enabling process).  Check
472    * {@link #isTableEnabled(byte[])} to learn when table is fully online.  If
473    * table is taking too long to online, check server logs.
474    * @param tableName
475    * @throws IOException
476    * @since 0.90.0
477    */
478   public void enableTableAsync(final byte [] tableName)
479   throws IOException {
480     isMasterRunning();
481     try {
482       getMaster().enableTable(tableName);
483     } catch (RemoteException e) {
484       throw e.unwrapRemoteException();
485     }
486     LOG.info("Started enable of " + Bytes.toString(tableName));
487   }
488 
489   public void disableTableAsync(final String tableName) throws IOException {
490     disableTableAsync(Bytes.toBytes(tableName));
491   }
492 
493   /**
494    * Starts the disable of a table.  If it is being served, the master
495    * will tell the servers to stop serving it.  This method returns immediately.
496    * The disable of a table can take some time if the table is large (all
497    * regions are closed as part of table disable operation).
498    * Call {@link #isTableDisabled(byte[])} to check for when disable completes.
499    * If table is taking too long to online, check server logs.
500    * @param tableName name of table
501    * @throws IOException if a remote or network exception occurs
502    * @see #isTableDisabled(byte[])
503    * @see #isTableEnabled(byte[])
504    * @since 0.90.0
505    */
506   public void disableTableAsync(final byte [] tableName) throws IOException {
507     isMasterRunning();
508     try {
509       getMaster().disableTable(tableName);
510     } catch (RemoteException e) {
511       throw e.unwrapRemoteException();
512     }
513     LOG.info("Started disable of " + Bytes.toString(tableName));
514   }
515 
516   public void disableTable(final String tableName)
517   throws IOException {
518     disableTable(Bytes.toBytes(tableName));
519   }
520 
521   /**
522    * Disable table and wait on completion.  May timeout eventually.  Use
523    * {@link #disableTableAsync(byte[])} and {@link #isTableDisabled(String)}
524    * instead.
525    * @param tableName
526    * @throws IOException
527    */
528   public void disableTable(final byte [] tableName)
529   throws IOException {
530     disableTableAsync(tableName);
531     // Wait until table is disabled
532     boolean disabled = false;
533     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
534       disabled = isTableDisabled(tableName);
535       if (disabled) {
536         break;
537       }
538       long sleep = getPauseTime(tries);
539       if (LOG.isDebugEnabled()) {
540         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
541           "disabled in " + Bytes.toString(tableName));
542       }
543       try {
544         Thread.sleep(sleep);
545       } catch (InterruptedException e) {
546         // Do this conversion rather than let it out because do not want to
547         // change the method signature.
548         Thread.currentThread().interrupt();
549         throw new IOException("Interrupted", e);
550       }
551     }
552     if (!disabled) {
553       throw new RegionException("Retries exhausted, it took too long to wait"+
554         " for the table " + Bytes.toString(tableName) + " to be disabled.");
555     }
556     LOG.info("Disabled " + Bytes.toString(tableName));
557   }
558 
559   /**
560    * @param tableName name of table to check
561    * @return true if table is on-line
562    * @throws IOException if a remote or network exception occurs
563    */
564   public boolean isTableEnabled(String tableName) throws IOException {
565     return isTableEnabled(Bytes.toBytes(tableName));
566   }
567   /**
568    * @param tableName name of table to check
569    * @return true if table is on-line
570    * @throws IOException if a remote or network exception occurs
571    */
572   public boolean isTableEnabled(byte[] tableName) throws IOException {
573     return connection.isTableEnabled(tableName);
574   }
575 
576   /**
577    * @param tableName name of table to check
578    * @return true if table is off-line
579    * @throws IOException if a remote or network exception occurs
580    */
581   public boolean isTableDisabled(final String tableName) throws IOException {
582     return isTableDisabled(Bytes.toBytes(tableName));
583   }
584 
585   /**
586    * @param tableName name of table to check
587    * @return true if table is off-line
588    * @throws IOException if a remote or network exception occurs
589    */
590   public boolean isTableDisabled(byte[] tableName) throws IOException {
591     return connection.isTableDisabled(tableName);
592   }
593 
594   /**
595    * @param tableName name of table to check
596    * @return true if all regions of the table are available
597    * @throws IOException if a remote or network exception occurs
598    */
599   public boolean isTableAvailable(byte[] tableName) throws IOException {
600     return connection.isTableAvailable(tableName);
601   }
602 
603   /**
604    * @param tableName name of table to check
605    * @return true if all regions of the table are available
606    * @throws IOException if a remote or network exception occurs
607    */
608   public boolean isTableAvailable(String tableName) throws IOException {
609     return connection.isTableAvailable(Bytes.toBytes(tableName));
610   }
611 
612   /**
613    * Add a column to an existing table.
614    * Asynchronous operation.
615    *
616    * @param tableName name of the table to add column to
617    * @param column column descriptor of column to be added
618    * @throws IOException if a remote or network exception occurs
619    */
620   public void addColumn(final String tableName, HColumnDescriptor column)
621   throws IOException {
622     addColumn(Bytes.toBytes(tableName), column);
623   }
624 
625   /**
626    * Add a column to an existing table.
627    * Asynchronous operation.
628    *
629    * @param tableName name of the table to add column to
630    * @param column column descriptor of column to be added
631    * @throws IOException if a remote or network exception occurs
632    */
633   public void addColumn(final byte [] tableName, HColumnDescriptor column)
634   throws IOException {
635     HTableDescriptor.isLegalTableName(tableName);
636     try {
637       getMaster().addColumn(tableName, column);
638     } catch (RemoteException e) {
639       throw RemoteExceptionHandler.decodeRemoteException(e);
640     }
641   }
642 
643   /**
644    * Delete a column from a table.
645    * Asynchronous operation.
646    *
647    * @param tableName name of table
648    * @param columnName name of column to be deleted
649    * @throws IOException if a remote or network exception occurs
650    */
651   public void deleteColumn(final String tableName, final String columnName)
652   throws IOException {
653     deleteColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName));
654   }
655 
656   /**
657    * Delete a column from a table.
658    * Asynchronous operation.
659    *
660    * @param tableName name of table
661    * @param columnName name of column to be deleted
662    * @throws IOException if a remote or network exception occurs
663    */
664   public void deleteColumn(final byte [] tableName, final byte [] columnName)
665   throws IOException {
666     try {
667       getMaster().deleteColumn(tableName, columnName);
668     } catch (RemoteException e) {
669       throw RemoteExceptionHandler.decodeRemoteException(e);
670     }
671   }
672 
673   /**
674    * Modify an existing column family on a table.
675    * Asynchronous operation.
676    *
677    * @param tableName name of table
678    * @param columnName name of column to be modified
679    * @param descriptor new column descriptor to use
680    * @throws IOException if a remote or network exception occurs
681    * @deprecated The <code>columnName</code> is redundant. Use {@link #addColumn(String, HColumnDescriptor)}
682    */
683   public void modifyColumn(final String tableName, final String columnName,
684       HColumnDescriptor descriptor)
685   throws IOException {
686     modifyColumn(tableName,  descriptor);
687   }
688 
689   /**
690    * Modify an existing column family on a table.
691    * Asynchronous operation.
692    *
693    * @param tableName name of table
694    * @param descriptor new column descriptor to use
695    * @throws IOException if a remote or network exception occurs
696    */
697   public void modifyColumn(final String tableName, HColumnDescriptor descriptor)
698   throws IOException {
699     modifyColumn(Bytes.toBytes(tableName), descriptor);
700   }
701 
702   /**
703    * Modify an existing column family on a table.
704    * Asynchronous operation.
705    *
706    * @param tableName name of table
707    * @param columnName name of column to be modified
708    * @param descriptor new column descriptor to use
709    * @throws IOException if a remote or network exception occurs
710    * @deprecated The <code>columnName</code> is redundant. Use {@link #modifyColumn(byte[], HColumnDescriptor)}
711    */
712   public void modifyColumn(final byte [] tableName, final byte [] columnName,
713     HColumnDescriptor descriptor)
714   throws IOException {
715     modifyColumn(tableName, descriptor);
716   }
717 
718   /**
719    * Modify an existing column family on a table.
720    * Asynchronous operation.
721    *
722    * @param tableName name of table
723    * @param descriptor new column descriptor to use
724    * @throws IOException if a remote or network exception occurs
725    */
726   public void modifyColumn(final byte [] tableName, HColumnDescriptor descriptor)
727   throws IOException {
728     try {
729       getMaster().modifyColumn(tableName, descriptor);
730     } catch (RemoteException re) {
731       // Convert RE exceptions in here; client shouldn't have to deal with them,
732       // at least w/ the type of exceptions that come out of this method:
733       // TableNotFoundException, etc.
734       throw RemoteExceptionHandler.decodeRemoteException(re);
735     }
736   }
737 
738   /**
739    * Close a region. For expert-admins.  Runs close on the regionserver.  The
740    * master will not be informed of the close.
741    * @param regionname region name to close
742    * @param hostAndPort If supplied, we'll use this location rather than
743    * the one currently in <code>.META.</code>
744    * @throws IOException if a remote or network exception occurs
745    */
746   public void closeRegion(final String regionname, final String hostAndPort)
747   throws IOException {
748     closeRegion(Bytes.toBytes(regionname), hostAndPort);
749   }
750 
751   /**
752    * Close a region.  For expert-admins  Runs close on the regionserver.  The
753    * master will not be informed of the close.
754    * @param regionname region name to close
755    * @param hostAndPort If supplied, we'll use this location rather than
756    * the one currently in <code>.META.</code>
757    * @throws IOException if a remote or network exception occurs
758    */
759   public void closeRegion(final byte [] regionname, final String hostAndPort)
760   throws IOException {
761     CatalogTracker ct = getCatalogTracker();
762     try {
763       if (hostAndPort != null) {
764         HServerAddress hsa = new HServerAddress(hostAndPort);
765         Pair<HRegionInfo, HServerAddress> pair =
766           MetaReader.getRegion(ct, regionname);
767         if (pair == null || pair.getSecond() == null) {
768           LOG.info("No server in .META. for " +
769             Bytes.toString(regionname) + "; pair=" + pair);
770         } else {
771           closeRegion(hsa, pair.getFirst());
772         }
773       } else {
774         Pair<HRegionInfo, HServerAddress> pair =
775           MetaReader.getRegion(ct, regionname);
776         if (pair == null || pair.getSecond() == null) {
777           LOG.info("No server in .META. for " +
778             Bytes.toString(regionname) + "; pair=" + pair);
779         } else {
780           closeRegion(pair.getSecond(), pair.getFirst());
781         }
782       }
783     } finally {
784       cleanupCatalogTracker(ct);
785     }
786   }
787 
788   private void closeRegion(final HServerAddress hsa, final HRegionInfo hri)
789   throws IOException {
790     HRegionInterface rs = this.connection.getHRegionConnection(hsa);
791     // Close the region without updating zk state.
792     rs.closeRegion(hri, false);
793   }
794 
795   /**
796    * Flush a table or an individual region.
797    * Asynchronous operation.
798    *
799    * @param tableNameOrRegionName table or region to flush
800    * @throws IOException if a remote or network exception occurs
801    * @throws InterruptedException 
802    */
803   public void flush(final String tableNameOrRegionName)
804   throws IOException, InterruptedException {
805     flush(Bytes.toBytes(tableNameOrRegionName));
806   }
807 
808   /**
809    * Flush a table or an individual region.
810    * Asynchronous operation.
811    *
812    * @param tableNameOrRegionName table or region to flush
813    * @throws IOException if a remote or network exception occurs
814    * @throws InterruptedException 
815    */
816   public void flush(final byte [] tableNameOrRegionName)
817   throws IOException, InterruptedException {
818     boolean isRegionName = isRegionName(tableNameOrRegionName);
819     CatalogTracker ct = getCatalogTracker();
820     try {
821       if (isRegionName) {
822         Pair<HRegionInfo, HServerAddress> pair =
823           MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName);
824         if (pair == null || pair.getSecond() == null) {
825           LOG.info("No server in .META. for " +
826             Bytes.toString(tableNameOrRegionName) + "; pair=" + pair);
827         } else {
828           flush(pair.getSecond(), pair.getFirst());
829         }
830       } else {
831         List<Pair<HRegionInfo, HServerAddress>> pairs =
832           MetaReader.getTableRegionsAndLocations(getCatalogTracker(),
833               Bytes.toString(tableNameOrRegionName));
834         for (Pair<HRegionInfo, HServerAddress> pair: pairs) {
835           if (pair.getFirst().isOffline()) continue;
836           if (pair.getSecond() == null) continue;
837           try {
838             flush(pair.getSecond(), pair.getFirst());
839           } catch (NotServingRegionException e) {
840             if (LOG.isDebugEnabled()) {
841               LOG.debug("Trying to flush " + pair.getFirst() + ": " +
842                 StringUtils.stringifyException(e));
843             }
844           }
845         }
846       }
847     } finally {
848       cleanupCatalogTracker(ct);
849     }
850   }
851 
852   private void flush(final HServerAddress hsa, final HRegionInfo hri)
853   throws IOException {
854     HRegionInterface rs = this.connection.getHRegionConnection(hsa);
855     rs.flushRegion(hri);
856   }
857 
858   /**
859    * Compact a table or an individual region.
860    * Asynchronous operation.
861    *
862    * @param tableNameOrRegionName table or region to compact
863    * @throws IOException if a remote or network exception occurs
864    * @throws InterruptedException 
865    */
866   public void compact(final String tableNameOrRegionName)
867   throws IOException, InterruptedException {
868     compact(Bytes.toBytes(tableNameOrRegionName));
869   }
870 
871   /**
872    * Compact a table or an individual region.
873    * Asynchronous operation.
874    *
875    * @param tableNameOrRegionName table or region to compact
876    * @throws IOException if a remote or network exception occurs
877    * @throws InterruptedException 
878    */
879   public void compact(final byte [] tableNameOrRegionName)
880   throws IOException, InterruptedException {
881     compact(tableNameOrRegionName, false);
882   }
883 
884   /**
885    * Major compact a table or an individual region.
886    * Asynchronous operation.
887    *
888    * @param tableNameOrRegionName table or region to major compact
889    * @throws IOException if a remote or network exception occurs
890    * @throws InterruptedException 
891    */
892   public void majorCompact(final String tableNameOrRegionName)
893   throws IOException, InterruptedException {
894     majorCompact(Bytes.toBytes(tableNameOrRegionName));
895   }
896 
897   /**
898    * Major compact a table or an individual region.
899    * Asynchronous operation.
900    *
901    * @param tableNameOrRegionName table or region to major compact
902    * @throws IOException if a remote or network exception occurs
903    * @throws InterruptedException 
904    */
905   public void majorCompact(final byte [] tableNameOrRegionName)
906   throws IOException, InterruptedException {
907     compact(tableNameOrRegionName, true);
908   }
909 
910   /**
911    * Compact a table or an individual region.
912    * Asynchronous operation.
913    *
914    * @param tableNameOrRegionName table or region to compact
915    * @param major True if we are to do a major compaction.
916    * @throws IOException if a remote or network exception occurs
917    * @throws InterruptedException 
918    */
919   private void compact(final byte [] tableNameOrRegionName, final boolean major)
920   throws IOException, InterruptedException {
921     CatalogTracker ct = getCatalogTracker();
922     try {
923       if (isRegionName(tableNameOrRegionName)) {
924         Pair<HRegionInfo, HServerAddress> pair =
925           MetaReader.getRegion(ct, tableNameOrRegionName);
926         if (pair == null || pair.getSecond() == null) {
927           LOG.info("No server in .META. for " +
928             Bytes.toString(tableNameOrRegionName) + "; pair=" + pair);
929         } else {
930           compact(pair.getSecond(), pair.getFirst(), major);
931         }
932       } else {
933         List<Pair<HRegionInfo, HServerAddress>> pairs =
934           MetaReader.getTableRegionsAndLocations(ct,
935               Bytes.toString(tableNameOrRegionName));
936         for (Pair<HRegionInfo, HServerAddress> pair: pairs) {
937           if (pair.getFirst().isOffline()) continue;
938           if (pair.getSecond() == null) continue;
939           try {
940             compact(pair.getSecond(), pair.getFirst(), major);
941           } catch (NotServingRegionException e) {
942             if (LOG.isDebugEnabled()) {
943               LOG.debug("Trying to" + (major ? " major" : "") + " compact " +
944                 pair.getFirst() + ": " +
945                 StringUtils.stringifyException(e));
946             }
947           }
948         }
949       }
950     } finally {
951       cleanupCatalogTracker(ct);
952     }
953   }
954 
955   private void compact(final HServerAddress hsa, final HRegionInfo hri,
956       final boolean major)
957   throws IOException {
958     HRegionInterface rs = this.connection.getHRegionConnection(hsa);
959     rs.compactRegion(hri, major);
960   }
961 
962   /**
963    * Move the region <code>r</code> to <code>dest</code>.
964    * @param encodedRegionName The encoded region name; i.e. the hash that makes
965    * up the region name suffix: e.g. if regionname is
966    * <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>,
967    * then the encoded region name is: <code>527db22f95c8a9e0116f0cc13c680396</code>.
968    * @param destServerName The servername of the destination regionserver.  If
969    * passed the empty byte array we'll assign to a random server.  A server name
970    * is made of host, port and startcode.  Here is an example:
971    * <code> host187.example.com,60020,1289493121758</code>.
972    * @throws UnknownRegionException Thrown if we can't find a region named
973    * <code>encodedRegionName</code>
974    * @throws ZooKeeperConnectionException 
975    * @throws MasterNotRunningException 
976    */
977   public void move(final byte [] encodedRegionName, final byte [] destServerName)
978   throws UnknownRegionException, MasterNotRunningException, ZooKeeperConnectionException {
979     getMaster().move(encodedRegionName, destServerName);
980   }
981 
982   /**
983    * @param regionName Region name to assign.
984    * @param force True to force assign.
985    * @throws MasterNotRunningException
986    * @throws ZooKeeperConnectionException
987    * @throws IOException
988    */
989   public void assign(final byte [] regionName, final boolean force)
990   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
991     getMaster().assign(regionName, force);
992   }
993 
994   /**
995    * Unassign a region from current hosting regionserver.  Region will then be
996    * assigned to a regionserver chosen at random.  Region could be reassigned
997    * back to the same server.  Use {@link #move(byte[], byte[])} if you want
998    * to control the region movement.
999    * @param regionName Region to unassign. Will clear any existing RegionPlan
1000    * if one found.
1001    * @param force If true, force unassign (Will remove region from
1002    * regions-in-transition too if present).
1003    * @throws MasterNotRunningException
1004    * @throws ZooKeeperConnectionException
1005    * @throws IOException
1006    */
1007   public void unassign(final byte [] regionName, final boolean force)
1008   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
1009     getMaster().unassign(regionName, force);
1010   }
1011 
1012   /**
1013    * Turn the load balancer on or off.
1014    * @param b If true, enable balancer. If false, disable balancer.
1015    * @return Previous balancer value
1016    */
1017   public boolean balanceSwitch(final boolean b)
1018   throws MasterNotRunningException, ZooKeeperConnectionException {
1019     return getMaster().balanceSwitch(b);
1020   }
1021 
1022   /**
1023    * Invoke the balancer.  Will run the balancer and if regions to move, it will
1024    * go ahead and do the reassignments.  Can NOT run for various reasons.  Check
1025    * logs.
1026    * @return True if balancer ran, false otherwise.
1027    */
1028   public boolean balancer()
1029   throws MasterNotRunningException, ZooKeeperConnectionException {
1030     return getMaster().balance();
1031   }
1032 
1033   /**
1034    * Split a table or an individual region.
1035    * Asynchronous operation.
1036    *
1037    * @param tableNameOrRegionName table or region to split
1038    * @throws IOException if a remote or network exception occurs
1039    * @throws InterruptedException 
1040    */
1041   public void split(final String tableNameOrRegionName)
1042   throws IOException, InterruptedException {
1043     split(Bytes.toBytes(tableNameOrRegionName));
1044   }
1045 
1046   /**
1047    * Split a table or an individual region.  Implicitly finds an optimal split
1048    * point.  Asynchronous operation.
1049    *
1050    * @param tableNameOrRegionName table to region to split
1051    * @throws IOException if a remote or network exception occurs
1052    * @throws InterruptedException 
1053    */
1054   public void split(final byte [] tableNameOrRegionName)
1055   throws IOException, InterruptedException {
1056     split(tableNameOrRegionName, null);
1057   }
1058 
1059   /**
1060    * Split a table or an individual region.
1061    * Asynchronous operation.
1062    *
1063    * @param tableNameOrRegionName table to region to split
1064    * @param splitPoint the explicit position to split on
1065    * @throws IOException if a remote or network exception occurs
1066    * @throws InterruptedException interrupt exception occurred
1067    */
1068   public void split(final byte [] tableNameOrRegionName,
1069       final byte [] splitPoint) throws IOException, InterruptedException {
1070     CatalogTracker ct = getCatalogTracker();
1071     try {
1072       if (isRegionName(tableNameOrRegionName)) {
1073         // Its a possible region name.
1074         Pair<HRegionInfo, HServerAddress> pair =
1075           MetaReader.getRegion(getCatalogTracker(), tableNameOrRegionName);
1076         if (pair == null || pair.getSecond() == null) {
1077           LOG.info("No server in .META. for " +
1078             Bytes.toString(tableNameOrRegionName) + "; pair=" + pair);
1079         } else {
1080           split(pair.getSecond(), pair.getFirst(), splitPoint);
1081         }
1082       } else {
1083         List<Pair<HRegionInfo, HServerAddress>> pairs =
1084           MetaReader.getTableRegionsAndLocations(getCatalogTracker(),
1085               Bytes.toString(tableNameOrRegionName));
1086         for (Pair<HRegionInfo, HServerAddress> pair: pairs) {
1087           // May not be a server for a particular row
1088           if (pair.getSecond() == null) continue;
1089           HRegionInfo r = pair.getFirst();
1090           // check for parents
1091           if (r.isSplitParent()) continue;
1092           if (splitPoint != null) {
1093             // if a split point given, only split that particular region
1094             if (!r.containsRow(splitPoint)) continue;
1095           }
1096           split(pair.getSecond(), pair.getFirst(), splitPoint);
1097         }
1098       }
1099     } finally {
1100       cleanupCatalogTracker(ct);
1101     }
1102   }
1103 
1104   private void split(final HServerAddress hsa, final HRegionInfo hri,
1105       byte[] splitPoint) throws IOException {
1106     HRegionInterface rs = this.connection.getHRegionConnection(hsa);
1107     rs.splitRegion(hri, splitPoint);
1108   }
1109 
1110   /**
1111    * Modify an existing table, more IRB friendly version.
1112    * Asynchronous operation.  This means that it may be a while before your
1113    * schema change is updated across all of the table.
1114    *
1115    * @param tableName name of table.
1116    * @param htd modified description of the table
1117    * @throws IOException if a remote or network exception occurs
1118    */
1119   public void modifyTable(final byte [] tableName, HTableDescriptor htd)
1120   throws IOException {
1121     try {
1122       getMaster().modifyTable(tableName, htd);
1123     } catch (RemoteException re) {
1124       // Convert RE exceptions in here; client shouldn't have to deal with them,
1125       // at least w/ the type of exceptions that come out of this method:
1126       // TableNotFoundException, etc.
1127       throw RemoteExceptionHandler.decodeRemoteException(re);
1128     }
1129   }
1130 
1131   /**
1132    * @param tableNameOrRegionName Name of a table or name of a region.
1133    * @return True if <code>tableNameOrRegionName</code> is *possibly* a region
1134    * name else false if a verified tablename (we call {@link #tableExists(byte[])};
1135    * else we throw an exception.
1136    * @throws IOException 
1137    */
1138   private boolean isRegionName(final byte [] tableNameOrRegionName)
1139   throws IOException {
1140     if (tableNameOrRegionName == null) {
1141       throw new IllegalArgumentException("Pass a table name or region name");
1142     }
1143     return !tableExists(tableNameOrRegionName);
1144   }
1145 
1146   /**
1147    * Shuts down the HBase cluster
1148    * @throws IOException if a remote or network exception occurs
1149    */
1150   public synchronized void shutdown() throws IOException {
1151     isMasterRunning();
1152     try {
1153       getMaster().shutdown();
1154     } catch (RemoteException e) {
1155       throw RemoteExceptionHandler.decodeRemoteException(e);
1156     }
1157   }
1158 
1159   /**
1160    * Shuts down the current HBase master only.
1161    * Does not shutdown the cluster.
1162    * @see #shutdown()
1163    * @throws IOException if a remote or network exception occurs
1164    */
1165   public synchronized void stopMaster() throws IOException {
1166     isMasterRunning();
1167     try {
1168       getMaster().stopMaster();
1169     } catch (RemoteException e) {
1170       throw RemoteExceptionHandler.decodeRemoteException(e);
1171     }
1172   }
1173 
1174   /**
1175    * Stop the designated regionserver.
1176    * @throws IOException if a remote or network exception occurs
1177    */
1178   public synchronized void stopRegionServer(final HServerAddress hsa)
1179   throws IOException {
1180     HRegionInterface rs = this.connection.getHRegionConnection(hsa);
1181     rs.stop("Called by admin client " + this.connection.toString());
1182   }
1183 
1184   /**
1185    * @return cluster status
1186    * @throws IOException if a remote or network exception occurs
1187    */
1188   public ClusterStatus getClusterStatus() throws IOException {
1189     return getMaster().getClusterStatus();
1190   }
1191 
1192   private HRegionLocation getFirstMetaServerForTable(final byte [] tableName)
1193   throws IOException {
1194     return connection.locateRegion(HConstants.META_TABLE_NAME,
1195       HRegionInfo.createRegionName(tableName, null, HConstants.NINES, false));
1196   }
1197 
1198   /**
1199    * @return Configuration used by the instance.
1200    */
1201   public Configuration getConfiguration() {
1202     return this.conf;
1203   }
1204 
1205   /**
1206    * Check to see if HBase is running. Throw an exception if not.
1207    *
1208    * @param conf system configuration
1209    * @throws MasterNotRunningException if the master is not running
1210    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
1211    */
1212   public static void checkHBaseAvailable(Configuration conf)
1213   throws MasterNotRunningException, ZooKeeperConnectionException {
1214     Configuration copyOfConf = HBaseConfiguration.create(conf);
1215     copyOfConf.setInt("hbase.client.retries.number", 1);
1216     new HBaseAdmin(copyOfConf);
1217   }
1218 }