View Javadoc

1   /**
2    * Copyright 2011 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.Closeable;
23  import java.io.IOException;
24  import java.io.InterruptedIOException;
25  import java.lang.reflect.Proxy;
26  import java.lang.reflect.UndeclaredThrowableException;
27  import java.net.SocketTimeoutException;
28  import java.util.Arrays;
29  import java.util.LinkedList;
30  import java.util.List;
31  import java.util.concurrent.atomic.AtomicInteger;
32  import java.util.concurrent.atomic.AtomicReference;
33  import java.util.regex.Pattern;
34  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.hadoop.conf.Configuration;
38  import org.apache.hadoop.hbase.Abortable;
39  import org.apache.hadoop.hbase.ClusterStatus;
40  import org.apache.hadoop.hbase.HBaseConfiguration;
41  import org.apache.hadoop.hbase.HColumnDescriptor;
42  import org.apache.hadoop.hbase.HConstants;
43  import org.apache.hadoop.hbase.HRegionInfo;
44  import org.apache.hadoop.hbase.HRegionLocation;
45  import org.apache.hadoop.hbase.HTableDescriptor;
46  import org.apache.hadoop.hbase.MasterNotRunningException;
47  import org.apache.hadoop.hbase.NotServingRegionException;
48  import org.apache.hadoop.hbase.RegionException;
49  import org.apache.hadoop.hbase.RemoteExceptionHandler;
50  import org.apache.hadoop.hbase.ServerName;
51  import org.apache.hadoop.hbase.TableExistsException;
52  import org.apache.hadoop.hbase.TableNotEnabledException;
53  import org.apache.hadoop.hbase.TableNotFoundException;
54  import org.apache.hadoop.hbase.UnknownRegionException;
55  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
56  import org.apache.hadoop.hbase.catalog.CatalogTracker;
57  import org.apache.hadoop.hbase.catalog.MetaReader;
58  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
59  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
60  import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
61  import org.apache.hadoop.hbase.ipc.HMasterInterface;
62  import org.apache.hadoop.hbase.ipc.HRegionInterface;
63  import org.apache.hadoop.hbase.ipc.MasterExecRPCInvoker;
64  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
65  import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest.CompactionState;
66  import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
67  import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
68  import org.apache.hadoop.hbase.snapshot.HSnapshotDescription;
69  import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
70  import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
71  import org.apache.hadoop.hbase.snapshot.SnapshotDescriptionUtils;
72  import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
73  import org.apache.hadoop.hbase.util.Addressing;
74  import org.apache.hadoop.hbase.util.Bytes;
75  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
76  import org.apache.hadoop.hbase.util.Pair;
77  import org.apache.hadoop.ipc.RemoteException;
78  import org.apache.hadoop.util.StringUtils;
79  
80  import com.google.protobuf.ServiceException;
81  
82  /**
83   * Provides an interface to manage HBase database table metadata + general
84   * administrative functions.  Use HBaseAdmin to create, drop, list, enable and
85   * disable tables. Use it also to add and drop table column families.
86   *
87   * <p>See {@link HTable} to add, update, and delete data from an individual table.
88   * <p>Currently HBaseAdmin instances are not expected to be long-lived.  For
89   * example, an HBaseAdmin instance will not ride over a Master restart.
90   */
91  public class HBaseAdmin implements Abortable, Closeable {
92    private final Log LOG = LogFactory.getLog(this.getClass().getName());
93  //  private final HConnection connection;
94    private HConnection connection;
95    private volatile Configuration conf;
96    private final long pause;
97    private final int numRetries;
98    // Some operations can take a long time such as disable of big table.
99    // numRetries is for 'normal' stuff... Mutliply by this factor when
100   // want to wait a long time.
101   private final int retryLongerMultiplier;
102   private boolean aborted;
103 
104   private static volatile boolean synchronousBalanceSwitchSupported = true;
105 
106   /**
107    * Constructor
108    *
109    * @param c Configuration object
110    * @throws MasterNotRunningException if the master is not running
111    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
112    */
113   public HBaseAdmin(Configuration c)
114   throws MasterNotRunningException, ZooKeeperConnectionException {
115     this.conf = HBaseConfiguration.create(c);
116     this.connection = HConnectionManager.getConnection(this.conf);
117     this.pause = this.conf.getLong("hbase.client.pause", 1000);
118     this.numRetries = this.conf.getInt("hbase.client.retries.number", 10);
119     this.retryLongerMultiplier = this.conf.getInt(
120         "hbase.client.retries.longer.multiplier", 10);
121 
122     int tries = 0;
123     while ( true ){
124       try {
125 
126         this.connection.getMaster();
127         return;
128 
129       } catch (MasterNotRunningException mnre) {
130         HConnectionManager.deleteStaleConnection(this.connection);
131         this.connection = HConnectionManager.getConnection(this.conf);
132       }
133 
134       tries++;
135       if (tries >= numRetries) {
136         // we should delete connection between client and zookeeper
137         HConnectionManager.deleteStaleConnection(this.connection);
138         throw new MasterNotRunningException("Retried " + numRetries + " times");
139       }
140 
141       try {
142         Thread.sleep(getPauseTime(tries));
143       } catch (InterruptedException e) {
144         Thread.currentThread().interrupt();
145         // we should delete connection between client and zookeeper
146         HConnectionManager.deleteStaleConnection(this.connection);
147         throw new MasterNotRunningException(
148           "Interrupted after "+tries+" tries");
149       }
150     }
151   }
152 
153  /**
154    * Constructor for externally managed HConnections.
155    * This constructor fails fast if the HMaster is not running.
156    * The HConnection can be re-used again in another attempt.
157    * This constructor fails fast.
158    *
159    * @param connection The HConnection instance to use
160    * @throws MasterNotRunningException if the master is not running
161    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
162    */
163   public HBaseAdmin(HConnection connection)
164       throws MasterNotRunningException, ZooKeeperConnectionException {
165     this.conf = connection.getConfiguration();
166     this.connection = connection;
167 
168     this.pause = this.conf.getLong("hbase.client.pause", 1000);
169     this.numRetries = this.conf.getInt("hbase.client.retries.number", 10);
170     this.retryLongerMultiplier = this.conf.getInt(
171         "hbase.client.retries.longer.multiplier", 10);
172 
173     this.connection.getMaster();
174   }
175 
176   /**
177    * @return A new CatalogTracker instance; call {@link #cleanupCatalogTracker(CatalogTracker)}
178    * to cleanup the returned catalog tracker.
179    * @throws ZooKeeperConnectionException
180    * @throws IOException
181    * @see #cleanupCatalogTracker(CatalogTracker)
182    */
183   private synchronized CatalogTracker getCatalogTracker()
184   throws ZooKeeperConnectionException, IOException {
185     CatalogTracker ct = null;
186     try {
187       ct = new CatalogTracker(this.conf);
188       ct.start();
189     } catch (InterruptedException e) {
190       // Let it out as an IOE for now until we redo all so tolerate IEs
191       Thread.currentThread().interrupt();
192       throw new IOException("Interrupted", e);
193     }
194     return ct;
195   }
196 
197   private void cleanupCatalogTracker(final CatalogTracker ct) {
198     ct.stop();
199   }
200 
201   @Override
202   public void abort(String why, Throwable e) {
203     // Currently does nothing but throw the passed message and exception
204     this.aborted = true;
205     throw new RuntimeException(why, e);
206   }
207 
208   @Override
209   public boolean isAborted(){
210     return this.aborted;
211   }
212 
213   /** @return HConnection used by this object. */
214   public HConnection getConnection() {
215     return connection;
216   }
217 
218   /**
219    * Get a connection to the currently set master.
220    * @return proxy connection to master server for this instance
221    * @throws MasterNotRunningException if the master is not running
222    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
223    * @deprecated  Master is an implementation detail for HBaseAdmin.
224    * Deprecated in HBase 0.94
225    */
226   @Deprecated
227   public HMasterInterface getMaster()
228   throws MasterNotRunningException, ZooKeeperConnectionException {
229     return this.connection.getMaster();
230   }
231 
232   /** @return - true if the master server is running
233    * @throws ZooKeeperConnectionException
234    * @throws MasterNotRunningException */
235   public boolean isMasterRunning()
236   throws MasterNotRunningException, ZooKeeperConnectionException {
237     return this.connection.isMasterRunning();
238   }
239 
240   /**
241    * @param tableName Table to check.
242    * @return True if table exists already.
243    * @throws IOException
244    */
245   public boolean tableExists(final String tableName)
246   throws IOException {
247     boolean b = false;
248     CatalogTracker ct = getCatalogTracker();
249     try {
250       b = MetaReader.tableExists(ct, tableName);
251     } finally {
252       cleanupCatalogTracker(ct);
253     }
254     return b;
255   }
256 
257   /**
258    * @param tableName Table to check.
259    * @return True if table exists already.
260    * @throws IOException
261    */
262   public boolean tableExists(final byte [] tableName)
263   throws IOException {
264     return tableExists(Bytes.toString(tableName));
265   }
266 
267   /**
268    * List all the userspace tables.  In other words, scan the META table.
269    *
270    * If we wanted this to be really fast, we could implement a special
271    * catalog table that just contains table names and their descriptors.
272    * Right now, it only exists as part of the META table's region info.
273    *
274    * @return - returns an array of HTableDescriptors
275    * @throws IOException if a remote or network exception occurs
276    */
277   public HTableDescriptor[] listTables() throws IOException {
278     return this.connection.listTables();
279   }
280 
281   /**
282    * List all the userspace tables matching the given pattern.
283    *
284    * @param pattern The compiled regular expression to match against
285    * @return - returns an array of HTableDescriptors
286    * @throws IOException if a remote or network exception occurs
287    * @see #listTables()
288    */
289   public HTableDescriptor[] listTables(Pattern pattern) throws IOException {
290     List<HTableDescriptor> matched = new LinkedList<HTableDescriptor>();
291     HTableDescriptor[] tables = listTables();
292     for (HTableDescriptor table : tables) {
293       if (pattern.matcher(table.getNameAsString()).matches()) {
294         matched.add(table);
295       }
296     }
297     return matched.toArray(new HTableDescriptor[matched.size()]);
298   }
299 
300   /**
301    * List all the userspace tables matching the given regular expression.
302    *
303    * @param regex The regular expression to match against
304    * @return - returns an array of HTableDescriptors
305    * @throws IOException if a remote or network exception occurs
306    * @see #listTables(java.util.regex.Pattern)
307    */
308   public HTableDescriptor[] listTables(String regex) throws IOException {
309     return listTables(Pattern.compile(regex));
310   }
311 
312 
313   /**
314    * Method for getting the tableDescriptor
315    * @param tableName as a byte []
316    * @return the tableDescriptor
317    * @throws TableNotFoundException
318    * @throws IOException if a remote or network exception occurs
319    */
320   public HTableDescriptor getTableDescriptor(final byte [] tableName)
321   throws TableNotFoundException, IOException {
322     return this.connection.getHTableDescriptor(tableName);
323   }
324 
325   private long getPauseTime(int tries) {
326     int triesCount = tries;
327     if (triesCount >= HConstants.RETRY_BACKOFF.length) {
328       triesCount = HConstants.RETRY_BACKOFF.length - 1;
329     }
330     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
331   }
332 
333   /**
334    * Creates a new table.
335    * Synchronous operation.
336    *
337    * @param desc table descriptor for table
338    *
339    * @throws IllegalArgumentException if the table name is reserved
340    * @throws MasterNotRunningException if master is not running
341    * @throws TableExistsException if table already exists (If concurrent
342    * threads, the table may have been created between test-for-existence
343    * and attempt-at-creation).
344    * @throws IOException if a remote or network exception occurs
345    */
346   public void createTable(HTableDescriptor desc)
347   throws IOException {
348     createTable(desc, null);
349   }
350 
351   /**
352    * Creates a new table with the specified number of regions.  The start key
353    * specified will become the end key of the first region of the table, and
354    * the end key specified will become the start key of the last region of the
355    * table (the first region has a null start key and the last region has a
356    * null end key).
357    *
358    * BigInteger math will be used to divide the key range specified into
359    * enough segments to make the required number of total regions.
360    *
361    * Synchronous operation.
362    *
363    * @param desc table descriptor for table
364    * @param startKey beginning of key range
365    * @param endKey end of key range
366    * @param numRegions the total number of regions to create
367    *
368    * @throws IllegalArgumentException if the table name is reserved
369    * @throws MasterNotRunningException if master is not running
370    * @throws TableExistsException if table already exists (If concurrent
371    * threads, the table may have been created between test-for-existence
372    * and attempt-at-creation).
373    * @throws IOException
374    */
375   public void createTable(HTableDescriptor desc, byte [] startKey,
376       byte [] endKey, int numRegions)
377   throws IOException {
378     HTableDescriptor.isLegalTableName(desc.getName());
379     if(numRegions < 3) {
380       throw new IllegalArgumentException("Must create at least three regions");
381     } else if(Bytes.compareTo(startKey, endKey) >= 0) {
382       throw new IllegalArgumentException("Start key must be smaller than end key");
383     }
384     byte [][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
385     if(splitKeys == null || splitKeys.length != numRegions - 1) {
386       throw new IllegalArgumentException("Unable to split key range into enough regions");
387     }
388     createTable(desc, splitKeys);
389   }
390 
391   /**
392    * Creates a new table with an initial set of empty regions defined by the
393    * specified split keys.  The total number of regions created will be the
394    * number of split keys plus one. Synchronous operation.
395    * Note : Avoid passing empty split key.
396    *
397    * @param desc table descriptor for table
398    * @param splitKeys array of split keys for the initial regions of the table
399    *
400    * @throws IllegalArgumentException if the table name is reserved, if the split keys
401    * are repeated and if the split key has empty byte array.
402    * @throws MasterNotRunningException if master is not running
403    * @throws TableExistsException if table already exists (If concurrent
404    * threads, the table may have been created between test-for-existence
405    * and attempt-at-creation).
406    * @throws IOException
407    */
408   public void createTable(final HTableDescriptor desc, byte [][] splitKeys)
409   throws IOException {
410     HTableDescriptor.isLegalTableName(desc.getName());
411     try {
412       createTableAsync(desc, splitKeys);
413     } catch (SocketTimeoutException ste) {
414       LOG.warn("Creating " + desc.getNameAsString() + " took too long", ste);
415     }
416     int numRegs = splitKeys == null ? 1 : splitKeys.length + 1;
417     int prevRegCount = 0;
418     boolean doneWithMetaScan = false;
419     for (int tries = 0; tries < this.numRetries * this.retryLongerMultiplier;
420       ++tries) {
421       if (!doneWithMetaScan) {
422         // Wait for new table to come on-line
423         final AtomicInteger actualRegCount = new AtomicInteger(0);
424         MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
425           @Override
426           public boolean processRow(Result rowResult) throws IOException {
427             if (rowResult == null || rowResult.size() <= 0) {
428               return true;
429             }
430             HRegionInfo info = MetaReader.parseHRegionInfoFromCatalogResult(
431               rowResult, HConstants.REGIONINFO_QUALIFIER);
432             if (info == null) {
433               LOG.warn("No serialized HRegionInfo in " + rowResult);
434               return true;
435             }
436             if (!(Bytes.equals(info.getTableName(), desc.getName()))) {
437               return false;
438             }
439             String hostAndPort = null;
440             byte [] value = rowResult.getValue(HConstants.CATALOG_FAMILY,
441               HConstants.SERVER_QUALIFIER);
442             // Make sure that regions are assigned to server
443             if (value != null && value.length > 0) {
444               hostAndPort = Bytes.toString(value);
445             }
446             if (!(info.isOffline() || info.isSplit()) && hostAndPort != null) {
447               actualRegCount.incrementAndGet();
448             }
449             return true;
450           }
451         };
452         MetaScanner.metaScan(conf, visitor, desc.getName());
453         if (actualRegCount.get() != numRegs) {
454           if (tries == this.numRetries * this.retryLongerMultiplier - 1) {
455             throw new RegionOfflineException("Only " + actualRegCount.get() +
456               " of " + numRegs + " regions are online; retries exhausted.");
457           }
458           try { // Sleep
459             Thread.sleep(getPauseTime(tries));
460           } catch (InterruptedException e) {
461             throw new InterruptedIOException("Interrupted when opening" +
462               " regions; " + actualRegCount.get() + " of " + numRegs +
463               " regions processed so far");
464           }
465           if (actualRegCount.get() > prevRegCount) { // Making progress
466             prevRegCount = actualRegCount.get();
467             tries = -1;
468           }
469         } else {
470           doneWithMetaScan = true;
471           tries = -1;
472         }
473       } else if (isTableEnabled(desc.getName())) {
474         return;
475       } else {
476         try { // Sleep
477           Thread.sleep(getPauseTime(tries));
478         } catch (InterruptedException e) {
479           throw new InterruptedIOException("Interrupted when waiting" +
480             " for table to be enabled; meta scan was done");
481         }
482       }
483     }
484     throw new TableNotEnabledException(
485       "Retries exhausted while still waiting for table: "
486       + desc.getNameAsString() + " to be enabled");
487   }
488 
489   /**
490    * Creates a new table but does not block and wait for it to come online.
491    * Asynchronous operation.  To check if the table exists, use
492    * {@link: #isTableAvailable} -- it is not safe to create an HTable
493    * instance to this table before it is available.
494    * Note : Avoid passing empty split key.
495    * @param desc table descriptor for table
496    *
497    * @throws IllegalArgumentException Bad table name, if the split keys
498    * are repeated and if the split key has empty byte array.
499    * @throws MasterNotRunningException if master is not running
500    * @throws TableExistsException if table already exists (If concurrent
501    * threads, the table may have been created between test-for-existence
502    * and attempt-at-creation).
503    * @throws IOException
504    */
505   public void createTableAsync(HTableDescriptor desc, byte [][] splitKeys)
506   throws IOException {
507     HTableDescriptor.isLegalTableName(desc.getName());
508     if(splitKeys != null && splitKeys.length > 0) {
509       Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
510       // Verify there are no duplicate split keys
511       byte [] lastKey = null;
512       for(byte [] splitKey : splitKeys) {
513         if (Bytes.compareTo(splitKey, HConstants.EMPTY_BYTE_ARRAY) == 0) {
514           throw new IllegalArgumentException(
515               "Empty split key must not be passed in the split keys.");
516         }
517         if(lastKey != null && Bytes.equals(splitKey, lastKey)) {
518           throw new IllegalArgumentException("All split keys must be unique, " +
519             "found duplicate: " + Bytes.toStringBinary(splitKey) +
520             ", " + Bytes.toStringBinary(lastKey));
521         }
522         lastKey = splitKey;
523       }
524     }
525     try {
526       getMaster().createTable(desc, splitKeys);
527     } catch (RemoteException e) {
528       throw e.unwrapRemoteException();
529     }
530   }
531 
532   /**
533    * Deletes a table.
534    * Synchronous operation.
535    *
536    * @param tableName name of table to delete
537    * @throws IOException if a remote or network exception occurs
538    */
539   public void deleteTable(final String tableName) throws IOException {
540     deleteTable(Bytes.toBytes(tableName));
541   }
542 
543   /**
544    * Deletes a table.
545    * Synchronous operation.
546    *
547    * @param tableName name of table to delete
548    * @throws IOException if a remote or network exception occurs
549    */
550   public void deleteTable(final byte [] tableName) throws IOException {
551     isMasterRunning();
552     HTableDescriptor.isLegalTableName(tableName);
553     HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName);
554     boolean tableExists = true;
555     try {
556       getMaster().deleteTable(tableName);
557     } catch (RemoteException e) {
558       throw RemoteExceptionHandler.decodeRemoteException(e);
559     }
560     // Wait until all regions deleted
561     HRegionInterface server =
562       connection.getHRegionConnection(firstMetaServer.getHostname(), firstMetaServer.getPort());
563     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
564       long scannerId = -1L;
565       try {
566 
567         Scan scan = MetaReader.getScanForTableName(tableName);
568         scan.addColumn(HConstants.CATALOG_FAMILY,
569             HConstants.REGIONINFO_QUALIFIER);
570         scannerId = server.openScanner(
571           firstMetaServer.getRegionInfo().getRegionName(), scan);
572         // Get a batch at a time.
573         Result values = server.next(scannerId);
574 
575         // let us wait until .META. table is updated and
576         // HMaster removes the table from its HTableDescriptors
577         if (values == null) {
578           tableExists = false;
579           HTableDescriptor[] htds = getMaster().getHTableDescriptors();
580           if (htds != null && htds.length > 0) {
581             for (HTableDescriptor htd: htds) {
582               if (Bytes.equals(tableName, htd.getName())) {
583                 tableExists = true;
584                 break;
585               }
586             }
587           }
588           if (!tableExists) {
589             break;
590           }
591         }
592       } catch (IOException ex) {
593         if(tries == numRetries - 1) {           // no more tries left
594           if (ex instanceof RemoteException) {
595             ex = RemoteExceptionHandler.decodeRemoteException((RemoteException) ex);
596           }
597           throw ex;
598         }
599       } finally {
600         if (scannerId != -1L) {
601           try {
602             server.close(scannerId);
603           } catch (Exception ex) {
604             LOG.warn(ex);
605           }
606         }
607       }
608       try {
609         Thread.sleep(getPauseTime(tries));
610       } catch (InterruptedException e) {
611         // continue
612       }
613     }
614 
615     if (tableExists) {
616       throw new IOException("Retries exhausted, it took too long to wait"+
617         " for the table " + Bytes.toString(tableName) + " to be deleted.");
618     }
619     // Delete cached information to prevent clients from using old locations
620     this.connection.clearRegionCache(tableName);
621     LOG.info("Deleted " + Bytes.toString(tableName));
622   }
623 
624   /**
625    * Deletes tables matching the passed in pattern and wait on completion.
626    *
627    * Warning: Use this method carefully, there is no prompting and the effect is
628    * immediate. Consider using {@link #listTables(java.lang.String)} and
629    * {@link #deleteTable(byte[])}
630    *
631    * @param regex The regular expression to match table names against
632    * @return Table descriptors for tables that couldn't be deleted
633    * @throws IOException
634    * @see #deleteTables(java.util.regex.Pattern)
635    * @see #deleteTable(java.lang.String)
636    */
637   public HTableDescriptor[] deleteTables(String regex) throws IOException {
638     return deleteTables(Pattern.compile(regex));
639   }
640 
641   /**
642    * Delete tables matching the passed in pattern and wait on completion.
643    *
644    * Warning: Use this method carefully, there is no prompting and the effect is
645    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
646    * {@link #deleteTable(byte[])}
647    *
648    * @param pattern The pattern to match table names against
649    * @return Table descriptors for tables that couldn't be deleted
650    * @throws IOException
651    */
652   public HTableDescriptor[] deleteTables(Pattern pattern) throws IOException {
653     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
654     for (HTableDescriptor table : listTables(pattern)) {
655       try {
656         deleteTable(table.getName());
657       } catch (IOException ex) {
658         LOG.info("Failed to delete table " + table.getNameAsString(), ex);
659         failed.add(table);
660       }
661     }
662     return failed.toArray(new HTableDescriptor[failed.size()]);
663   }
664 
665 
666   public void enableTable(final String tableName)
667   throws IOException {
668     enableTable(Bytes.toBytes(tableName));
669   }
670 
671   /**
672    * Enable a table.  May timeout.  Use {@link #enableTableAsync(byte[])}
673    * and {@link #isTableEnabled(byte[])} instead.
674    * The table has to be in disabled state for it to be enabled.
675    * @param tableName name of the table
676    * @throws IOException if a remote or network exception occurs
677    * There could be couple types of IOException
678    * TableNotFoundException means the table doesn't exist.
679    * TableNotDisabledException means the table isn't in disabled state.
680    * @see #isTableEnabled(byte[])
681    * @see #disableTable(byte[])
682    * @see #enableTableAsync(byte[])
683    */
684   public void enableTable(final byte [] tableName)
685   throws IOException {
686     enableTableAsync(tableName);
687 
688     // Wait until all regions are enabled
689     waitUntilTableIsEnabled(tableName);
690 
691     LOG.info("Enabled table " + Bytes.toString(tableName));
692   }
693 
694   /**
695    * Wait for the table to be enabled and available
696    * If enabling the table exceeds the retry period, an exception is thrown.
697    * @param tableName name of the table
698    * @throws IOException if a remote or network exception occurs or
699    *    table is not enabled after the retries period.
700    */
701   private void waitUntilTableIsEnabled(final byte[] tableName) throws IOException {
702     boolean enabled = false;
703     long start = EnvironmentEdgeManager.currentTimeMillis();
704     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
705       enabled = isTableEnabled(tableName) && isTableAvailable(tableName);
706       if (enabled) {
707         break;
708       }
709       long sleep = getPauseTime(tries);
710       if (LOG.isDebugEnabled()) {
711         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
712           "enabled in " + Bytes.toString(tableName));
713       }
714       try {
715         Thread.sleep(sleep);
716       } catch (InterruptedException e) {
717         Thread.currentThread().interrupt();
718         // Do this conversion rather than let it out because do not want to
719         // change the method signature.
720         throw new IOException("Interrupted", e);
721       }
722     }
723     if (!enabled) {
724       long msec = EnvironmentEdgeManager.currentTimeMillis() - start;
725       throw new IOException("Table '" + Bytes.toString(tableName) +
726         "' not yet enabled, after " + msec + "ms.");
727     }
728   }
729 
730   public void enableTableAsync(final String tableName)
731   throws IOException {
732     enableTableAsync(Bytes.toBytes(tableName));
733   }
734 
735   /**
736    * Brings a table on-line (enables it).  Method returns immediately though
737    * enable of table may take some time to complete, especially if the table
738    * is large (All regions are opened as part of enabling process).  Check
739    * {@link #isTableEnabled(byte[])} to learn when table is fully online.  If
740    * table is taking too long to online, check server logs.
741    * @param tableName
742    * @throws IOException
743    * @since 0.90.0
744    */
745   public void enableTableAsync(final byte [] tableName)
746   throws IOException {
747     HTableDescriptor.isLegalTableName(tableName);
748     isMasterRunning();
749     try {
750       getMaster().enableTable(tableName);
751     } catch (RemoteException e) {
752       throw e.unwrapRemoteException();
753     }
754     LOG.info("Started enable of " + Bytes.toString(tableName));
755   }
756 
757   /**
758    * Enable tables matching the passed in pattern and wait on completion.
759    *
760    * Warning: Use this method carefully, there is no prompting and the effect is
761    * immediate. Consider using {@link #listTables(java.lang.String)} and
762    * {@link #enableTable(byte[])}
763    *
764    * @param regex The regular expression to match table names against
765    * @throws IOException
766    * @see #enableTables(java.util.regex.Pattern)
767    * @see #enableTable(java.lang.String)
768    */
769   public HTableDescriptor[] enableTables(String regex) throws IOException {
770     return enableTables(Pattern.compile(regex));
771   }
772 
773   /**
774    * Enable tables matching the passed in pattern and wait on completion.
775    *
776    * Warning: Use this method carefully, there is no prompting and the effect is
777    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
778    * {@link #enableTable(byte[])}
779    *
780    * @param pattern The pattern to match table names against
781    * @throws IOException
782    */
783   public HTableDescriptor[] enableTables(Pattern pattern) throws IOException {
784     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
785     for (HTableDescriptor table : listTables(pattern)) {
786       if (isTableDisabled(table.getName())) {
787         try {
788           enableTable(table.getName());
789         } catch (IOException ex) {
790           LOG.info("Failed to enable table " + table.getNameAsString(), ex);
791           failed.add(table);
792         }
793       }
794     }
795     return failed.toArray(new HTableDescriptor[failed.size()]);
796   }
797 
798   public void disableTableAsync(final String tableName) throws IOException {
799     disableTableAsync(Bytes.toBytes(tableName));
800   }
801 
802   /**
803    * Starts the disable of a table.  If it is being served, the master
804    * will tell the servers to stop serving it.  This method returns immediately.
805    * The disable of a table can take some time if the table is large (all
806    * regions are closed as part of table disable operation).
807    * Call {@link #isTableDisabled(byte[])} to check for when disable completes.
808    * If table is taking too long to online, check server logs.
809    * @param tableName name of table
810    * @throws IOException if a remote or network exception occurs
811    * @see #isTableDisabled(byte[])
812    * @see #isTableEnabled(byte[])
813    * @since 0.90.0
814    */
815   public void disableTableAsync(final byte [] tableName) throws IOException {
816     HTableDescriptor.isLegalTableName(tableName);
817     isMasterRunning();
818     try {
819       getMaster().disableTable(tableName);
820     } catch (RemoteException e) {
821       throw e.unwrapRemoteException();
822     }
823     LOG.info("Started disable of " + Bytes.toString(tableName));
824   }
825 
826   public void disableTable(final String tableName)
827   throws IOException {
828     disableTable(Bytes.toBytes(tableName));
829   }
830 
831   /**
832    * Disable table and wait on completion.  May timeout eventually.  Use
833    * {@link #disableTableAsync(byte[])} and {@link #isTableDisabled(String)}
834    * instead.
835    * The table has to be in enabled state for it to be disabled.
836    * @param tableName
837    * @throws IOException
838    * There could be couple types of IOException
839    * TableNotFoundException means the table doesn't exist.
840    * TableNotEnabledException means the table isn't in enabled state.
841    */
842   public void disableTable(final byte [] tableName)
843   throws IOException {
844     disableTableAsync(tableName);
845     // Wait until table is disabled
846     boolean disabled = false;
847     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
848       disabled = isTableDisabled(tableName);
849       if (disabled) {
850         break;
851       }
852       long sleep = getPauseTime(tries);
853       if (LOG.isDebugEnabled()) {
854         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
855           "disabled in " + Bytes.toString(tableName));
856       }
857       try {
858         Thread.sleep(sleep);
859       } catch (InterruptedException e) {
860         // Do this conversion rather than let it out because do not want to
861         // change the method signature.
862         Thread.currentThread().interrupt();
863         throw new IOException("Interrupted", e);
864       }
865     }
866     if (!disabled) {
867       throw new RegionException("Retries exhausted, it took too long to wait"+
868         " for the table " + Bytes.toString(tableName) + " to be disabled.");
869     }
870     LOG.info("Disabled " + Bytes.toString(tableName));
871   }
872 
873   /**
874    * Disable tables matching the passed in pattern and wait on completion.
875    *
876    * Warning: Use this method carefully, there is no prompting and the effect is
877    * immediate. Consider using {@link #listTables(java.lang.String)} and
878    * {@link #disableTable(byte[])}
879    *
880    * @param regex The regular expression to match table names against
881    * @return Table descriptors for tables that couldn't be disabled
882    * @throws IOException
883    * @see #disableTables(java.util.regex.Pattern)
884    * @see #disableTable(java.lang.String)
885    */
886   public HTableDescriptor[] disableTables(String regex) throws IOException {
887     return disableTables(Pattern.compile(regex));
888   }
889 
890   /**
891    * Disable tables matching the passed in pattern and wait on completion.
892    *
893    * Warning: Use this method carefully, there is no prompting and the effect is
894    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
895    * {@link #disableTable(byte[])}
896    *
897    * @param pattern The pattern to match table names against
898    * @return Table descriptors for tables that couldn't be disabled
899    * @throws IOException
900    */
901   public HTableDescriptor[] disableTables(Pattern pattern) throws IOException {
902     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
903     for (HTableDescriptor table : listTables(pattern)) {
904       if (isTableEnabled(table.getName())) {
905         try {
906           disableTable(table.getName());
907         } catch (IOException ex) {
908           LOG.info("Failed to disable table " + table.getNameAsString(), ex);
909           failed.add(table);
910         }
911       }
912     }
913     return failed.toArray(new HTableDescriptor[failed.size()]);
914   }
915 
916   /**
917    * @param tableName name of table to check
918    * @return true if table is on-line
919    * @throws IOException if a remote or network exception occurs
920    */
921   public boolean isTableEnabled(String tableName) throws IOException {
922     return isTableEnabled(Bytes.toBytes(tableName));
923   }
924   /**
925    * @param tableName name of table to check
926    * @return true if table is on-line
927    * @throws IOException if a remote or network exception occurs
928    */
929   public boolean isTableEnabled(byte[] tableName) throws IOException {
930     if (!HTableDescriptor.isMetaTable(tableName)) {
931       HTableDescriptor.isLegalTableName(tableName);
932     }
933     return connection.isTableEnabled(tableName);
934   }
935 
936   /**
937    * @param tableName name of table to check
938    * @return true if table is off-line
939    * @throws IOException if a remote or network exception occurs
940    */
941   public boolean isTableDisabled(final String tableName) throws IOException {
942     return isTableDisabled(Bytes.toBytes(tableName));
943   }
944 
945   /**
946    * @param tableName name of table to check
947    * @return true if table is off-line
948    * @throws IOException if a remote or network exception occurs
949    */
950   public boolean isTableDisabled(byte[] tableName) throws IOException {
951     if (!HTableDescriptor.isMetaTable(tableName)) {
952       HTableDescriptor.isLegalTableName(tableName);
953     }
954     return connection.isTableDisabled(tableName);
955   }
956 
957   /**
958    * @param tableName name of table to check
959    * @return true if all regions of the table are available
960    * @throws IOException if a remote or network exception occurs
961    */
962   public boolean isTableAvailable(byte[] tableName) throws IOException {
963     return connection.isTableAvailable(tableName);
964   }
965 
966   /**
967    * @param tableName name of table to check
968    * @return true if all regions of the table are available
969    * @throws IOException if a remote or network exception occurs
970    */
971   public boolean isTableAvailable(String tableName) throws IOException {
972     return connection.isTableAvailable(Bytes.toBytes(tableName));
973   }
974 
975   /**
976    * Get the status of alter command - indicates how many regions have received
977    * the updated schema Asynchronous operation.
978    *
979    * @param tableName
980    *          name of the table to get the status of
981    * @return Pair indicating the number of regions updated Pair.getFirst() is the
982    *         regions that are yet to be updated Pair.getSecond() is the total number
983    *         of regions of the table
984    * @throws IOException
985    *           if a remote or network exception occurs
986    */
987   public Pair<Integer, Integer> getAlterStatus(final byte[] tableName)
988   throws IOException {
989     HTableDescriptor.isLegalTableName(tableName);
990     try {
991       return getMaster().getAlterStatus(tableName);
992     } catch (RemoteException e) {
993       throw RemoteExceptionHandler.decodeRemoteException(e);
994     }
995   }
996 
997   /**
998    * Add a column to an existing table.
999    * Asynchronous operation.
1000    *
1001    * @param tableName name of the table to add column to
1002    * @param column column descriptor of column to be added
1003    * @throws IOException if a remote or network exception occurs
1004    */
1005   public void addColumn(final String tableName, HColumnDescriptor column)
1006   throws IOException {
1007     addColumn(Bytes.toBytes(tableName), column);
1008   }
1009 
1010   /**
1011    * Add a column to an existing table.
1012    * Asynchronous operation.
1013    *
1014    * @param tableName name of the table to add column to
1015    * @param column column descriptor of column to be added
1016    * @throws IOException if a remote or network exception occurs
1017    */
1018   public void addColumn(final byte [] tableName, HColumnDescriptor column)
1019   throws IOException {
1020     HTableDescriptor.isLegalTableName(tableName);
1021     try {
1022       getMaster().addColumn(tableName, column);
1023     } catch (RemoteException e) {
1024       throw RemoteExceptionHandler.decodeRemoteException(e);
1025     }
1026   }
1027 
1028   /**
1029    * Delete a column from a table.
1030    * Asynchronous operation.
1031    *
1032    * @param tableName name of table
1033    * @param columnName name of column to be deleted
1034    * @throws IOException if a remote or network exception occurs
1035    */
1036   public void deleteColumn(final String tableName, final String columnName)
1037   throws IOException {
1038     deleteColumn(Bytes.toBytes(tableName), Bytes.toBytes(columnName));
1039   }
1040 
1041   /**
1042    * Delete a column from a table.
1043    * Asynchronous operation.
1044    *
1045    * @param tableName name of table
1046    * @param columnName name of column to be deleted
1047    * @throws IOException if a remote or network exception occurs
1048    */
1049   public void deleteColumn(final byte [] tableName, final byte [] columnName)
1050   throws IOException {
1051     try {
1052       getMaster().deleteColumn(tableName, columnName);
1053     } catch (RemoteException e) {
1054       throw RemoteExceptionHandler.decodeRemoteException(e);
1055     }
1056   }
1057 
1058   /**
1059    * Modify an existing column family on a table.
1060    * Asynchronous operation.
1061    *
1062    * @param tableName name of table
1063    * @param descriptor new column descriptor to use
1064    * @throws IOException if a remote or network exception occurs
1065    */
1066   public void modifyColumn(final String tableName, HColumnDescriptor descriptor)
1067   throws IOException {
1068     modifyColumn(Bytes.toBytes(tableName), descriptor);
1069   }
1070 
1071   /**
1072    * Modify an existing column family on a table.
1073    * Asynchronous operation.
1074    *
1075    * @param tableName name of table
1076    * @param descriptor new column descriptor to use
1077    * @throws IOException if a remote or network exception occurs
1078    */
1079   public void modifyColumn(final byte [] tableName, HColumnDescriptor descriptor)
1080   throws IOException {
1081     try {
1082       getMaster().modifyColumn(tableName, descriptor);
1083     } catch (RemoteException re) {
1084       // Convert RE exceptions in here; client shouldn't have to deal with them,
1085       // at least w/ the type of exceptions that come out of this method:
1086       // TableNotFoundException, etc.
1087       throw RemoteExceptionHandler.decodeRemoteException(re);
1088     }
1089   }
1090 
1091   /**
1092    * Close a region. For expert-admins.  Runs close on the regionserver.  The
1093    * master will not be informed of the close.
1094    * @param regionname region name to close
1095    * @param serverName If supplied, we'll use this location rather than
1096    * the one currently in <code>.META.</code>
1097    * @throws IOException if a remote or network exception occurs
1098    */
1099   public void closeRegion(final String regionname, final String serverName)
1100   throws IOException {
1101     closeRegion(Bytes.toBytes(regionname), serverName);
1102   }
1103 
1104   /**
1105    * Close a region.  For expert-admins  Runs close on the regionserver.  The
1106    * master will not be informed of the close.
1107    * @param regionname region name to close
1108    * @param serverName The servername of the regionserver.  If passed null we
1109    * will use servername found in the .META. table. A server name
1110    * is made of host, port and startcode.  Here is an example:
1111    * <code> host187.example.com,60020,1289493121758</code>
1112    * @throws IOException if a remote or network exception occurs
1113    */
1114   public void closeRegion(final byte [] regionname, final String serverName)
1115   throws IOException {
1116     CatalogTracker ct = getCatalogTracker();
1117     try {
1118       if (serverName != null) {
1119         Pair<HRegionInfo, ServerName> pair = MetaReader.getRegion(ct, regionname);
1120         if (pair == null || pair.getFirst() == null) {
1121           throw new UnknownRegionException(Bytes.toStringBinary(regionname));
1122         } else {
1123           closeRegion(new ServerName(serverName), pair.getFirst());
1124         }
1125       } else {
1126         Pair<HRegionInfo, ServerName> pair = MetaReader.getRegion(ct, regionname);
1127         if (pair == null) {
1128           throw new UnknownRegionException(Bytes.toStringBinary(regionname));
1129         } else if (pair.getSecond() == null) {
1130           throw new NoServerForRegionException(Bytes.toStringBinary(regionname));
1131         } else {
1132           closeRegion(pair.getSecond(), pair.getFirst());
1133         }
1134       }
1135     } finally {
1136       cleanupCatalogTracker(ct);
1137     }
1138   }
1139 
1140   /**
1141    * For expert-admins. Runs close on the regionserver. Closes a region based on
1142    * the encoded region name. The region server name is mandatory. If the
1143    * servername is provided then based on the online regions in the specified
1144    * regionserver the specified region will be closed. The master will not be
1145    * informed of the close. Note that the regionname is the encoded regionname.
1146    *
1147    * @param encodedRegionName
1148    *          The encoded region name; i.e. the hash that makes up the region
1149    *          name suffix: e.g. if regionname is
1150    *          <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>
1151    *          , then the encoded region name is:
1152    *          <code>527db22f95c8a9e0116f0cc13c680396</code>.
1153    * @param serverName
1154    *          The servername of the regionserver. A server name is made of host,
1155    *          port and startcode. This is mandatory. Here is an example:
1156    *          <code> host187.example.com,60020,1289493121758</code>
1157    * @return true if the region was closed, false if not.
1158    * @throws IOException
1159    *           if a remote or network exception occurs
1160    */
1161   public boolean closeRegionWithEncodedRegionName(final String encodedRegionName,
1162       final String serverName) throws IOException {
1163     byte[] encodedRegionNameInBytes = Bytes.toBytes(encodedRegionName);
1164     if (null == serverName || ("").equals(serverName.trim())) {
1165       throw new IllegalArgumentException(
1166           "The servername cannot be null or empty.");
1167     }
1168     ServerName sn = new ServerName(serverName);
1169     HRegionInterface rs = this.connection.getHRegionConnection(
1170         sn.getHostname(), sn.getPort());
1171     // Close the region without updating zk state.
1172     boolean isRegionClosed = rs.closeRegion(encodedRegionNameInBytes, false);
1173     if (false == isRegionClosed) {
1174       LOG.error("Not able to close the region " + encodedRegionName + ".");
1175     }
1176     return isRegionClosed;
1177   }
1178 
1179   /**
1180    * Close a region.  For expert-admins  Runs close on the regionserver.  The
1181    * master will not be informed of the close.
1182    * @param sn
1183    * @param hri
1184    * @throws IOException
1185    */
1186   public void closeRegion(final ServerName sn, final HRegionInfo hri)
1187   throws IOException {
1188     HRegionInterface rs =
1189       this.connection.getHRegionConnection(sn.getHostname(), sn.getPort());
1190     // Close the region without updating zk state.
1191     rs.closeRegion(hri, false);
1192   }
1193 
1194   /**
1195    * Flush a table or an individual region.
1196    * Asynchronous operation.
1197    *
1198    * @param tableNameOrRegionName table or region to flush
1199    * @throws IOException if a remote or network exception occurs
1200    * @throws InterruptedException
1201    */
1202   public void flush(final String tableNameOrRegionName)
1203   throws IOException, InterruptedException {
1204     flush(Bytes.toBytes(tableNameOrRegionName));
1205   }
1206 
1207   /**
1208    * Flush a table or an individual region.
1209    * Asynchronous operation.
1210    *
1211    * @param tableNameOrRegionName table or region to flush
1212    * @throws IOException if a remote or network exception occurs
1213    * @throws InterruptedException
1214    */
1215   public void flush(final byte [] tableNameOrRegionName)
1216   throws IOException, InterruptedException {
1217     CatalogTracker ct = getCatalogTracker();
1218     try {
1219       Pair<HRegionInfo, ServerName> regionServerPair
1220         = getRegion(tableNameOrRegionName, ct);
1221       if (regionServerPair != null) {
1222         if (regionServerPair.getSecond() == null) {
1223           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1224         } else {
1225           flush(regionServerPair.getSecond(), regionServerPair.getFirst());
1226         }
1227       } else {
1228         final String tableName = tableNameString(tableNameOrRegionName, ct);
1229         List<Pair<HRegionInfo, ServerName>> pairs =
1230           MetaReader.getTableRegionsAndLocations(ct,
1231               tableName);
1232         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1233           if (pair.getFirst().isOffline()) continue;
1234           if (pair.getSecond() == null) continue;
1235           try {
1236             flush(pair.getSecond(), pair.getFirst());
1237           } catch (NotServingRegionException e) {
1238             if (LOG.isDebugEnabled()) {
1239               LOG.debug("Trying to flush " + pair.getFirst() + ": " +
1240                 StringUtils.stringifyException(e));
1241             }
1242           }
1243         }
1244       }
1245     } finally {
1246       cleanupCatalogTracker(ct);
1247     }
1248   }
1249 
1250   private void flush(final ServerName sn, final HRegionInfo hri)
1251   throws IOException {
1252     HRegionInterface rs =
1253       this.connection.getHRegionConnection(sn.getHostname(), sn.getPort());
1254     rs.flushRegion(hri);
1255   }
1256 
1257   /**
1258    * Compact a table or an individual region.
1259    * Asynchronous operation.
1260    *
1261    * @param tableNameOrRegionName table or region to compact
1262    * @throws IOException if a remote or network exception occurs
1263    * @throws InterruptedException
1264    */
1265   public void compact(final String tableNameOrRegionName)
1266   throws IOException, InterruptedException {
1267     compact(Bytes.toBytes(tableNameOrRegionName));
1268   }
1269 
1270   /**
1271    * Compact a table or an individual region.
1272    * Asynchronous operation.
1273    *
1274    * @param tableNameOrRegionName table or region to compact
1275    * @throws IOException if a remote or network exception occurs
1276    * @throws InterruptedException
1277    */
1278   public void compact(final byte [] tableNameOrRegionName)
1279   throws IOException, InterruptedException {
1280     compact(tableNameOrRegionName, null, false);
1281   }
1282 
1283   /**
1284    * Compact a column family within a table or region.
1285    * Asynchronous operation.
1286    *
1287    * @param tableOrRegionName table or region to compact
1288    * @param columnFamily column family within a table or region
1289    * @throws IOException if a remote or network exception occurs
1290    * @throws InterruptedException
1291    */
1292   public void compact(String tableOrRegionName, String columnFamily)
1293     throws IOException,  InterruptedException {
1294     compact(Bytes.toBytes(tableOrRegionName), Bytes.toBytes(columnFamily));
1295   }
1296 
1297   /**
1298    * Compact a column family within a table or region.
1299    * Asynchronous operation.
1300    *
1301    * @param tableNameOrRegionName table or region to compact
1302    * @param columnFamily column family within a table or region
1303    * @throws IOException if a remote or network exception occurs
1304    * @throws InterruptedException
1305    */
1306   public void compact(final byte [] tableNameOrRegionName, final byte[] columnFamily)
1307   throws IOException, InterruptedException {
1308     compact(tableNameOrRegionName, columnFamily, false);
1309   }
1310 
1311   /**
1312    * Major compact a table or an individual region.
1313    * Asynchronous operation.
1314    *
1315    * @param tableNameOrRegionName table or region to major compact
1316    * @throws IOException if a remote or network exception occurs
1317    * @throws InterruptedException
1318    */
1319   public void majorCompact(final String tableNameOrRegionName)
1320   throws IOException, InterruptedException {
1321     majorCompact(Bytes.toBytes(tableNameOrRegionName));
1322   }
1323 
1324   /**
1325    * Major compact a table or an individual region.
1326    * Asynchronous operation.
1327    *
1328    * @param tableNameOrRegionName table or region to major compact
1329    * @throws IOException if a remote or network exception occurs
1330    * @throws InterruptedException
1331    */
1332   public void majorCompact(final byte [] tableNameOrRegionName)
1333   throws IOException, InterruptedException {
1334     compact(tableNameOrRegionName, null, true);
1335   }
1336 
1337   /**
1338    * Major compact a column family within a table or region.
1339    * Asynchronous operation.
1340    *
1341    * @param tableNameOrRegionName table or region to major compact
1342    * @param columnFamily column family within a table or region
1343    * @throws IOException if a remote or network exception occurs
1344    * @throws InterruptedException
1345    */
1346   public void majorCompact(final String tableNameOrRegionName,
1347     final String columnFamily) throws IOException, InterruptedException {
1348     majorCompact(Bytes.toBytes(tableNameOrRegionName),
1349       Bytes.toBytes(columnFamily));
1350   }
1351 
1352   /**
1353    * Major compact a column family within a table or region.
1354    * Asynchronous operation.
1355    *
1356    * @param tableNameOrRegionName table or region to major compact
1357    * @param columnFamily column family within a table or region
1358    * @throws IOException if a remote or network exception occurs
1359    * @throws InterruptedException
1360    */
1361   public void majorCompact(final byte [] tableNameOrRegionName,
1362     final byte[] columnFamily) throws IOException, InterruptedException {
1363     compact(tableNameOrRegionName, columnFamily, true);
1364   }
1365 
1366   /**
1367    * Compact a table or an individual region.
1368    * Asynchronous operation.
1369    *
1370    * @param tableNameOrRegionName table or region to compact
1371    * @param columnFamily column family within a table or region
1372    * @param major True if we are to do a major compaction.
1373    * @throws IOException if a remote or network exception occurs
1374    * @throws InterruptedException
1375    */
1376   private void compact(final byte [] tableNameOrRegionName,
1377     final byte[] columnFamily, final boolean major)
1378   throws IOException, InterruptedException {
1379     CatalogTracker ct = getCatalogTracker();
1380     try {
1381       Pair<HRegionInfo, ServerName> regionServerPair
1382         = getRegion(tableNameOrRegionName, ct);
1383       if (regionServerPair != null) {
1384         if (regionServerPair.getSecond() == null) {
1385           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1386         } else {
1387           compact(regionServerPair.getSecond(), regionServerPair.getFirst(), major, columnFamily);
1388         }
1389       } else {
1390         final String tableName = tableNameString(tableNameOrRegionName, ct);
1391         List<Pair<HRegionInfo, ServerName>> pairs =
1392           MetaReader.getTableRegionsAndLocations(ct,
1393               tableName);
1394         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1395           if (pair.getFirst().isOffline()) continue;
1396           if (pair.getSecond() == null) continue;
1397           try {
1398             compact(pair.getSecond(), pair.getFirst(), major, columnFamily);
1399           } catch (NotServingRegionException e) {
1400             if (LOG.isDebugEnabled()) {
1401               LOG.debug("Trying to" + (major ? " major" : "") + " compact " +
1402                 pair.getFirst() + ": " +
1403                 StringUtils.stringifyException(e));
1404             }
1405           }
1406         }
1407       }
1408     } finally {
1409       cleanupCatalogTracker(ct);
1410     }
1411   }
1412 
1413   private void compact(final ServerName sn, final HRegionInfo hri,
1414       final boolean major, final byte [] family)
1415   throws IOException {
1416     HRegionInterface rs =
1417       this.connection.getHRegionConnection(sn.getHostname(), sn.getPort());
1418     if (family != null) {
1419       try {
1420         rs.compactRegion(hri, major, family);
1421       } catch (IOException ioe) {
1422         String notFoundMsg = "java.lang.NoSuchMethodException: org.apache.hadoop.hbase.ipc.HRegionInterface."
1423           + "compactRegion(org.apache.hadoop.hbase.HRegionInfo, boolean, [B)";
1424         if (ioe.getMessage().contains(notFoundMsg)) {
1425           throw new IOException("per-column family compaction not supported on this version "
1426             + "of the HBase server.  You may still compact at the table or region level by "
1427           	+ "omitting the column family name.  Alternatively, you can upgrade the HBase server");
1428         }
1429         throw ioe;
1430       }
1431     } else {
1432       rs.compactRegion(hri, major);
1433     }
1434   }
1435 
1436   /**
1437    * Move the region <code>r</code> to <code>dest</code>.
1438    * @param encodedRegionName The encoded region name; i.e. the hash that makes
1439    * up the region name suffix: e.g. if regionname is
1440    * <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>,
1441    * then the encoded region name is: <code>527db22f95c8a9e0116f0cc13c680396</code>.
1442    * @param destServerName The servername of the destination regionserver.  If
1443    * passed the empty byte array we'll assign to a random server.  A server name
1444    * is made of host, port and startcode.  Here is an example:
1445    * <code> host187.example.com,60020,1289493121758</code>
1446    * @throws UnknownRegionException Thrown if we can't find a region named
1447    * <code>encodedRegionName</code>
1448    * @throws ZooKeeperConnectionException
1449    * @throws MasterNotRunningException
1450    */
1451   public void move(final byte [] encodedRegionName, final byte [] destServerName)
1452   throws UnknownRegionException, MasterNotRunningException, ZooKeeperConnectionException {
1453     getMaster().move(encodedRegionName, destServerName);
1454   }
1455 
1456   /**
1457    * @param regionName
1458    *          Region name to assign.
1459    * @throws MasterNotRunningException
1460    * @throws ZooKeeperConnectionException
1461    * @throws IOException
1462    */
1463   public void assign(final byte[] regionName) throws MasterNotRunningException,
1464       ZooKeeperConnectionException, IOException {
1465     getMaster().assign(regionName);
1466   }
1467 
1468   /**
1469    * Unassign a region from current hosting regionserver.  Region will then be
1470    * assigned to a regionserver chosen at random.  Region could be reassigned
1471    * back to the same server.  Use {@link #move(byte[], byte[])} if you want
1472    * to control the region movement.
1473    * @param regionName Region to unassign. Will clear any existing RegionPlan
1474    * if one found.
1475    * @param force If true, force unassign (Will remove region from
1476    * regions-in-transition too if present. If results in double assignment
1477    * use hbck -fix to resolve. To be used by experts).
1478    * @throws MasterNotRunningException
1479    * @throws ZooKeeperConnectionException
1480    * @throws IOException
1481    */
1482   public void unassign(final byte [] regionName, final boolean force)
1483   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
1484     getMaster().unassign(regionName, force);
1485   }
1486 
1487   /**
1488    * Turn the load balancer on or off.
1489    * @param b If true, enable balancer. If false, disable balancer.
1490    * @return Previous balancer value
1491    * @deprecated use setBalancerRunning(boolean, boolean) instead
1492    */
1493   @Deprecated
1494   public boolean balanceSwitch(final boolean b)
1495   throws MasterNotRunningException, ZooKeeperConnectionException {
1496     return getMaster().balanceSwitch(b);
1497   }
1498 
1499   /**
1500    * Turn the load balancer on or off.
1501    * @param on If true, enable balancer. If false, disable balancer.
1502    * @param synchronous If true, it waits until current balance() call, if outstanding, to return.
1503    * @return Previous balancer value
1504    */
1505   public boolean setBalancerRunning(final boolean on, final boolean synchronous)
1506   throws MasterNotRunningException, ZooKeeperConnectionException {
1507     if (synchronous && synchronousBalanceSwitchSupported) {
1508       try {
1509         return getMaster().synchronousBalanceSwitch(on);
1510       } catch (UndeclaredThrowableException ute) {
1511         String error = ute.getCause().getMessage();
1512         if (error != null && error.matches(
1513             "(?s).+NoSuchMethodException:.+synchronousBalanceSwitch.+")) {
1514           LOG.info("HMaster doesn't support synchronousBalanceSwitch");
1515           synchronousBalanceSwitchSupported = false;
1516         } else {
1517           throw ute;
1518         }
1519       }
1520     }
1521     return balanceSwitch(on);
1522   }
1523 
1524   /**
1525    * Invoke the balancer.  Will run the balancer and if regions to move, it will
1526    * go ahead and do the reassignments.  Can NOT run for various reasons.  Check
1527    * logs.
1528    * @return True if balancer ran, false otherwise.
1529    */
1530   public boolean balancer()
1531   throws MasterNotRunningException, ZooKeeperConnectionException {
1532     return getMaster().balance();
1533   }
1534 
1535   /**
1536    * Split a table or an individual region.
1537    * Asynchronous operation.
1538    *
1539    * @param tableNameOrRegionName table or region to split
1540    * @throws IOException if a remote or network exception occurs
1541    * @throws InterruptedException
1542    */
1543   public void split(final String tableNameOrRegionName)
1544   throws IOException, InterruptedException {
1545     split(Bytes.toBytes(tableNameOrRegionName));
1546   }
1547 
1548   /**
1549    * Split a table or an individual region.  Implicitly finds an optimal split
1550    * point.  Asynchronous operation.
1551    *
1552    * @param tableNameOrRegionName table to region to split
1553    * @throws IOException if a remote or network exception occurs
1554    * @throws InterruptedException
1555    */
1556   public void split(final byte [] tableNameOrRegionName)
1557   throws IOException, InterruptedException {
1558     split(tableNameOrRegionName, null);
1559   }
1560 
1561   public void split(final String tableNameOrRegionName,
1562     final String splitPoint) throws IOException, InterruptedException {
1563     split(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(splitPoint));
1564   }
1565 
1566   /**
1567    * Split a table or an individual region.
1568    * Asynchronous operation.
1569    *
1570    * @param tableNameOrRegionName table to region to split
1571    * @param splitPoint the explicit position to split on
1572    * @throws IOException if a remote or network exception occurs
1573    * @throws InterruptedException interrupt exception occurred
1574    */
1575   public void split(final byte [] tableNameOrRegionName,
1576       final byte [] splitPoint) throws IOException, InterruptedException {
1577     CatalogTracker ct = getCatalogTracker();
1578     try {
1579       Pair<HRegionInfo, ServerName> regionServerPair
1580         = getRegion(tableNameOrRegionName, ct);
1581       if (regionServerPair != null) {
1582         if (regionServerPair.getSecond() == null) {
1583             throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1584         } else {
1585           split(regionServerPair.getSecond(), regionServerPair.getFirst(), splitPoint);
1586         }
1587       } else {
1588         final String tableName = tableNameString(tableNameOrRegionName, ct);
1589         List<Pair<HRegionInfo, ServerName>> pairs =
1590           MetaReader.getTableRegionsAndLocations(ct,
1591               tableName);
1592         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1593           // May not be a server for a particular row
1594           if (pair.getSecond() == null) continue;
1595           HRegionInfo r = pair.getFirst();
1596           // check for parents
1597           if (r.isSplitParent()) continue;
1598           // if a split point given, only split that particular region
1599           if (splitPoint != null && !r.containsRow(splitPoint)) continue;
1600           // call out to region server to do split now
1601           split(pair.getSecond(), pair.getFirst(), splitPoint);
1602         }
1603       }
1604     } finally {
1605       cleanupCatalogTracker(ct);
1606     }
1607   }
1608 
1609   private void split(final ServerName sn, final HRegionInfo hri,
1610       byte[] splitPoint) throws IOException {
1611     HRegionInterface rs =
1612       this.connection.getHRegionConnection(sn.getHostname(), sn.getPort());
1613     rs.splitRegion(hri, splitPoint);
1614   }
1615 
1616   /**
1617    * Modify an existing table, more IRB friendly version.
1618    * Asynchronous operation.  This means that it may be a while before your
1619    * schema change is updated across all of the table.
1620    *
1621    * @param tableName name of table.
1622    * @param htd modified description of the table
1623    * @throws IOException if a remote or network exception occurs
1624    */
1625   public void modifyTable(final byte [] tableName, HTableDescriptor htd)
1626   throws IOException {
1627     try {
1628       getMaster().modifyTable(tableName, htd);
1629     } catch (RemoteException re) {
1630       // Convert RE exceptions in here; client shouldn't have to deal with them,
1631       // at least w/ the type of exceptions that come out of this method:
1632       // TableNotFoundException, etc.
1633       throw RemoteExceptionHandler.decodeRemoteException(re);
1634     }
1635   }
1636 
1637   /**
1638    * @param tableNameOrRegionName Name of a table or name of a region.
1639    * @param ct A {@link CatalogTracker} instance (caller of this method usually has one).
1640    * @return a pair of HRegionInfo and ServerName if <code>tableNameOrRegionName</code> is
1641    *  a verified region name (we call {@link  MetaReader#getRegion( CatalogTracker, byte[])}
1642    *  else null.
1643    * Throw an exception if <code>tableNameOrRegionName</code> is null.
1644    * @throws IOException
1645    */
1646   Pair<HRegionInfo, ServerName> getRegion(final byte[] tableNameOrRegionName,
1647       final CatalogTracker ct) throws IOException {
1648     if (tableNameOrRegionName == null) {
1649       throw new IllegalArgumentException("Pass a table name or region name");
1650     }
1651     Pair<HRegionInfo, ServerName> pair = MetaReader.getRegion(ct, tableNameOrRegionName);
1652     if (pair == null) {
1653       final AtomicReference<Pair<HRegionInfo, ServerName>> result =
1654         new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
1655       final String encodedName = Bytes.toString(tableNameOrRegionName);
1656       MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
1657         @Override
1658         public boolean processRow(Result data) throws IOException {
1659           if (data == null || data.size() <= 0) {
1660             return true;
1661           }
1662           HRegionInfo info = MetaReader.parseHRegionInfoFromCatalogResult(
1663             data, HConstants.REGIONINFO_QUALIFIER);
1664           if (info == null) {
1665             LOG.warn("No serialized HRegionInfo in " + data);
1666             return true;
1667           }
1668           if (!encodedName.equals(info.getEncodedName())) return true;
1669           ServerName sn = MetaReader.getServerNameFromCatalogResult(data);
1670           result.set(new Pair<HRegionInfo, ServerName>(info, sn));
1671           return false; // found the region, stop
1672         }
1673       };
1674 
1675       MetaScanner.metaScan(conf, visitor);
1676       pair = result.get();
1677     }
1678     return pair;
1679   }
1680 
1681   /**
1682    * Convert the table name byte array into a table name string and check if table
1683    * exists or not.
1684    * @param tableNameBytes Name of a table.
1685    * @param ct A {@link #CatalogTracker} instance (caller of this method usually has one).
1686    * @return tableName in string form.
1687    * @throws IOException if a remote or network exception occurs.
1688    * @throws TableNotFoundException if table does not exist.
1689    */
1690   private String tableNameString(final byte[] tableNameBytes, CatalogTracker ct)
1691       throws IOException {
1692     String tableNameString = Bytes.toString(tableNameBytes);
1693     if (!MetaReader.tableExists(ct, tableNameString)) {
1694       throw new TableNotFoundException(tableNameString);
1695     }
1696     return tableNameString;
1697   }
1698 
1699   /**
1700    * Shuts down the HBase cluster
1701    * @throws IOException if a remote or network exception occurs
1702    */
1703   public synchronized void shutdown() throws IOException {
1704     isMasterRunning();
1705     try {
1706       getMaster().shutdown();
1707     } catch (RemoteException e) {
1708       throw RemoteExceptionHandler.decodeRemoteException(e);
1709     }
1710   }
1711 
1712   /**
1713    * Shuts down the current HBase master only.
1714    * Does not shutdown the cluster.
1715    * @see #shutdown()
1716    * @throws IOException if a remote or network exception occurs
1717    */
1718   public synchronized void stopMaster() throws IOException {
1719     isMasterRunning();
1720     try {
1721       getMaster().stopMaster();
1722     } catch (RemoteException e) {
1723       throw RemoteExceptionHandler.decodeRemoteException(e);
1724     }
1725   }
1726 
1727   /**
1728    * Stop the designated regionserver
1729    * @param hostnamePort Hostname and port delimited by a <code>:</code> as in
1730    * <code>example.org:1234</code>
1731    * @throws IOException if a remote or network exception occurs
1732    */
1733   public synchronized void stopRegionServer(final String hostnamePort)
1734   throws IOException {
1735     String hostname = Addressing.parseHostname(hostnamePort);
1736     int port = Addressing.parsePort(hostnamePort);
1737     HRegionInterface rs =
1738       this.connection.getHRegionConnection(hostname, port);
1739     rs.stop("Called by admin client " + this.connection.toString());
1740   }
1741 
1742   /**
1743    * @return cluster status
1744    * @throws IOException if a remote or network exception occurs
1745    */
1746   public ClusterStatus getClusterStatus() throws IOException {
1747     return getMaster().getClusterStatus();
1748   }
1749 
1750   private HRegionLocation getFirstMetaServerForTable(final byte [] tableName)
1751   throws IOException {
1752     return connection.locateRegion(HConstants.META_TABLE_NAME,
1753       HRegionInfo.createRegionName(tableName, null, HConstants.NINES, false));
1754   }
1755 
1756   /**
1757    * @return Configuration used by the instance.
1758    */
1759   public Configuration getConfiguration() {
1760     return this.conf;
1761   }
1762 
1763   /**
1764    * Check to see if HBase is running. Throw an exception if not.
1765    *
1766    * @param conf system configuration
1767    * @throws MasterNotRunningException if the master is not running
1768    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
1769    */
1770   public static void checkHBaseAvailable(Configuration conf)
1771   throws MasterNotRunningException, ZooKeeperConnectionException {
1772     Configuration copyOfConf = HBaseConfiguration.create(conf);
1773     copyOfConf.setInt("hbase.client.retries.number", 1);
1774     HBaseAdmin admin = new HBaseAdmin(copyOfConf);
1775     try {
1776       admin.close();
1777     } catch (IOException ioe) {
1778       admin.LOG.info("Failed to close connection", ioe);
1779     }
1780   }
1781 
1782   /**
1783    * get the regions of a given table.
1784    *
1785    * @param tableName the name of the table
1786    * @return Ordered list of {@link HRegionInfo}.
1787    * @throws IOException
1788    */
1789   public List<HRegionInfo> getTableRegions(final byte[] tableName)
1790   throws IOException {
1791     CatalogTracker ct = getCatalogTracker();
1792     List<HRegionInfo> Regions = null;
1793     try {
1794       Regions = MetaReader.getTableRegions(ct, tableName, true);
1795     } finally {
1796       cleanupCatalogTracker(ct);
1797     }
1798     return Regions;
1799   }
1800 
1801   public void close() throws IOException {
1802     if (this.connection != null) {
1803       this.connection.close();
1804     }
1805   }
1806 
1807  /**
1808  * Get tableDescriptors
1809  * @param tableNames List of table names
1810  * @return HTD[] the tableDescriptor
1811  * @throws IOException if a remote or network exception occurs
1812  */
1813   public HTableDescriptor[] getTableDescriptors(List<String> tableNames)
1814   throws IOException {
1815     return this.connection.getHTableDescriptors(tableNames);
1816   }
1817 
1818   /**
1819    * Roll the log writer. That is, start writing log messages to a new file.
1820    *
1821    * @param serverName
1822    *          The servername of the regionserver. A server name is made of host,
1823    *          port and startcode. This is mandatory. Here is an example:
1824    *          <code> host187.example.com,60020,1289493121758</code>
1825    * @return If lots of logs, flush the returned regions so next time through
1826    * we can clean logs. Returns null if nothing to flush.  Names are actual
1827    * region names as returned by {@link HRegionInfo#getEncodedName()}
1828    * @throws IOException if a remote or network exception occurs
1829    * @throws FailedLogCloseException
1830    */
1831  public synchronized  byte[][] rollHLogWriter(String serverName)
1832       throws IOException, FailedLogCloseException {
1833     ServerName sn = new ServerName(serverName);
1834     HRegionInterface rs = this.connection.getHRegionConnection(
1835         sn.getHostname(), sn.getPort());
1836     return rs.rollHLogWriter();
1837   }
1838 
1839   public String[] getMasterCoprocessors() {
1840     try {
1841       return getClusterStatus().getMasterCoprocessors();
1842     } catch (IOException e) {
1843       LOG.error("Could not getClusterStatus()",e);
1844       return null;
1845     }
1846   }
1847 
1848   /**
1849    * Get the current compaction state of a table or region.
1850    * It could be in a major compaction, a minor compaction, both, or none.
1851    *
1852    * @param tableNameOrRegionName table or region to major compact
1853    * @throws IOException if a remote or network exception occurs
1854    * @throws InterruptedException
1855    * @return the current compaction state
1856    */
1857   public CompactionState getCompactionState(final String tableNameOrRegionName)
1858       throws IOException, InterruptedException {
1859     return getCompactionState(Bytes.toBytes(tableNameOrRegionName));
1860   }
1861 
1862   /**
1863    * Get the current compaction state of a table or region.
1864    * It could be in a major compaction, a minor compaction, both, or none.
1865    *
1866    * @param tableNameOrRegionName table or region to major compact
1867    * @throws IOException if a remote or network exception occurs
1868    * @throws InterruptedException
1869    * @return the current compaction state
1870    */
1871   public CompactionState getCompactionState(final byte [] tableNameOrRegionName)
1872       throws IOException, InterruptedException {
1873     CompactionState state = CompactionState.NONE;
1874     CatalogTracker ct = getCatalogTracker();
1875     try {
1876       Pair<HRegionInfo, ServerName> regionServerPair
1877         = getRegion(tableNameOrRegionName, ct);
1878       if (regionServerPair != null) {
1879         if (regionServerPair.getSecond() == null) {
1880           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1881         } else {
1882           ServerName sn = regionServerPair.getSecond();
1883           HRegionInterface rs =
1884             this.connection.getHRegionConnection(sn.getHostname(), sn.getPort());
1885           return CompactionState.valueOf(
1886             rs.getCompactionState(regionServerPair.getFirst().getRegionName()));
1887         }
1888       } else {
1889         final String tableName = tableNameString(tableNameOrRegionName, ct);
1890         List<Pair<HRegionInfo, ServerName>> pairs =
1891           MetaReader.getTableRegionsAndLocations(ct, tableName);
1892         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1893           if (pair.getFirst().isOffline()) continue;
1894           if (pair.getSecond() == null) continue;
1895           try {
1896             ServerName sn = pair.getSecond();
1897             HRegionInterface rs =
1898               this.connection.getHRegionConnection(sn.getHostname(), sn.getPort());
1899             switch (CompactionState.valueOf(
1900               rs.getCompactionState(pair.getFirst().getRegionName()))) {
1901             case MAJOR_AND_MINOR:
1902               return CompactionState.MAJOR_AND_MINOR;
1903             case MAJOR:
1904               if (state == CompactionState.MINOR) {
1905                 return CompactionState.MAJOR_AND_MINOR;
1906               }
1907               state = CompactionState.MAJOR;
1908               break;
1909             case MINOR:
1910               if (state == CompactionState.MAJOR) {
1911                 return CompactionState.MAJOR_AND_MINOR;
1912               }
1913               state = CompactionState.MINOR;
1914               break;
1915             case NONE:
1916               default: // nothing, continue
1917             }
1918           } catch (NotServingRegionException e) {
1919             if (LOG.isDebugEnabled()) {
1920               LOG.debug("Trying to get compaction state of " +
1921                 pair.getFirst() + ": " +
1922                 StringUtils.stringifyException(e));
1923             }
1924           }
1925         }
1926       }
1927     } finally {
1928       cleanupCatalogTracker(ct);
1929     }
1930     return state;
1931   }
1932 
1933   /**
1934    * Creates and returns a proxy to the CoprocessorProtocol instance running in the
1935    * master.
1936    *
1937    * @param protocol The class or interface defining the remote protocol
1938    * @return A CoprocessorProtocol instance
1939    */
1940   public <T extends CoprocessorProtocol> T coprocessorProxy(
1941       Class<T> protocol) {
1942     return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),
1943         new Class[]{protocol},
1944         new MasterExecRPCInvoker(conf,
1945             connection,
1946             protocol));
1947   }
1948 
1949 
1950   /**
1951    * Create a timestamp consistent snapshot for the given table.
1952    * <p>
1953    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
1954    * snapshot with the same name (even a different type or with different parameters) will fail with
1955    * a {@link SnapshotCreationException} indicating the duplicate naming.
1956    * <p>
1957    * Snapshot names follow the same naming constraints as tables in HBase. See
1958    * {@link HTableDescriptor#isLegalTableName(byte[])}.
1959    * @param snapshotName name of the snapshot to be created
1960    * @param tableName name of the table for which snapshot is created
1961    * @throws IOException if a remote or network exception occurs
1962    * @throws SnapshotCreationException if snapshot creation failed
1963    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
1964    */
1965   public void snapshot(final String snapshotName, final String tableName) throws IOException,
1966       SnapshotCreationException, IllegalArgumentException {
1967     snapshot(snapshotName, tableName, SnapshotDescription.Type.FLUSH);
1968   }
1969 
1970   /**
1971    * Take a snapshot for the given table. If the table is enabled, a FLUSH-type snapshot will be
1972    * taken. If the table is disabled, an offline snapshot is taken.
1973    * <p>
1974    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
1975    * snapshot with the same name (even a different type or with different parameters) will fail with
1976    * a {@link SnapshotCreationException} indicating the duplicate naming.
1977    * <p>
1978    * Snapshot names follow the same naming constraints as tables in HBase. See
1979    * {@link HTableDescriptor#isLegalTableName(byte[])}.
1980    * @param snapshotName name of the snapshot to be created
1981    * @param tableName name of the table for which snapshot is created
1982    * @throws IOException if a remote or network exception occurs
1983    * @throws SnapshotCreationException if snapshot creation failed
1984    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
1985    */
1986   public void snapshot(final byte[] snapshotName, final byte[] tableName) throws IOException,
1987       SnapshotCreationException, IllegalArgumentException {
1988     snapshot(Bytes.toString(snapshotName), Bytes.toString(tableName));
1989   }
1990 
1991   /**
1992    * Create typed snapshot of the table.
1993    * <p>
1994    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
1995    * snapshot with the same name (even a different type or with different parameters) will fail with
1996    * a {@link SnapshotCreationException} indicating the duplicate naming.
1997    * <p>
1998    * Snapshot names follow the same naming constraints as tables in HBase. See
1999    * {@link HTableDescriptor#isLegalTableName(byte[])}.
2000    * <p>
2001    * @param snapshotName name to give the snapshot on the filesystem. Must be unique from all other
2002    * snapshots stored on the cluster
2003    * @param tableName name of the table to snapshot
2004    * @param type type of snapshot to take
2005    * @throws IOException we fail to reach the master
2006    * @throws SnapshotCreationException if snapshot creation failed
2007    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2008    */
2009   public void snapshot(final String snapshotName, final String tableName,
2010       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
2011       IllegalArgumentException {
2012     SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
2013     builder.setTable(tableName);
2014     builder.setName(snapshotName);
2015     builder.setType(type);
2016     snapshot(builder.build());
2017   }
2018 
2019   /**
2020    * Take a snapshot and wait for the server to complete that snapshot (blocking).
2021    * <p>
2022    * Only a single snapshot should be taken at a time for an instance of HBase, or results may be
2023    * undefined (you can tell multiple HBase clusters to snapshot at the same time, but only one at a
2024    * time for a single cluster).
2025    * <p>
2026    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
2027    * snapshot with the same name (even a different type or with different parameters) will fail with
2028    * a {@link SnapshotCreationException} indicating the duplicate naming.
2029    * <p>
2030    * Snapshot names follow the same naming constraints as tables in HBase. See
2031    * {@link HTableDescriptor#isLegalTableName(byte[])}.
2032    * <p>
2033    * You should probably use {@link #snapshot(String, String)} or {@link #snapshot(byte[], byte[])}
2034    * unless you are sure about the type of snapshot that you want to take.
2035    * @param snapshot snapshot to take
2036    * @throws IOException or we lose contact with the master.
2037    * @throws SnapshotCreationException if snapshot failed to be taken
2038    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2039    */
2040   public void snapshot(SnapshotDescription snapshot) throws IOException, SnapshotCreationException,
2041       IllegalArgumentException {
2042     HSnapshotDescription snapshotWritable = new HSnapshotDescription(snapshot);
2043 
2044     try {
2045       // actually take the snapshot
2046       long max = takeSnapshotAsync(snapshot);
2047       long start = EnvironmentEdgeManager.currentTimeMillis();
2048       long maxPauseTime = max / this.numRetries;
2049       boolean done = false;
2050       int tries = 0;
2051       LOG.debug("Waiting a max of " + max + " ms for snapshot '" +
2052           SnapshotDescriptionUtils.toString(snapshot) + "' to complete. (max " +
2053           maxPauseTime + " ms per retry)");
2054       while (tries == 0 || (EnvironmentEdgeManager.currentTimeMillis() - start) < max && !done) {
2055         try {
2056           // sleep a backoff <= pauseTime amount
2057           long sleep = getPauseTime(tries++);
2058           sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
2059           LOG.debug("(#" + tries + ") Sleeping: " + sleep +
2060             "ms while waiting for snapshot completion.");
2061           Thread.sleep(sleep);
2062 
2063         } catch (InterruptedException e) {
2064           LOG.debug("Interrupted while waiting for snapshot " + snapshot + " to complete");
2065           Thread.currentThread().interrupt();
2066         }
2067         LOG.debug("Getting current status of snapshot from master...");
2068         done = getMaster().isSnapshotDone(snapshotWritable);
2069       }
2070 
2071       if (!done) {
2072         throw new SnapshotCreationException("Snapshot '" + snapshot.getName()
2073             + "' wasn't completed in expectedTime:" + max + " ms", snapshot);
2074       }
2075     } catch (RemoteException e) {
2076       throw RemoteExceptionHandler.decodeRemoteException(e);
2077     }
2078   }
2079 
2080   /**
2081    * Take a snapshot without waiting for the server to complete that snapshot (asynchronous)
2082    * <p>
2083    * Only a single snapshot should be taken at a time, or results may be undefined.
2084    * @param snapshot snapshot to take
2085    * @return the max time in millis to wait for the snapshot
2086    * @throws IOException if the snapshot did not succeed or we lose contact with the master.
2087    * @throws SnapshotCreationException if snapshot creation failed
2088    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2089    */
2090   public long takeSnapshotAsync(SnapshotDescription snapshot) throws IOException,
2091       SnapshotCreationException {
2092     SnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
2093     HSnapshotDescription snapshotWritable = new HSnapshotDescription(snapshot);
2094     return getMaster().snapshot(snapshotWritable);
2095   }
2096 
2097   /**
2098    * Check the current state of the passed snapshot.
2099    * <p>
2100    * There are three possible states:
2101    * <ol>
2102    * <li>running - returns <tt>false</tt></li>
2103    * <li>finished - returns <tt>true</tt></li>
2104    * <li>finished with error - throws the exception that caused the snapshot to fail</li>
2105    * </ol>
2106    * <p>
2107    * The cluster only knows about the most recent snapshot. Therefore, if another snapshot has been
2108    * run/started since the snapshot your are checking, you will recieve an
2109    * {@link UnknownSnapshotException}.
2110    * @param snapshot description of the snapshot to check
2111    * @return <tt>true</tt> if the snapshot is completed, <tt>false</tt> if the snapshot is still
2112    * running
2113    * @throws IOException if we have a network issue
2114    * @throws HBaseSnapshotException if the snapshot failed
2115    * @throws UnknownSnapshotException if the requested snapshot is unknown
2116    */
2117   public boolean isSnapshotFinished(final SnapshotDescription snapshot)
2118       throws IOException, HBaseSnapshotException, UnknownSnapshotException {
2119     try {
2120       return getMaster().isSnapshotDone(new HSnapshotDescription(snapshot));
2121     } catch (RemoteException e) {
2122       throw RemoteExceptionHandler.decodeRemoteException(e);
2123     }
2124   }
2125 
2126   /**
2127    * Restore the specified snapshot on the original table. (The table must be disabled)
2128    * Before restoring the table, a new snapshot with the current table state is created.
2129    * In case of failure, the table will be rolled back to the its original state.
2130    *
2131    * @param snapshotName name of the snapshot to restore
2132    * @throws IOException if a remote or network exception occurs
2133    * @throws RestoreSnapshotException if snapshot failed to be restored
2134    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2135    */
2136   public void restoreSnapshot(final byte[] snapshotName)
2137       throws IOException, RestoreSnapshotException {
2138     restoreSnapshot(Bytes.toString(snapshotName));
2139   }
2140 
2141   /**
2142    * Restore the specified snapshot on the original table. (The table must be disabled)
2143    * Before restoring the table, a new snapshot with the current table state is created.
2144    * In case of failure, the table will be rolled back to its original state.
2145    *
2146    * @param snapshotName name of the snapshot to restore
2147    * @throws IOException if a remote or network exception occurs
2148    * @throws RestoreSnapshotException if snapshot failed to be restored
2149    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2150    */
2151   public void restoreSnapshot(final String snapshotName)
2152       throws IOException, RestoreSnapshotException {
2153     String rollbackSnapshot = snapshotName + "-" + EnvironmentEdgeManager.currentTimeMillis();
2154 
2155     String tableName = null;
2156     for (SnapshotDescription snapshotInfo: listSnapshots()) {
2157       if (snapshotInfo.getName().equals(snapshotName)) {
2158         tableName = snapshotInfo.getTable();
2159         break;
2160       }
2161     }
2162 
2163     if (tableName == null) {
2164       throw new RestoreSnapshotException(
2165         "Unable to find the table name for snapshot=" + snapshotName);
2166     }
2167 
2168     // Take a snapshot of the current state
2169     snapshot(rollbackSnapshot, tableName);
2170 
2171     // Restore snapshot
2172     try {
2173       internalRestoreSnapshot(snapshotName, tableName);
2174     } catch (IOException e) {
2175       // Try to rollback
2176       try {
2177         String msg = "Restore snapshot=" + snapshotName +
2178           " failed. Rollback to snapshot=" + rollbackSnapshot + " succeeded.";
2179         LOG.error(msg, e);
2180         internalRestoreSnapshot(rollbackSnapshot, tableName);
2181         throw new RestoreSnapshotException(msg, e);
2182       } catch (IOException ex) {
2183         String msg = "Failed to restore and rollback to snapshot=" + rollbackSnapshot;
2184         LOG.error(msg, ex);
2185         throw new RestoreSnapshotException(msg, ex);
2186       }
2187     }
2188   }
2189 
2190   /**
2191    * Create a new table by cloning the snapshot content.
2192    *
2193    * @param snapshotName name of the snapshot to be cloned
2194    * @param tableName name of the table where the snapshot will be restored
2195    * @throws IOException if a remote or network exception occurs
2196    * @throws TableExistsException if table to be created already exists
2197    * @throws RestoreSnapshotException if snapshot failed to be cloned
2198    * @throws IllegalArgumentException if the specified table has not a valid name
2199    */
2200   public void cloneSnapshot(final byte[] snapshotName, final byte[] tableName)
2201       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
2202     cloneSnapshot(Bytes.toString(snapshotName), Bytes.toString(tableName));
2203   }
2204 
2205   /**
2206    * Create a new table by cloning the snapshot content.
2207    *
2208    * @param snapshotName name of the snapshot to be cloned
2209    * @param tableName name of the table where the snapshot will be restored
2210    * @throws IOException if a remote or network exception occurs
2211    * @throws TableExistsException if table to be created already exists
2212    * @throws RestoreSnapshotException if snapshot failed to be cloned
2213    * @throws IllegalArgumentException if the specified table has not a valid name
2214    */
2215   public void cloneSnapshot(final String snapshotName, final String tableName)
2216       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
2217     if (tableExists(tableName)) {
2218       throw new TableExistsException("Table '" + tableName + " already exists");
2219     }
2220     internalRestoreSnapshot(snapshotName, tableName);
2221     waitUntilTableIsEnabled(Bytes.toBytes(tableName));
2222   }
2223 
2224   /**
2225    * Execute Restore/Clone snapshot and wait for the server to complete (blocking).
2226    * To check if the cloned table exists, use {@link #isTableAvailable} -- it is not safe to
2227    * create an HTable instance to this table before it is available.
2228    * @param snapshot snapshot to restore
2229    * @param tableName table name to restore the snapshot on
2230    * @throws IOException if a remote or network exception occurs
2231    * @throws RestoreSnapshotException if snapshot failed to be restored
2232    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2233    */
2234   private void internalRestoreSnapshot(final String snapshotName, final String tableName)
2235       throws IOException, RestoreSnapshotException {
2236     HSnapshotDescription snapshot = new HSnapshotDescription(
2237       SnapshotDescription.newBuilder().setName(snapshotName).setTable(tableName).build());
2238 
2239     try {
2240       // actually restore the snapshot
2241       getMaster().restoreSnapshot(snapshot);
2242 
2243       final long maxPauseTime = 5000;
2244       boolean done = false;
2245       int tries = 0;
2246       while (!done) {
2247         try {
2248           // sleep a backoff <= pauseTime amount
2249           long sleep = getPauseTime(tries++);
2250           sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
2251           LOG.debug(tries + ") Sleeping: " + sleep + " ms while we wait for snapshot restore to complete.");
2252           Thread.sleep(sleep);
2253         } catch (InterruptedException e) {
2254           LOG.debug("Interrupted while waiting for snapshot " + snapshot + " restore to complete");
2255           Thread.currentThread().interrupt();
2256         }
2257         LOG.debug("Getting current status of snapshot restore from master...");
2258         done = getMaster().isRestoreSnapshotDone(snapshot);
2259       }
2260       if (!done) {
2261         throw new RestoreSnapshotException("Snapshot '" + snapshot.getName() + "' wasn't restored.");
2262       }
2263     } catch (RemoteException e) {
2264       throw RemoteExceptionHandler.decodeRemoteException(e);
2265     }
2266   }
2267 
2268   /**
2269    * List completed snapshots.
2270    * @return a list of snapshot descriptors for completed snapshots
2271    * @throws IOException if a network error occurs
2272    */
2273   public List<SnapshotDescription> listSnapshots() throws IOException {
2274     List<SnapshotDescription> snapshots = new LinkedList<SnapshotDescription>();
2275     try {
2276       for (HSnapshotDescription snapshot: getMaster().getCompletedSnapshots()) {
2277         snapshots.add(snapshot.getProto());
2278       }
2279     } catch (RemoteException e) {
2280       throw RemoteExceptionHandler.decodeRemoteException(e);
2281     }
2282     return snapshots;
2283   }
2284 
2285   /**
2286    * Delete an existing snapshot.
2287    * @param snapshotName name of the snapshot
2288    * @throws IOException if a remote or network exception occurs
2289    */
2290   public void deleteSnapshot(final byte[] snapshotName) throws IOException {
2291     // make sure the snapshot is possibly valid
2292     HTableDescriptor.isLegalTableName(snapshotName);
2293     // do the delete
2294     SnapshotDescription snapshot = SnapshotDescription.newBuilder()
2295       .setName(Bytes.toString(snapshotName)).build();
2296     try {
2297       getMaster().deleteSnapshot(new HSnapshotDescription(snapshot));
2298     } catch (RemoteException e) {
2299       throw RemoteExceptionHandler.decodeRemoteException(e);
2300     }
2301   }
2302 
2303   /**
2304    * Delete an existing snapshot.
2305    * @param snapshotName name of the snapshot
2306    * @throws IOException if a remote or network exception occurs
2307    */
2308   public void deleteSnapshot(final String snapshotName) throws IOException {
2309     deleteSnapshot(Bytes.toBytes(snapshotName));
2310   }
2311 }