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 org.apache.commons.logging.Log;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.hadoop.conf.Configuration;
25  import org.apache.hadoop.hbase.ClusterStatus;
26  import org.apache.hadoop.hbase.HBaseConfiguration;
27  import org.apache.hadoop.hbase.HColumnDescriptor;
28  import org.apache.hadoop.hbase.HConstants;
29  import org.apache.hadoop.hbase.HRegionInfo;
30  import org.apache.hadoop.hbase.HRegionLocation;
31  import org.apache.hadoop.hbase.HTableDescriptor;
32  import org.apache.hadoop.hbase.MasterNotRunningException;
33  import org.apache.hadoop.hbase.RegionException;
34  import org.apache.hadoop.hbase.RemoteExceptionHandler;
35  import org.apache.hadoop.hbase.TableExistsException;
36  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
37  import org.apache.hadoop.hbase.ipc.HMasterInterface;
38  import org.apache.hadoop.hbase.ipc.HRegionInterface;
39  import org.apache.hadoop.hbase.util.Bytes;
40  import org.apache.hadoop.hbase.util.MetaUtils;
41  import org.apache.hadoop.hbase.util.Writables;
42  import org.apache.hadoop.io.BooleanWritable;
43  import org.apache.hadoop.io.Writable;
44  import org.apache.hadoop.ipc.RemoteException;
45  
46  import java.io.IOException;
47  import java.util.Arrays;
48  import java.util.Map;
49  import java.util.NavigableMap;
50  
51  /**
52   * Provides an interface to manage HBase database table metadata + general 
53   * administrative functions.  Use HBaseAdmin to create, drop, list, enable and 
54   * disable tables. Use it also to add and drop table column families. 
55   * 
56   * See {@link HTable} to add, update, and delete data from an individual table.
57   */
58  public class HBaseAdmin {
59    private final Log LOG = LogFactory.getLog(this.getClass().getName());
60  //  private final HConnection connection;
61    final HConnection connection;
62    private volatile Configuration conf;
63    private final long pause;
64    private final int numRetries;
65    private volatile HMasterInterface master;
66  
67    /**
68     * Constructor
69     *
70     * @param conf Configuration object
71     * @throws MasterNotRunningException if the master is not running
72     */
73    public HBaseAdmin(Configuration conf) throws MasterNotRunningException {
74      this.connection = HConnectionManager.getConnection(conf);
75      this.conf = conf;
76      this.pause = conf.getLong("hbase.client.pause", 30 * 1000);
77      this.numRetries = conf.getInt("hbase.client.retries.number", 5);
78      this.master = connection.getMaster();
79    }
80  
81    /** @return HConnection used by this object. */
82    public HConnection getConnection() {
83      return connection;
84    }
85  
86    /**
87     * @return proxy connection to master server for this instance
88     * @throws MasterNotRunningException if the master is not running
89     */
90    public HMasterInterface getMaster() throws MasterNotRunningException{
91      return this.connection.getMaster();
92    }
93  
94    /** @return - true if the master server is running */
95    public boolean isMasterRunning() {
96      return this.connection.isMasterRunning();
97    }
98  
99    /**
100    * @param tableName Table to check.
101    * @return True if table exists already.
102    * @throws MasterNotRunningException if the master is not running
103    */
104   public boolean tableExists(final String tableName)
105   throws MasterNotRunningException {
106     return tableExists(Bytes.toBytes(tableName));
107   }
108 
109   /**
110    * @param tableName Table to check.
111    * @return True if table exists already.
112    * @throws MasterNotRunningException if the master is not running
113    */
114   public boolean tableExists(final byte [] tableName)
115   throws MasterNotRunningException {
116     if (this.master == null) {
117       throw new MasterNotRunningException("master has been shut down");
118     }
119     return connection.tableExists(tableName);
120   }
121 
122   /**
123    * List all the userspace tables.  In other words, scan the META table.
124    *
125    * If we wanted this to be really fast, we could implement a special
126    * catalog table that just contains table names and their descriptors.
127    * Right now, it only exists as part of the META table's region info.
128    *
129    * @return - returns an array of HTableDescriptors
130    * @throws IOException if a remote or network exception occurs
131    */
132   public HTableDescriptor[] listTables() throws IOException {
133     return this.connection.listTables();
134   }
135 
136 
137   /**
138    * Method for getting the tableDescriptor
139    * @param tableName as a byte []
140    * @return the tableDescriptor
141    * @throws IOException if a remote or network exception occurs
142    */
143   public HTableDescriptor getTableDescriptor(final byte [] tableName)
144   throws IOException {
145     return this.connection.getHTableDescriptor(tableName);
146   }
147 
148   private long getPauseTime(int tries) {
149     int triesCount = tries;
150     if (triesCount >= HConstants.RETRY_BACKOFF.length)
151       triesCount = HConstants.RETRY_BACKOFF.length - 1;
152     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
153   }
154 
155   /**
156    * Creates a new table.
157    * Synchronous operation.
158    *
159    * @param desc table descriptor for table
160    *
161    * @throws IllegalArgumentException if the table name is reserved
162    * @throws MasterNotRunningException if master is not running
163    * @throws TableExistsException if table already exists (If concurrent
164    * threads, the table may have been created between test-for-existence
165    * and attempt-at-creation).
166    * @throws IOException if a remote or network exception occurs
167    */
168   public void createTable(HTableDescriptor desc)
169   throws IOException {
170     createTable(desc, null);
171   }
172 
173   /**
174    * Creates a new table with the specified number of regions.  The start key
175    * specified will become the end key of the first region of the table, and
176    * the end key specified will become the start key of the last region of the
177    * table (the first region has a null start key and the last region has a
178    * null end key).
179    *
180    * BigInteger math will be used to divide the key range specified into
181    * enough segments to make the required number of total regions.
182    *
183    * Synchronous operation.
184    *
185    * @param desc table descriptor for table
186    * @param startKey beginning of key range
187    * @param endKey end of key range
188    * @param numRegions the total number of regions to create
189    *
190    * @throws IllegalArgumentException if the table name is reserved
191    * @throws MasterNotRunningException if master is not running
192    * @throws TableExistsException if table already exists (If concurrent
193    * threads, the table may have been created between test-for-existence
194    * and attempt-at-creation).
195    * @throws IOException
196    */
197   public void createTable(HTableDescriptor desc, byte [] startKey,
198       byte [] endKey, int numRegions)
199   throws IOException {
200     HTableDescriptor.isLegalTableName(desc.getName());
201     if(numRegions < 3) {
202       throw new IllegalArgumentException("Must create at least three regions");
203     } else if(Bytes.compareTo(startKey, endKey) >= 0) {
204       throw new IllegalArgumentException("Start key must be smaller than end key");
205     }
206     byte [][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
207     if(splitKeys == null || splitKeys.length != numRegions - 1) {
208       throw new IllegalArgumentException("Unable to split key range into enough regions");
209     }
210     createTable(desc, splitKeys);
211   }
212 
213   /**
214    * Creates a new table with an initial set of empty regions defined by the
215    * specified split keys.  The total number of regions created will be the
216    * number of split keys plus one (the first region has a null start key and
217    * the last region has a null end key).
218    * Synchronous operation.
219    *
220    * @param desc table descriptor for table
221    * @param splitKeys array of split keys for the initial regions of the table
222    *
223    * @throws IllegalArgumentException if the table name is reserved
224    * @throws MasterNotRunningException if master is not running
225    * @throws TableExistsException if table already exists (If concurrent
226    * threads, the table may have been created between test-for-existence
227    * and attempt-at-creation).
228    * @throws IOException
229    */
230   public void createTable(HTableDescriptor desc, byte [][] splitKeys)
231   throws IOException {
232     HTableDescriptor.isLegalTableName(desc.getName());
233     if(splitKeys != null && splitKeys.length > 1) {
234       Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
235       // Verify there are no duplicate split keys
236       byte [] lastKey = null;
237       for(byte [] splitKey : splitKeys) {
238         if(lastKey != null && Bytes.equals(splitKey, lastKey)) {
239           throw new IllegalArgumentException("All split keys must be unique, found duplicate");
240         }
241         lastKey = splitKey;
242       }
243     }
244     createTableAsync(desc, splitKeys);
245     for (int tries = 0; tries < numRetries; tries++) {
246       try {
247         // Wait for new table to come on-line
248         connection.locateRegion(desc.getName(), HConstants.EMPTY_START_ROW);
249         break;
250 
251       } catch (RegionException e) {
252         if (tries == numRetries - 1) {
253           // Ran out of tries
254           throw e;
255         }
256       }
257       try {
258         Thread.sleep(getPauseTime(tries));
259       } catch (InterruptedException e) {
260         // continue
261       }
262     }
263   }
264 
265   /**
266    * Creates a new table but does not block and wait for it to come online.
267    * Asynchronous operation.
268    *
269    * @param desc table descriptor for table
270    *
271    * @throws IllegalArgumentException Bad table name.
272    * @throws MasterNotRunningException if master is not running
273    * @throws TableExistsException if table already exists (If concurrent
274    * threads, the table may have been created between test-for-existence
275    * and attempt-at-creation).
276    * @throws IOException
277    */
278   public void createTableAsync(HTableDescriptor desc, byte [][] splitKeys)
279   throws IOException {
280     if (this.master == null) {
281       throw new MasterNotRunningException("master has been shut down");
282     }
283     HTableDescriptor.isLegalTableName(desc.getName());
284     try {
285       this.master.createTable(desc, splitKeys);
286     } catch (RemoteException e) {
287       throw RemoteExceptionHandler.decodeRemoteException(e);
288     }
289   }
290 
291   /**
292    * Deletes a table.
293    * Synchronous operation.
294    *
295    * @param tableName name of table to delete
296    * @throws IOException if a remote or network exception occurs
297    */
298   public void deleteTable(final String tableName) throws IOException {
299     deleteTable(Bytes.toBytes(tableName));
300   }
301 
302   /**
303    * Deletes a table.
304    * Synchronous operation.
305    *
306    * @param tableName name of table to delete
307    * @throws IOException if a remote or network exception occurs
308    */
309   public void deleteTable(final byte [] tableName) throws IOException {
310     if (this.master == null) {
311       throw new MasterNotRunningException("master has been shut down");
312     }
313     HTableDescriptor.isLegalTableName(tableName);
314     HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName);
315     try {
316       this.master.deleteTable(tableName);
317     } catch (RemoteException e) {
318       throw RemoteExceptionHandler.decodeRemoteException(e);
319     }
320     final int batchCount = this.conf.getInt("hbase.admin.scanner.caching", 10);
321     // Wait until first region is deleted
322     HRegionInterface server =
323       connection.getHRegionConnection(firstMetaServer.getServerAddress());
324     HRegionInfo info = new HRegionInfo();
325     for (int tries = 0; tries < numRetries; tries++) {
326       long scannerId = -1L;
327       try {
328         Scan scan = new Scan().addColumn(HConstants.CATALOG_FAMILY,
329           HConstants.REGIONINFO_QUALIFIER);
330         scannerId = server.openScanner(
331           firstMetaServer.getRegionInfo().getRegionName(), scan);
332         // Get a batch at a time.
333         Result [] values = server.next(scannerId, batchCount);
334         if (values == null || values.length == 0) {
335           break;
336         }
337         boolean found = false;
338         for (Result r : values) {
339           NavigableMap<byte[], byte[]> infoValues =
340               r.getFamilyMap(HConstants.CATALOG_FAMILY);
341           for (Map.Entry<byte[], byte[]> e : infoValues.entrySet()) {
342             if (Bytes.equals(e.getKey(), HConstants.REGIONINFO_QUALIFIER)) {
343               info = (HRegionInfo) Writables.getWritable(e.getValue(), info);
344               if (Bytes.equals(info.getTableDesc().getName(), tableName)) {
345                 found = true;
346               } else {
347                 found = false;
348                 break;
349               }
350             }
351           }
352         }
353         if (!found) {
354           break;
355         }
356       } catch (IOException ex) {
357         if(tries == numRetries - 1) {           // no more tries left
358           if (ex instanceof RemoteException) {
359             ex = RemoteExceptionHandler.decodeRemoteException((RemoteException) ex);
360           }
361           throw ex;
362         }
363       } finally {
364         if (scannerId != -1L) {
365           try {
366             server.close(scannerId);
367           } catch (Exception ex) {
368             LOG.warn(ex);
369           }
370         }
371       }
372       try {
373         Thread.sleep(getPauseTime(tries));
374       } catch (InterruptedException e) {
375         // continue
376       }
377     }
378     // Delete cached information to prevent clients from using old locations
379     HConnectionManager.deleteConnectionInfo(conf, false);
380     LOG.info("Deleted " + Bytes.toString(tableName));
381   }
382 
383 
384 
385   /**
386    * Brings a table on-line (enables it).
387    * Synchronous operation.
388    *
389    * @param tableName name of the table
390    * @throws IOException if a remote or network exception occurs
391    */
392   public void enableTable(final String tableName) throws IOException {
393     enableTable(Bytes.toBytes(tableName));
394   }
395 
396   /**
397    * Brings a table on-line (enables it).
398    * Synchronous operation.
399    *
400    * @param tableName name of the table
401    * @throws IOException if a remote or network exception occurs
402    */
403   public void enableTable(final byte [] tableName) throws IOException {
404     if (this.master == null) {
405       throw new MasterNotRunningException("master has been shut down");
406     }
407 
408     // Wait until all regions are enabled
409     boolean enabled = false;
410     for (int tries = 0; tries < this.numRetries; tries++) {
411 
412       try {
413         this.master.enableTable(tableName);
414       } catch (RemoteException e) {
415         throw RemoteExceptionHandler.decodeRemoteException(e);
416       }
417       enabled = isTableEnabled(tableName);
418       if (enabled) break;
419       long sleep = getPauseTime(tries);
420       if (LOG.isDebugEnabled()) {
421         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
422           "enabled in " + Bytes.toString(tableName));
423       }
424       try {
425         Thread.sleep(sleep);
426       } catch (InterruptedException e) {
427         // continue
428       }
429       if (LOG.isDebugEnabled()) {
430         LOG.debug("Wake. Waiting for all regions to be enabled from " +
431           Bytes.toString(tableName));
432       }
433     }
434     if (!enabled)
435       throw new IOException("Unable to enable table " +
436         Bytes.toString(tableName));
437     LOG.info("Enabled table " + Bytes.toString(tableName));
438   }
439 
440   /**
441    * Disables a table (takes it off-line) If it is being served, the master
442    * will tell the servers to stop serving it.
443    * Synchronous operation.
444    *
445    * @param tableName name of table
446    * @throws IOException if a remote or network exception occurs
447    */
448   public void disableTable(final String tableName) throws IOException {
449     disableTable(Bytes.toBytes(tableName));
450   }
451 
452   /**
453    * Disables a table (takes it off-line) If it is being served, the master
454    * will tell the servers to stop serving it.
455    * Synchronous operation.
456    *
457    * @param tableName name of table
458    * @throws IOException if a remote or network exception occurs
459    */
460   public void disableTable(final byte [] tableName) throws IOException {
461     if (this.master == null) {
462       throw new MasterNotRunningException("master has been shut down");
463     }
464 
465     // Wait until all regions are disabled
466     boolean disabled = false;
467     for (int tries = 0; tries < this.numRetries; tries++) {
468       try {
469         this.master.disableTable(tableName);
470       } catch (RemoteException e) {
471         throw RemoteExceptionHandler.decodeRemoteException(e);
472       }
473       disabled = isTableDisabled(tableName);
474       if (disabled) break;
475       if (LOG.isDebugEnabled()) {
476         LOG.debug("Sleep. Waiting for all regions to be disabled from " +
477           Bytes.toString(tableName));
478       }
479       try {
480         Thread.sleep(getPauseTime(tries));
481       } catch (InterruptedException e) {
482         // continue
483       }
484       if (LOG.isDebugEnabled()) {
485         LOG.debug("Wake. Waiting for all regions to be disabled from " +
486           Bytes.toString(tableName));
487       }
488     }
489     if (!disabled) {
490       throw new RegionException("Retries exhausted, it took too long to wait"+
491         " for the table " + Bytes.toString(tableName) + " to be disabled.");
492     }
493     LOG.info("Disabled " + Bytes.toString(tableName));
494   }
495 
496   /**
497    * @param tableName name of table to check
498    * @return true if table is on-line
499    * @throws IOException if a remote or network exception occurs
500    */
501   public boolean isTableEnabled(String tableName) throws IOException {
502     return isTableEnabled(Bytes.toBytes(tableName));
503   }
504   /**
505    * @param tableName name of table to check
506    * @return true if table is on-line
507    * @throws IOException if a remote or network exception occurs
508    */
509   public boolean isTableEnabled(byte[] tableName) throws IOException {
510     return connection.isTableEnabled(tableName);
511   }
512 
513   /**
514    * @param tableName name of table to check
515    * @return true if table is off-line
516    * @throws IOException if a remote or network exception occurs
517    */
518   public boolean isTableDisabled(byte[] tableName) throws IOException {
519     return connection.isTableDisabled(tableName);
520   }
521 
522   /**
523    * @param tableName name of table to check
524    * @return true if all regions of the table are available
525    * @throws IOException if a remote or network exception occurs
526    */
527   public boolean isTableAvailable(byte[] tableName) throws IOException {
528     return connection.isTableAvailable(tableName);
529   }
530 
531   /**
532    * @param tableName name of table to check
533    * @return true if all regions of the table are available
534    * @throws IOException if a remote or network exception occurs
535    */
536   public boolean isTableAvailable(String tableName) throws IOException {
537     return connection.isTableAvailable(Bytes.toBytes(tableName));
538   }
539 
540   /**
541    * Add a column to an existing table.
542    * Asynchronous operation.
543    *
544    * @param tableName name of the table to add column to
545    * @param column column descriptor of column to be added
546    * @throws IOException if a remote or network exception occurs
547    */
548   public void addColumn(final String tableName, HColumnDescriptor column)
549   throws IOException {
550     addColumn(Bytes.toBytes(tableName), column);
551   }
552 
553   /**
554    * Add a column to an existing table.
555    * Asynchronous operation.
556    *
557    * @param tableName name of the table to add column to
558    * @param column column descriptor of column to be added
559    * @throws IOException if a remote or network exception occurs
560    */
561   public void addColumn(final byte [] tableName, HColumnDescriptor column)
562   throws IOException {
563     if (this.master == null) {
564       throw new MasterNotRunningException("master has been shut down");
565     }
566     HTableDescriptor.isLegalTableName(tableName);
567     try {
568       this.master.addColumn(tableName, column);
569     } catch (RemoteException e) {
570       throw RemoteExceptionHandler.decodeRemoteException(e);
571     }
572   }
573 
574   /**
575    * Delete a column from a table.
576    * Asynchronous operation.
577    *
578    * @param tableName name of table
579    * @param columnName name of column to be deleted
580    * @throws IOException if a remote or network exception occurs
581    */
582   public void deleteColumn(final String tableName, final String columnName)
583   throws IOException {
584     deleteColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName));
585   }
586 
587   /**
588    * Delete a column from a table.
589    * Asynchronous operation.
590    *
591    * @param tableName name of table
592    * @param columnName name of column to be deleted
593    * @throws IOException if a remote or network exception occurs
594    */
595   public void deleteColumn(final byte [] tableName, final byte [] columnName)
596   throws IOException {
597     if (this.master == null) {
598       throw new MasterNotRunningException("master has been shut down");
599     }
600     HTableDescriptor.isLegalTableName(tableName);
601     try {
602       this.master.deleteColumn(tableName, columnName);
603     } catch (RemoteException e) {
604       throw RemoteExceptionHandler.decodeRemoteException(e);
605     }
606   }
607 
608   /**
609    * Modify an existing column family on a table.
610    * Asynchronous operation.
611    *
612    * @param tableName name of table
613    * @param columnName name of column to be modified
614    * @param descriptor new column descriptor to use
615    * @throws IOException if a remote or network exception occurs
616    */
617   public void modifyColumn(final String tableName, final String columnName,
618       HColumnDescriptor descriptor)
619   throws IOException {
620     modifyColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName),
621       descriptor);
622   }
623 
624   /**
625    * Modify an existing column family on a table.
626    * Asynchronous operation.
627    *
628    * @param tableName name of table
629    * @param columnName name of column to be modified
630    * @param descriptor new column descriptor to use
631    * @throws IOException if a remote or network exception occurs
632    */
633   public void modifyColumn(final byte [] tableName, final byte [] columnName,
634     HColumnDescriptor descriptor)
635   throws IOException {
636     if (this.master == null) {
637       throw new MasterNotRunningException("master has been shut down");
638     }
639     HTableDescriptor.isLegalTableName(tableName);
640     try {
641       this.master.modifyColumn(tableName, columnName, descriptor);
642     } catch (RemoteException e) {
643       throw RemoteExceptionHandler.decodeRemoteException(e);
644     }
645   }
646 
647   /**
648    * Close a region. For expert-admins.
649    * Asynchronous operation.
650    *
651    * @param regionname region name to close
652    * @param args Optional server name.  Otherwise, we'll send close to the
653    * server registered in .META.
654    * @throws IOException if a remote or network exception occurs
655    */
656   public void closeRegion(final String regionname, final Object... args)
657   throws IOException {
658     closeRegion(Bytes.toBytes(regionname), args);
659   }
660 
661   /**
662    * Close a region.  For expert-admins.
663    * Asynchronous operation.
664    *
665    * @param regionname region name to close
666    * @param args Optional server name.  Otherwise, we'll send close to the
667    * server registered in .META.
668    * @throws IOException if a remote or network exception occurs
669    */
670   public void closeRegion(final byte [] regionname, final Object... args)
671   throws IOException {
672     // Be careful. Must match the handler over in HMaster at MODIFY_CLOSE_REGION
673     int len = (args == null)? 0: args.length;
674     int xtraArgsCount = 1;
675     Object [] newargs = new Object[len + xtraArgsCount];
676     newargs[0] = regionname;
677     if(args != null) {
678       System.arraycopy(args, 0, newargs, xtraArgsCount, len);
679     }
680     modifyTable(HConstants.META_TABLE_NAME, HConstants.Modify.CLOSE_REGION,
681       newargs);
682   }
683 
684   /**
685    * Flush a table or an individual region.
686    * Asynchronous operation.
687    *
688    * @param tableNameOrRegionName table or region to flush
689    * @throws IOException if a remote or network exception occurs
690    */
691   public void flush(final String tableNameOrRegionName) throws IOException {
692     flush(Bytes.toBytes(tableNameOrRegionName));
693   }
694 
695   /**
696    * Flush a table or an individual region.
697    * Asynchronous operation.
698    *
699    * @param tableNameOrRegionName table or region to flush
700    * @throws IOException if a remote or network exception occurs
701    */
702   public void flush(final byte [] tableNameOrRegionName) throws IOException {
703     modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_FLUSH);
704   }
705 
706   /**
707    * Compact a table or an individual region.
708    * Asynchronous operation.
709    *
710    * @param tableNameOrRegionName table or region to compact
711    * @throws IOException if a remote or network exception occurs
712    */
713   public void compact(final String tableNameOrRegionName) throws IOException {
714     compact(Bytes.toBytes(tableNameOrRegionName));
715   }
716 
717   /**
718    * Compact a table or an individual region.
719    * Asynchronous operation.
720    *
721    * @param tableNameOrRegionName table or region to compact
722    * @throws IOException if a remote or network exception occurs
723    */
724   public void compact(final byte [] tableNameOrRegionName) throws IOException {
725     modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_COMPACT);
726   }
727 
728   /**
729    * Major compact a table or an individual region.
730    * Asynchronous operation.
731    *
732    * @param tableNameOrRegionName table or region to major compact
733    * @throws IOException if a remote or network exception occurs
734    */
735   public void majorCompact(final String tableNameOrRegionName)
736   throws IOException {
737     majorCompact(Bytes.toBytes(tableNameOrRegionName));
738   }
739 
740   /**
741    * Major compact a table or an individual region.
742    * Asynchronous operation.
743    *
744    * @param tableNameOrRegionName table or region to major compact
745    * @throws IOException if a remote or network exception occurs
746    */
747   public void majorCompact(final byte [] tableNameOrRegionName)
748   throws IOException {
749     modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_MAJOR_COMPACT);
750   }
751 
752   /**
753    * Split a table or an individual region.
754    * Asynchronous operation.
755    *
756    * @param tableNameOrRegionName table or region to split
757    * @throws IOException if a remote or network exception occurs
758    */
759   public void split(final String tableNameOrRegionName) throws IOException {
760     split(Bytes.toBytes(tableNameOrRegionName));
761   }
762 
763   /**
764    * Split a table or an individual region.
765    * Asynchronous operation.
766    *
767    * @param tableNameOrRegionName table to region to split
768    * @throws IOException if a remote or network exception occurs
769    */
770   public void split(final byte [] tableNameOrRegionName) throws IOException {
771     modifyTable(tableNameOrRegionName, HConstants.Modify.TABLE_SPLIT);
772   }
773 
774   /*
775    * Call modifyTable using passed tableName or region name String.  If no
776    * such table, presume we have been passed a region name.
777    * @param tableNameOrRegionName
778    * @param op
779    * @throws IOException
780    */
781   private void modifyTable(final byte [] tableNameOrRegionName,
782       final HConstants.Modify op)
783   throws IOException {
784     if (tableNameOrRegionName == null) {
785       throw new IllegalArgumentException("Pass a table name or region name");
786     }
787     byte [] tableName = tableExists(tableNameOrRegionName)?
788       tableNameOrRegionName: null;
789     byte [] regionName = tableName == null? tableNameOrRegionName: null;
790     Object [] args = regionName == null? null: new byte [][] {regionName};
791     modifyTable(tableName == null? null: tableName, op, args);
792   }
793 
794   /**
795    * Modify an existing table, more IRB friendly version.
796    * Asynchronous operation.
797    *
798    * @param tableName name of table.
799    * @param htd modified description of the table
800    * @throws IOException if a remote or network exception occurs
801    */
802   public void modifyTable(final byte [] tableName, HTableDescriptor htd)
803   throws IOException {
804     modifyTable(tableName, HConstants.Modify.TABLE_SET_HTD, htd);
805   }
806 
807   /**
808    * Modify an existing table.
809    * Asynchronous operation.
810    *
811    * @param tableName name of table.  May be null if we are operating on a
812    * region.
813    * @param op table modification operation
814    * @param args operation specific arguments
815    * @throws IOException if a remote or network exception occurs
816    */
817   public void modifyTable(final byte [] tableName, HConstants.Modify op,
818       Object... args)
819       throws IOException {
820     if (this.master == null) {
821       throw new MasterNotRunningException("master has been shut down");
822     }
823     // Let pass if its a catalog table.  Used by admins.
824     if (tableName != null && !MetaUtils.isMetaTableName(tableName)) {
825       // This will throw exception
826       HTableDescriptor.isLegalTableName(tableName);
827     }
828     Writable[] arr = null;
829     try {
830       switch (op) {
831       case TABLE_SET_HTD:
832         if (args == null || args.length < 1 ||
833             !(args[0] instanceof HTableDescriptor)) {
834           throw new IllegalArgumentException("SET_HTD requires a HTableDescriptor");
835         }
836         arr = new Writable[1];
837         arr[0] = (HTableDescriptor)args[0];
838         this.master.modifyTable(tableName, op, arr);
839         break;
840 
841       case TABLE_COMPACT:
842       case TABLE_SPLIT:
843       case TABLE_MAJOR_COMPACT:
844       case TABLE_FLUSH:
845         if (args != null && args.length > 0) {
846           arr = new Writable[1];
847           if (args[0] instanceof byte[]) {
848             arr[0] = new ImmutableBytesWritable((byte[])args[0]);
849           } else if (args[0] instanceof ImmutableBytesWritable) {
850             arr[0] = (ImmutableBytesWritable)args[0];
851           } else if (args[0] instanceof String) {
852             arr[0] = new ImmutableBytesWritable(Bytes.toBytes((String)args[0]));
853           } else {
854             throw new IllegalArgumentException("Requires byte[], String, or" +
855               "ImmutableBytesWritable");
856           }
857         }
858         this.master.modifyTable(tableName, op, arr);
859         break;
860 
861       case CLOSE_REGION:
862         if (args == null || args.length < 1) {
863           throw new IllegalArgumentException("Requires at least a region name");
864         }
865         arr = new Writable[args.length];
866         for (int i = 0; i < args.length; i++) {
867           if (args[i] instanceof byte[]) {
868             arr[i] = new ImmutableBytesWritable((byte[])args[i]);
869           } else if (args[i] instanceof ImmutableBytesWritable) {
870             arr[i] = (ImmutableBytesWritable)args[i];
871           } else if (args[i] instanceof String) {
872             arr[i] = new ImmutableBytesWritable(Bytes.toBytes((String)args[i]));
873           } else if (args[i] instanceof Boolean) {
874             arr[i] = new BooleanWritable((Boolean) args[i]);
875           } else {
876             throw new IllegalArgumentException("Requires byte [] or " +
877               "ImmutableBytesWritable, not " + args[i]);
878           }
879         }
880         this.master.modifyTable(tableName, op, arr);
881         break;
882 
883       default:
884         throw new IOException("unknown modifyTable op " + op);
885       }
886     } catch (RemoteException e) {
887       throw RemoteExceptionHandler.decodeRemoteException(e);
888     }
889   }
890 
891   /**
892    * Shuts down the HBase instance
893    * @throws IOException if a remote or network exception occurs
894    */
895   public synchronized void shutdown() throws IOException {
896     if (this.master == null) {
897       throw new MasterNotRunningException("master has been shut down");
898     }
899     try {
900       this.master.shutdown();
901     } catch (RemoteException e) {
902       throw RemoteExceptionHandler.decodeRemoteException(e);
903     } finally {
904       this.master = null;
905     }
906   }
907 
908   /**
909    * @return cluster status
910    * @throws IOException if a remote or network exception occurs
911    */
912   public ClusterStatus getClusterStatus() throws IOException {
913     if (this.master == null) {
914       throw new MasterNotRunningException("master has been shut down");
915     }
916     return this.master.getClusterStatus();
917   }
918 
919   private HRegionLocation getFirstMetaServerForTable(final byte [] tableName)
920   throws IOException {
921     return connection.locateRegion(HConstants.META_TABLE_NAME,
922       HRegionInfo.createRegionName(tableName, null, HConstants.NINES, false));
923   }
924 
925   /**
926    * Check to see if HBase is running. Throw an exception if not.
927    *
928    * @param conf system configuration
929    * @throws MasterNotRunningException if a remote or network exception occurs
930    */
931   public static void checkHBaseAvailable(Configuration conf)
932   throws MasterNotRunningException {
933     Configuration copyOfConf = HBaseConfiguration.create(conf);
934     copyOfConf.setInt("hbase.client.retries.number", 1);
935     new HBaseAdmin(copyOfConf);
936   }
937 }