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