View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import java.io.Closeable;
22  import java.io.IOException;
23  import java.io.InterruptedIOException;
24  import java.net.SocketTimeoutException;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.concurrent.atomic.AtomicInteger;
30  import java.util.concurrent.atomic.AtomicReference;
31  import java.util.regex.Pattern;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.classification.InterfaceAudience;
36  import org.apache.hadoop.classification.InterfaceStability;
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.HBaseIOException;
42  import org.apache.hadoop.hbase.HColumnDescriptor;
43  import org.apache.hadoop.hbase.HConstants;
44  import org.apache.hadoop.hbase.HRegionInfo;
45  import org.apache.hadoop.hbase.HRegionLocation;
46  import org.apache.hadoop.hbase.HTableDescriptor;
47  import org.apache.hadoop.hbase.MasterNotRunningException;
48  import org.apache.hadoop.hbase.NamespaceDescriptor;
49  import org.apache.hadoop.hbase.NotServingRegionException;
50  import org.apache.hadoop.hbase.RegionException;
51  import org.apache.hadoop.hbase.ServerName;
52  import org.apache.hadoop.hbase.TableExistsException;
53  import org.apache.hadoop.hbase.TableName;
54  import org.apache.hadoop.hbase.TableNotDisabledException;
55  import org.apache.hadoop.hbase.TableNotEnabledException;
56  import org.apache.hadoop.hbase.TableNotFoundException;
57  import org.apache.hadoop.hbase.UnknownRegionException;
58  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
59  import org.apache.hadoop.hbase.catalog.CatalogTracker;
60  import org.apache.hadoop.hbase.catalog.MetaReader;
61  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
62  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
63  import org.apache.hadoop.hbase.exceptions.DeserializationException;
64  import org.apache.hadoop.hbase.exceptions.MergeRegionException;
65  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
66  import org.apache.hadoop.hbase.ipc.MasterCoprocessorRpcChannel;
67  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
68  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
69  import org.apache.hadoop.hbase.protobuf.RequestConverter;
70  import org.apache.hadoop.hbase.protobuf.ResponseConverter;
71  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
72  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionRequest;
73  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionResponse;
74  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CompactRegionRequest;
75  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.FlushRegionRequest;
76  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoRequest;
77  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse;
78  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
79  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterRequest;
80  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterResponse;
81  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.StopServerRequest;
82  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ClientService;
83  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
84  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
85  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
86  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
87  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableSchema;
88  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AddColumnRequest;
89  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AssignRegionRequest;
90  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateNamespaceRequest;
91  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableRequest;
92  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteColumnRequest;
93  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteNamespaceRequest;
94  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteSnapshotRequest;
95  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableRequest;
96  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableRequest;
97  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DispatchMergingRegionsRequest;
98  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableRequest;
99  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterStatusRequest;
100 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest;
101 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetNamespaceDescriptorRequest;
102 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusRequest;
103 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusResponse;
104 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest;
105 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
106 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneRequest;
107 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneResponse;
108 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
109 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
110 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListNamespaceDescriptorsRequest;
111 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableDescriptorsByNamespaceRequest;
112 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableNamesByNamespaceRequest;
113 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyColumnRequest;
114 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyNamespaceRequest;
115 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyTableRequest;
116 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MoveRegionRequest;
117 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotRequest;
118 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotResponse;
119 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
120 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownRequest;
121 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotRequest;
122 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotResponse;
123 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterRequest;
124 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionRequest;
125 import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
126 import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
127 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
128 import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
129 import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
130 import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
131 import org.apache.hadoop.hbase.util.Addressing;
132 import org.apache.hadoop.hbase.util.Bytes;
133 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
134 import org.apache.hadoop.hbase.util.Pair;
135 import org.apache.hadoop.ipc.RemoteException;
136 import org.apache.hadoop.util.StringUtils;
137 import org.apache.zookeeper.KeeperException;
138 
139 import com.google.protobuf.ByteString;
140 import com.google.protobuf.ServiceException;
141 
142 /**
143  * Provides an interface to manage HBase database table metadata + general
144  * administrative functions.  Use HBaseAdmin to create, drop, list, enable and
145  * disable tables. Use it also to add and drop table column families.
146  *
147  * <p>See {@link HTable} to add, update, and delete data from an individual table.
148  * <p>Currently HBaseAdmin instances are not expected to be long-lived.  For
149  * example, an HBaseAdmin instance will not ride over a Master restart.
150  */
151 @InterfaceAudience.Public
152 @InterfaceStability.Evolving
153 public class HBaseAdmin implements Abortable, Closeable {
154   private static final Log LOG = LogFactory.getLog(HBaseAdmin.class);
155 
156   // We use the implementation class rather then the interface because we
157   //  need the package protected functions to get the connection to master
158   private HConnection connection;
159 
160   private volatile Configuration conf;
161   private final long pause;
162   private final int numRetries;
163   // Some operations can take a long time such as disable of big table.
164   // numRetries is for 'normal' stuff... Multiply by this factor when
165   // want to wait a long time.
166   private final int retryLongerMultiplier;
167   private boolean aborted;
168   private boolean cleanupConnectionOnClose = false; // close the connection in close()
169   private boolean closed = false;
170 
171   private RpcRetryingCallerFactory rpcCallerFactory;
172 
173   /**
174    * Constructor.
175    * See {@link #HBaseAdmin(HConnection connection)}
176    *
177    * @param c Configuration object. Copied internally.
178    */
179   public HBaseAdmin(Configuration c)
180   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
181     // Will not leak connections, as the new implementation of the constructor
182     // does not throw exceptions anymore.
183     this(HConnectionManager.getConnection(new Configuration(c)));
184     this.cleanupConnectionOnClose = true;
185   }
186 
187  /**
188   * Constructor for externally managed HConnections.
189   * The connection to master will be created when required by admin functions.
190   *
191   * @param connection The HConnection instance to use
192   * @throws MasterNotRunningException, ZooKeeperConnectionException are not
193   *  thrown anymore but kept into the interface for backward api compatibility
194   */
195   public HBaseAdmin(HConnection connection)
196       throws MasterNotRunningException, ZooKeeperConnectionException {
197     this.conf = connection.getConfiguration();
198     this.connection = connection;
199 
200     this.pause = this.conf.getLong(HConstants.HBASE_CLIENT_PAUSE,
201         HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
202     this.numRetries = this.conf.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
203         HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
204     this.retryLongerMultiplier = this.conf.getInt(
205         "hbase.client.retries.longer.multiplier", 10);
206     this.rpcCallerFactory = RpcRetryingCallerFactory.instantiate(this.conf);
207   }
208 
209   /**
210    * @return A new CatalogTracker instance; call {@link #cleanupCatalogTracker(CatalogTracker)}
211    * to cleanup the returned catalog tracker.
212    * @throws org.apache.hadoop.hbase.ZooKeeperConnectionException
213    * @throws IOException
214    * @see #cleanupCatalogTracker(CatalogTracker)
215    */
216   private synchronized CatalogTracker getCatalogTracker()
217   throws ZooKeeperConnectionException, IOException {
218     CatalogTracker ct = null;
219     try {
220       ct = new CatalogTracker(this.conf);
221       ct.start();
222     } catch (InterruptedException e) {
223       // Let it out as an IOE for now until we redo all so tolerate IEs
224       Thread.currentThread().interrupt();
225       throw new IOException("Interrupted", e);
226     }
227     return ct;
228   }
229 
230   private void cleanupCatalogTracker(final CatalogTracker ct) {
231     ct.stop();
232   }
233 
234   @Override
235   public void abort(String why, Throwable e) {
236     // Currently does nothing but throw the passed message and exception
237     this.aborted = true;
238     throw new RuntimeException(why, e);
239   }
240 
241   @Override
242   public boolean isAborted(){
243     return this.aborted;
244   }
245 
246   /** @return HConnection used by this object. */
247   public HConnection getConnection() {
248     return connection;
249   }
250 
251   /** @return - true if the master server is running. Throws an exception
252    *  otherwise.
253    * @throws ZooKeeperConnectionException
254    * @throws MasterNotRunningException
255    */
256   public boolean isMasterRunning()
257   throws MasterNotRunningException, ZooKeeperConnectionException {
258     return connection.isMasterRunning();
259   }
260 
261   /**
262    * @param tableName Table to check.
263    * @return True if table exists already.
264    * @throws IOException
265    */
266   public boolean tableExists(final TableName tableName)
267   throws IOException {
268     boolean b = false;
269     CatalogTracker ct = getCatalogTracker();
270     try {
271       b = MetaReader.tableExists(ct, tableName);
272     } finally {
273       cleanupCatalogTracker(ct);
274     }
275     return b;
276   }
277 
278   public boolean tableExists(final byte[] tableName)
279   throws IOException {
280     return tableExists(TableName.valueOf(tableName));
281   }
282 
283   public boolean tableExists(final String tableName)
284   throws IOException {
285     return tableExists(TableName.valueOf(tableName));
286   }
287 
288   /**
289    * List all the userspace tables.  In other words, scan the hbase:meta table.
290    *
291    * If we wanted this to be really fast, we could implement a special
292    * catalog table that just contains table names and their descriptors.
293    * Right now, it only exists as part of the hbase:meta table's region info.
294    *
295    * @return - returns an array of HTableDescriptors
296    * @throws IOException if a remote or network exception occurs
297    */
298   public HTableDescriptor[] listTables() throws IOException {
299     return this.connection.listTables();
300   }
301 
302   /**
303    * List all the userspace tables matching the given pattern.
304    *
305    * @param pattern The compiled regular expression to match against
306    * @return - returns an array of HTableDescriptors
307    * @throws IOException if a remote or network exception occurs
308    * @see #listTables()
309    */
310   public HTableDescriptor[] listTables(Pattern pattern) throws IOException {
311     List<HTableDescriptor> matched = new LinkedList<HTableDescriptor>();
312     HTableDescriptor[] tables = listTables();
313     for (HTableDescriptor table : tables) {
314       if (pattern.matcher(table.getTableName().getNameAsString()).matches()) {
315         matched.add(table);
316       }
317     }
318     return matched.toArray(new HTableDescriptor[matched.size()]);
319   }
320 
321   /**
322    * List all the userspace tables matching the given regular expression.
323    *
324    * @param regex The regular expression to match against
325    * @return - returns an array of HTableDescriptors
326    * @throws IOException if a remote or network exception occurs
327    * @see #listTables(java.util.regex.Pattern)
328    */
329   public HTableDescriptor[] listTables(String regex) throws IOException {
330     return listTables(Pattern.compile(regex));
331   }
332 
333   /**
334    * List all of the names of userspace tables.
335    * @return String[] table names
336    * @throws IOException if a remote or network exception occurs
337    */
338   @Deprecated
339   public String[] getTableNames() throws IOException {
340     return this.connection.getTableNames();
341   }
342 
343   /**
344    * List all of the names of userspace tables matching the given regular expression.
345    * @param pattern The regular expression to match against
346    * @return String[] table names
347    * @throws IOException if a remote or network exception occurs
348    */
349   @Deprecated
350   public String[] getTableNames(Pattern pattern) throws IOException {
351     List<String> matched = new ArrayList<String>();
352     for (String name: this.connection.getTableNames()) {
353       if (pattern.matcher(name).matches()) {
354         matched.add(name);
355       }
356     }
357     return matched.toArray(new String[matched.size()]);
358   }
359 
360   /**
361    * List all of the names of userspace tables matching the given regular expression.
362    * @param regex The regular expression to match against
363    * @return String[] table names
364    * @throws IOException if a remote or network exception occurs
365    */
366   @Deprecated
367   public String[] getTableNames(String regex) throws IOException {
368     return getTableNames(Pattern.compile(regex));
369   }
370 
371   /**
372    * List all of the names of userspace tables.
373    * @return TableName[] table names
374    * @throws IOException if a remote or network exception occurs
375    */
376   public TableName[] listTableNames() throws IOException {
377     return this.connection.listTableNames();
378   }
379 
380   /**
381    * Method for getting the tableDescriptor
382    * @param tableName as a byte []
383    * @return the tableDescriptor
384    * @throws TableNotFoundException
385    * @throws IOException if a remote or network exception occurs
386    */
387   public HTableDescriptor getTableDescriptor(final TableName tableName)
388   throws TableNotFoundException, IOException {
389     return this.connection.getHTableDescriptor(tableName);
390   }
391 
392   public HTableDescriptor getTableDescriptor(final byte[] tableName)
393   throws TableNotFoundException, IOException {
394     return getTableDescriptor(TableName.valueOf(tableName));
395   }
396 
397   private long getPauseTime(int tries) {
398     int triesCount = tries;
399     if (triesCount >= HConstants.RETRY_BACKOFF.length) {
400       triesCount = HConstants.RETRY_BACKOFF.length - 1;
401     }
402     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
403   }
404 
405   /**
406    * Creates a new table.
407    * Synchronous operation.
408    *
409    * @param desc table descriptor for table
410    *
411    * @throws IllegalArgumentException if the table name is reserved
412    * @throws MasterNotRunningException if master is not running
413    * @throws TableExistsException if table already exists (If concurrent
414    * threads, the table may have been created between test-for-existence
415    * and attempt-at-creation).
416    * @throws IOException if a remote or network exception occurs
417    */
418   public void createTable(HTableDescriptor desc)
419   throws IOException {
420     createTable(desc, null);
421   }
422 
423   /**
424    * Creates a new table with the specified number of regions.  The start key
425    * specified will become the end key of the first region of the table, and
426    * the end key specified will become the start key of the last region of the
427    * table (the first region has a null start key and the last region has a
428    * null end key).
429    *
430    * BigInteger math will be used to divide the key range specified into
431    * enough segments to make the required number of total regions.
432    *
433    * Synchronous operation.
434    *
435    * @param desc table descriptor for table
436    * @param startKey beginning of key range
437    * @param endKey end of key range
438    * @param numRegions the total number of regions to create
439    *
440    * @throws IllegalArgumentException if the table name is reserved
441    * @throws MasterNotRunningException if master is not running
442    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
443    * threads, the table may have been created between test-for-existence
444    * and attempt-at-creation).
445    * @throws IOException
446    */
447   public void createTable(HTableDescriptor desc, byte [] startKey,
448       byte [] endKey, int numRegions)
449   throws IOException {
450     if(numRegions < 3) {
451       throw new IllegalArgumentException("Must create at least three regions");
452     } else if(Bytes.compareTo(startKey, endKey) >= 0) {
453       throw new IllegalArgumentException("Start key must be smaller than end key");
454     }
455     if (numRegions == 3) {
456       createTable(desc, new byte[][]{startKey, endKey});
457       return;
458     }
459     byte [][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
460     if(splitKeys == null || splitKeys.length != numRegions - 1) {
461       throw new IllegalArgumentException("Unable to split key range into enough regions");
462     }
463     createTable(desc, splitKeys);
464   }
465 
466   /**
467    * Creates a new table with an initial set of empty regions defined by the
468    * specified split keys.  The total number of regions created will be the
469    * number of split keys plus one. Synchronous operation.
470    * Note : Avoid passing empty split key.
471    *
472    * @param desc table descriptor for table
473    * @param splitKeys array of split keys for the initial regions of the table
474    *
475    * @throws IllegalArgumentException if the table name is reserved, if the split keys
476    * are repeated and if the split key has empty byte array.
477    * @throws MasterNotRunningException if master is not running
478    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
479    * threads, the table may have been created between test-for-existence
480    * and attempt-at-creation).
481    * @throws IOException
482    */
483   public void createTable(final HTableDescriptor desc, byte [][] splitKeys)
484   throws IOException {
485     try {
486       createTableAsync(desc, splitKeys);
487     } catch (SocketTimeoutException ste) {
488       LOG.warn("Creating " + desc.getTableName() + " took too long", ste);
489     }
490     int numRegs = splitKeys == null ? 1 : splitKeys.length + 1;
491     int prevRegCount = 0;
492     boolean doneWithMetaScan = false;
493     for (int tries = 0; tries < this.numRetries * this.retryLongerMultiplier;
494       ++tries) {
495       if (!doneWithMetaScan) {
496         // Wait for new table to come on-line
497         final AtomicInteger actualRegCount = new AtomicInteger(0);
498         MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
499           @Override
500           public boolean processRow(Result rowResult) throws IOException {
501             HRegionInfo info = HRegionInfo.getHRegionInfo(rowResult);
502             if (info == null) {
503               LOG.warn("No serialized HRegionInfo in " + rowResult);
504               return true;
505             }
506             if (!info.getTable().equals(desc.getTableName())) {
507               return false;
508             }
509             ServerName serverName = HRegionInfo.getServerName(rowResult);
510             // Make sure that regions are assigned to server
511             if (!(info.isOffline() || info.isSplit()) && serverName != null
512                 && serverName.getHostAndPort() != null) {
513               actualRegCount.incrementAndGet();
514             }
515             return true;
516           }
517         };
518         MetaScanner.metaScan(conf, connection, visitor, desc.getTableName());
519         if (actualRegCount.get() < numRegs) {
520           if (tries == this.numRetries * this.retryLongerMultiplier - 1) {
521             throw new RegionOfflineException("Only " + actualRegCount.get() +
522               " of " + numRegs + " regions are online; retries exhausted.");
523           }
524           try { // Sleep
525             Thread.sleep(getPauseTime(tries));
526           } catch (InterruptedException e) {
527             throw new InterruptedIOException("Interrupted when opening" +
528               " regions; " + actualRegCount.get() + " of " + numRegs +
529               " regions processed so far");
530           }
531           if (actualRegCount.get() > prevRegCount) { // Making progress
532             prevRegCount = actualRegCount.get();
533             tries = -1;
534           }
535         } else {
536           doneWithMetaScan = true;
537           tries = -1;
538         }
539       } else if (isTableEnabled(desc.getTableName())) {
540         return;
541       } else {
542         try { // Sleep
543           Thread.sleep(getPauseTime(tries));
544         } catch (InterruptedException e) {
545           throw new InterruptedIOException("Interrupted when waiting" +
546             " for table to be enabled; meta scan was done");
547         }
548       }
549     }
550     throw new TableNotEnabledException(
551       "Retries exhausted while still waiting for table: "
552       + desc.getTableName() + " to be enabled");
553   }
554 
555   /**
556    * Creates a new table but does not block and wait for it to come online.
557    * Asynchronous operation.  To check if the table exists, use
558    * {@link #isTableAvailable} -- it is not safe to create an HTable
559    * instance to this table before it is available.
560    * Note : Avoid passing empty split key.
561    * @param desc table descriptor for table
562    *
563    * @throws IllegalArgumentException Bad table name, if the split keys
564    * are repeated and if the split key has empty byte array.
565    * @throws MasterNotRunningException if master is not running
566    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
567    * threads, the table may have been created between test-for-existence
568    * and attempt-at-creation).
569    * @throws IOException
570    */
571   public void createTableAsync(
572     final HTableDescriptor desc, final byte [][] splitKeys)
573   throws IOException {
574     if(desc.getTableName() == null) {
575       throw new IllegalArgumentException("TableName cannot be null");
576     }
577     if(splitKeys != null && splitKeys.length > 0) {
578       Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
579       // Verify there are no duplicate split keys
580       byte [] lastKey = null;
581       for(byte [] splitKey : splitKeys) {
582         if (Bytes.compareTo(splitKey, HConstants.EMPTY_BYTE_ARRAY) == 0) {
583           throw new IllegalArgumentException(
584               "Empty split key must not be passed in the split keys.");
585         }
586         if(lastKey != null && Bytes.equals(splitKey, lastKey)) {
587           throw new IllegalArgumentException("All split keys must be unique, " +
588             "found duplicate: " + Bytes.toStringBinary(splitKey) +
589             ", " + Bytes.toStringBinary(lastKey));
590         }
591         lastKey = splitKey;
592       }
593     }
594 
595     executeCallable(new MasterCallable<Void>(getConnection()) {
596       @Override
597       public Void call() throws ServiceException {
598         CreateTableRequest request = RequestConverter.buildCreateTableRequest(desc, splitKeys);
599         master.createTable(null, request);
600         return null;
601       }
602     });
603   }
604 
605   public void deleteTable(final String tableName) throws IOException {
606     deleteTable(TableName.valueOf(tableName));
607   }
608 
609   public void deleteTable(final byte[] tableName) throws IOException {
610     deleteTable(TableName.valueOf(tableName));
611   }
612 
613   /**
614    * Deletes a table.
615    * Synchronous operation.
616    *
617    * @param tableName name of table to delete
618    * @throws IOException if a remote or network exception occurs
619    */
620   public void deleteTable(final TableName tableName) throws IOException {
621     HRegionLocation firstMetaServer = getFirstMetaServerForTable(tableName);
622     boolean tableExists = true;
623 
624     executeCallable(new MasterCallable<Void>(getConnection()) {
625       @Override
626       public Void call() throws ServiceException {
627         DeleteTableRequest req = RequestConverter.buildDeleteTableRequest(tableName);
628         master.deleteTable(null,req);
629         return null;
630       }
631     });
632 
633     // Wait until all regions deleted
634     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
635       try {
636 
637         Scan scan = MetaReader.getScanForTableName(tableName);
638         scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
639         ScanRequest request = RequestConverter.buildScanRequest(
640           firstMetaServer.getRegionInfo().getRegionName(), scan, 1, true);
641         Result[] values = null;
642         // Get a batch at a time.
643         ClientService.BlockingInterface server = connection.getClient(firstMetaServer
644             .getServerName());
645         PayloadCarryingRpcController controller = new PayloadCarryingRpcController();
646         try {
647           controller.setPriority(tableName);
648           ScanResponse response = server.scan(controller, request);
649           values = ResponseConverter.getResults(controller.cellScanner(), response);
650         } catch (ServiceException se) {
651           throw ProtobufUtil.getRemoteException(se);
652         }
653 
654         // let us wait until hbase:meta table is updated and
655         // HMaster removes the table from its HTableDescriptors
656         if (values == null || values.length == 0) {
657           tableExists = false;
658           GetTableDescriptorsResponse htds;
659           MasterKeepAliveConnection master = connection.getKeepAliveMasterService();
660           try {
661             GetTableDescriptorsRequest req =
662               RequestConverter.buildGetTableDescriptorsRequest(tableName);
663             htds = master.getTableDescriptors(null, req);
664           } catch (ServiceException se) {
665             throw ProtobufUtil.getRemoteException(se);
666           } finally {
667             master.close();
668           }
669           tableExists = !htds.getTableSchemaList().isEmpty();
670           if (!tableExists) {
671             break;
672           }
673         }
674       } catch (IOException ex) {
675         if(tries == numRetries - 1) {           // no more tries left
676           if (ex instanceof RemoteException) {
677             throw ((RemoteException) ex).unwrapRemoteException();
678           } else {
679             throw ex;
680           }
681         }
682       }
683       try {
684         Thread.sleep(getPauseTime(tries));
685       } catch (InterruptedException e) {
686         // continue
687       }
688     }
689 
690     if (tableExists) {
691       throw new IOException("Retries exhausted, it took too long to wait"+
692         " for the table " + tableName + " to be deleted.");
693     }
694     // Delete cached information to prevent clients from using old locations
695     this.connection.clearRegionCache(tableName);
696     LOG.info("Deleted " + tableName);
697   }
698 
699   /**
700    * Deletes tables matching the passed in pattern and wait on completion.
701    *
702    * Warning: Use this method carefully, there is no prompting and the effect is
703    * immediate. Consider using {@link #listTables(java.lang.String)} and
704    * {@link #deleteTable(byte[])}
705    *
706    * @param regex The regular expression to match table names against
707    * @return Table descriptors for tables that couldn't be deleted
708    * @throws IOException
709    * @see #deleteTables(java.util.regex.Pattern)
710    * @see #deleteTable(java.lang.String)
711    */
712   public HTableDescriptor[] deleteTables(String regex) throws IOException {
713     return deleteTables(Pattern.compile(regex));
714   }
715 
716   /**
717    * Delete tables matching the passed in pattern and wait on completion.
718    *
719    * Warning: Use this method carefully, there is no prompting and the effect is
720    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
721    * {@link #deleteTable(byte[])}
722    *
723    * @param pattern The pattern to match table names against
724    * @return Table descriptors for tables that couldn't be deleted
725    * @throws IOException
726    */
727   public HTableDescriptor[] deleteTables(Pattern pattern) throws IOException {
728     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
729     for (HTableDescriptor table : listTables(pattern)) {
730       try {
731         deleteTable(table.getTableName());
732       } catch (IOException ex) {
733         LOG.info("Failed to delete table " + table.getTableName(), ex);
734         failed.add(table);
735       }
736     }
737     return failed.toArray(new HTableDescriptor[failed.size()]);
738   }
739 
740 
741   /**
742    * Enable a table.  May timeout.  Use {@link #enableTableAsync(byte[])}
743    * and {@link #isTableEnabled(byte[])} instead.
744    * The table has to be in disabled state for it to be enabled.
745    * @param tableName name of the table
746    * @throws IOException if a remote or network exception occurs
747    * There could be couple types of IOException
748    * TableNotFoundException means the table doesn't exist.
749    * TableNotDisabledException means the table isn't in disabled state.
750    * @see #isTableEnabled(byte[])
751    * @see #disableTable(byte[])
752    * @see #enableTableAsync(byte[])
753    */
754   public void enableTable(final TableName tableName)
755   throws IOException {
756     enableTableAsync(tableName);
757 
758     // Wait until all regions are enabled
759     waitUntilTableIsEnabled(tableName);
760 
761     LOG.info("Enabled table " + tableName);
762   }
763 
764   public void enableTable(final byte[] tableName)
765   throws IOException {
766     enableTable(TableName.valueOf(tableName));
767   }
768 
769   public void enableTable(final String tableName)
770   throws IOException {
771     enableTable(TableName.valueOf(tableName));
772   }
773 
774   /**
775    * Wait for the table to be enabled and available
776    * If enabling the table exceeds the retry period, an exception is thrown.
777    * @param tableName name of the table
778    * @throws IOException if a remote or network exception occurs or
779    *    table is not enabled after the retries period.
780    */
781   private void waitUntilTableIsEnabled(final TableName tableName) throws IOException {
782     boolean enabled = false;
783     long start = EnvironmentEdgeManager.currentTimeMillis();
784     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
785       try {
786         enabled = isTableEnabled(tableName);
787       } catch (TableNotFoundException tnfe) {
788         // wait for table to be created
789         enabled = false;
790       }
791       enabled = enabled && isTableAvailable(tableName);
792       if (enabled) {
793         break;
794       }
795       long sleep = getPauseTime(tries);
796       if (LOG.isDebugEnabled()) {
797         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
798           "enabled in " + tableName);
799       }
800       try {
801         Thread.sleep(sleep);
802       } catch (InterruptedException e) {
803         Thread.currentThread().interrupt();
804         // Do this conversion rather than let it out because do not want to
805         // change the method signature.
806         throw new IOException("Interrupted", e);
807       }
808     }
809     if (!enabled) {
810       long msec = EnvironmentEdgeManager.currentTimeMillis() - start;
811       throw new IOException("Table '" + tableName +
812         "' not yet enabled, after " + msec + "ms.");
813     }
814   }
815 
816   /**
817    * Brings a table on-line (enables it).  Method returns immediately though
818    * enable of table may take some time to complete, especially if the table
819    * is large (All regions are opened as part of enabling process).  Check
820    * {@link #isTableEnabled(byte[])} to learn when table is fully online.  If
821    * table is taking too long to online, check server logs.
822    * @param tableName
823    * @throws IOException
824    * @since 0.90.0
825    */
826   public void enableTableAsync(final TableName tableName)
827   throws IOException {
828     TableName.isLegalFullyQualifiedTableName(tableName.getName());
829     executeCallable(new MasterCallable<Void>(getConnection()) {
830       @Override
831       public Void call() throws ServiceException {
832         LOG.info("Started enable of " + tableName);
833         EnableTableRequest req = RequestConverter.buildEnableTableRequest(tableName);
834         master.enableTable(null,req);
835         return null;
836       }
837     });
838   }
839 
840   public void enableTableAsync(final byte[] tableName)
841   throws IOException {
842     enableTable(TableName.valueOf(tableName));
843   }
844 
845   public void enableTableAsync(final String tableName)
846   throws IOException {
847     enableTableAsync(TableName.valueOf(tableName));
848   }
849 
850   /**
851    * Enable tables matching the passed in pattern and wait on completion.
852    *
853    * Warning: Use this method carefully, there is no prompting and the effect is
854    * immediate. Consider using {@link #listTables(java.lang.String)} and
855    * {@link #enableTable(byte[])}
856    *
857    * @param regex The regular expression to match table names against
858    * @throws IOException
859    * @see #enableTables(java.util.regex.Pattern)
860    * @see #enableTable(java.lang.String)
861    */
862   public HTableDescriptor[] enableTables(String regex) throws IOException {
863     return enableTables(Pattern.compile(regex));
864   }
865 
866   /**
867    * Enable tables matching the passed in pattern and wait on completion.
868    *
869    * Warning: Use this method carefully, there is no prompting and the effect is
870    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
871    * {@link #enableTable(byte[])}
872    *
873    * @param pattern The pattern to match table names against
874    * @throws IOException
875    */
876   public HTableDescriptor[] enableTables(Pattern pattern) throws IOException {
877     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
878     for (HTableDescriptor table : listTables(pattern)) {
879       if (isTableDisabled(table.getTableName())) {
880         try {
881           enableTable(table.getTableName());
882         } catch (IOException ex) {
883           LOG.info("Failed to enable table " + table.getTableName(), ex);
884           failed.add(table);
885         }
886       }
887     }
888     return failed.toArray(new HTableDescriptor[failed.size()]);
889   }
890 
891   /**
892    * Starts the disable of a table.  If it is being served, the master
893    * will tell the servers to stop serving it.  This method returns immediately.
894    * The disable of a table can take some time if the table is large (all
895    * regions are closed as part of table disable operation).
896    * Call {@link #isTableDisabled(byte[])} to check for when disable completes.
897    * If table is taking too long to online, check server logs.
898    * @param tableName name of table
899    * @throws IOException if a remote or network exception occurs
900    * @see #isTableDisabled(byte[])
901    * @see #isTableEnabled(byte[])
902    * @since 0.90.0
903    */
904   public void disableTableAsync(final TableName tableName) throws IOException {
905     TableName.isLegalFullyQualifiedTableName(tableName.getName());
906     executeCallable(new MasterCallable<Void>(getConnection()) {
907       @Override
908       public Void call() throws ServiceException {
909         LOG.info("Started disable of " + tableName);
910         DisableTableRequest req = RequestConverter.buildDisableTableRequest(tableName);
911         master.disableTable(null,req);
912         return null;
913       }
914     });
915   }
916 
917   public void disableTableAsync(final byte[] tableName) throws IOException {
918     disableTableAsync(TableName.valueOf(tableName));
919   }
920 
921   public void disableTableAsync(final String tableName) throws IOException {
922     disableTableAsync(TableName.valueOf(tableName));
923   }
924 
925   /**
926    * Disable table and wait on completion.  May timeout eventually.  Use
927    * {@link #disableTableAsync(byte[])} and {@link #isTableDisabled(String)}
928    * instead.
929    * The table has to be in enabled state for it to be disabled.
930    * @param tableName
931    * @throws IOException
932    * There could be couple types of IOException
933    * TableNotFoundException means the table doesn't exist.
934    * TableNotEnabledException means the table isn't in enabled state.
935    */
936   public void disableTable(final TableName tableName)
937   throws IOException {
938     disableTableAsync(tableName);
939     // Wait until table is disabled
940     boolean disabled = false;
941     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
942       disabled = isTableDisabled(tableName);
943       if (disabled) {
944         break;
945       }
946       long sleep = getPauseTime(tries);
947       if (LOG.isDebugEnabled()) {
948         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
949           "disabled in " + tableName);
950       }
951       try {
952         Thread.sleep(sleep);
953       } catch (InterruptedException e) {
954         // Do this conversion rather than let it out because do not want to
955         // change the method signature.
956         Thread.currentThread().interrupt();
957         throw new IOException("Interrupted", e);
958       }
959     }
960     if (!disabled) {
961       throw new RegionException("Retries exhausted, it took too long to wait"+
962         " for the table " + tableName + " to be disabled.");
963     }
964     LOG.info("Disabled " + tableName);
965   }
966 
967   public void disableTable(final byte[] tableName)
968   throws IOException {
969     disableTable(TableName.valueOf(tableName));
970   }
971 
972   public void disableTable(final String tableName)
973   throws IOException {
974     disableTable(TableName.valueOf(tableName));
975   }
976 
977   /**
978    * Disable tables matching the passed in pattern and wait on completion.
979    *
980    * Warning: Use this method carefully, there is no prompting and the effect is
981    * immediate. Consider using {@link #listTables(java.lang.String)} and
982    * {@link #disableTable(byte[])}
983    *
984    * @param regex The regular expression to match table names against
985    * @return Table descriptors for tables that couldn't be disabled
986    * @throws IOException
987    * @see #disableTables(java.util.regex.Pattern)
988    * @see #disableTable(java.lang.String)
989    */
990   public HTableDescriptor[] disableTables(String regex) throws IOException {
991     return disableTables(Pattern.compile(regex));
992   }
993 
994   /**
995    * Disable tables matching the passed in pattern and wait on completion.
996    *
997    * Warning: Use this method carefully, there is no prompting and the effect is
998    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
999    * {@link #disableTable(byte[])}
1000    *
1001    * @param pattern The pattern to match table names against
1002    * @return Table descriptors for tables that couldn't be disabled
1003    * @throws IOException
1004    */
1005   public HTableDescriptor[] disableTables(Pattern pattern) throws IOException {
1006     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
1007     for (HTableDescriptor table : listTables(pattern)) {
1008       if (isTableEnabled(table.getTableName())) {
1009         try {
1010           disableTable(table.getTableName());
1011         } catch (IOException ex) {
1012           LOG.info("Failed to disable table " + table.getTableName(), ex);
1013           failed.add(table);
1014         }
1015       }
1016     }
1017     return failed.toArray(new HTableDescriptor[failed.size()]);
1018   }
1019 
1020   /*
1021    * Checks whether table exists. If not, throws TableNotFoundException
1022    * @param tableName
1023    */
1024   private void checkTableExistence(TableName tableName) throws IOException {
1025     if (!tableExists(tableName)) {
1026       throw new TableNotFoundException(tableName);
1027     }
1028   }
1029   
1030   /**
1031    * @param tableName name of table to check
1032    * @return true if table is on-line
1033    * @throws IOException if a remote or network exception occurs
1034    */
1035   public boolean isTableEnabled(TableName tableName) throws IOException {
1036     checkTableExistence(tableName);
1037     return connection.isTableEnabled(tableName);
1038   }
1039 
1040   public boolean isTableEnabled(byte[] tableName) throws IOException {
1041     return isTableEnabled(TableName.valueOf(tableName));
1042   }
1043 
1044   public boolean isTableEnabled(String tableName) throws IOException {
1045     return isTableEnabled(TableName.valueOf(tableName));
1046   }
1047 
1048 
1049 
1050   /**
1051    * @param tableName name of table to check
1052    * @return true if table is off-line
1053    * @throws IOException if a remote or network exception occurs
1054    */
1055   public boolean isTableDisabled(TableName tableName) throws IOException {
1056     checkTableExistence(tableName);
1057     return connection.isTableDisabled(tableName);
1058   }
1059 
1060   public boolean isTableDisabled(byte[] tableName) throws IOException {
1061     return isTableDisabled(TableName.valueOf(tableName));
1062   }
1063 
1064   public boolean isTableDisabled(String tableName) throws IOException {
1065     return isTableDisabled(TableName.valueOf(tableName));
1066   }
1067 
1068   /**
1069    * @param tableName name of table to check
1070    * @return true if all regions of the table are available
1071    * @throws IOException if a remote or network exception occurs
1072    */
1073   public boolean isTableAvailable(TableName tableName) throws IOException {
1074     return connection.isTableAvailable(tableName);
1075   }
1076 
1077   public boolean isTableAvailable(byte[] tableName) throws IOException {
1078     return isTableAvailable(TableName.valueOf(tableName));
1079   }
1080 
1081   public boolean isTableAvailable(String tableName) throws IOException {
1082     return isTableAvailable(TableName.valueOf(tableName));
1083   }
1084 
1085   /**
1086    * Use this api to check if the table has been created with the specified number of
1087    * splitkeys which was used while creating the given table.
1088    * Note : If this api is used after a table's region gets splitted, the api may return
1089    * false.
1090    * @param tableName
1091    *          name of table to check
1092    * @param splitKeys
1093    *          keys to check if the table has been created with all split keys
1094    * @throws IOException
1095    *           if a remote or network excpetion occurs
1096    */
1097   public boolean isTableAvailable(TableName tableName,
1098                                   byte[][] splitKeys) throws IOException {
1099     return connection.isTableAvailable(tableName, splitKeys);
1100   }
1101 
1102   public boolean isTableAvailable(byte[] tableName,
1103                                   byte[][] splitKeys) throws IOException {
1104     return isTableAvailable(TableName.valueOf(tableName), splitKeys);
1105   }
1106 
1107   public boolean isTableAvailable(String tableName,
1108                                   byte[][] splitKeys) throws IOException {
1109     return isTableAvailable(TableName.valueOf(tableName), splitKeys);
1110   }
1111 
1112   /**
1113    * Get the status of alter command - indicates how many regions have received
1114    * the updated schema Asynchronous operation.
1115    *
1116    * @param tableName TableName instance
1117    * @return Pair indicating the number of regions updated Pair.getFirst() is the
1118    *         regions that are yet to be updated Pair.getSecond() is the total number
1119    *         of regions of the table
1120    * @throws IOException
1121    *           if a remote or network exception occurs
1122    */
1123   public Pair<Integer, Integer> getAlterStatus(final TableName tableName)
1124   throws IOException {
1125     return executeCallable(new MasterCallable<Pair<Integer, Integer>>(getConnection()) {
1126       @Override
1127       public Pair<Integer, Integer> call() throws ServiceException {
1128         GetSchemaAlterStatusRequest req = RequestConverter
1129             .buildGetSchemaAlterStatusRequest(tableName);
1130         GetSchemaAlterStatusResponse ret = master.getSchemaAlterStatus(null, req);
1131         Pair<Integer, Integer> pair = new Pair<Integer, Integer>(Integer.valueOf(ret
1132             .getYetToUpdateRegions()), Integer.valueOf(ret.getTotalRegions()));
1133         return pair;
1134       }
1135     });
1136   }
1137 
1138   /**
1139    * Get the status of alter command - indicates how many regions have received
1140    * the updated schema Asynchronous operation.
1141    *
1142    * @param tableName
1143    *          name of the table to get the status of
1144    * @return Pair indicating the number of regions updated Pair.getFirst() is the
1145    *         regions that are yet to be updated Pair.getSecond() is the total number
1146    *         of regions of the table
1147    * @throws IOException
1148    *           if a remote or network exception occurs
1149    */
1150   public Pair<Integer, Integer> getAlterStatus(final byte[] tableName)
1151    throws IOException {
1152     return getAlterStatus(TableName.valueOf(tableName));
1153   }
1154 
1155   /**
1156    * Add a column to an existing table.
1157    * Asynchronous operation.
1158    *
1159    * @param tableName name of the table to add column to
1160    * @param column column descriptor of column to be added
1161    * @throws IOException if a remote or network exception occurs
1162    */
1163   public void addColumn(final byte[] tableName, HColumnDescriptor column)
1164   throws IOException {
1165     addColumn(TableName.valueOf(tableName), column);
1166   }
1167 
1168 
1169   /**
1170    * Add a column to an existing table.
1171    * Asynchronous operation.
1172    *
1173    * @param tableName name of the table to add column to
1174    * @param column column descriptor of column to be added
1175    * @throws IOException if a remote or network exception occurs
1176    */
1177   public void addColumn(final String tableName, HColumnDescriptor column)
1178   throws IOException {
1179     addColumn(TableName.valueOf(tableName), column);
1180   }
1181 
1182   /**
1183    * Add a column to an existing table.
1184    * Asynchronous operation.
1185    *
1186    * @param tableName name of the table to add column to
1187    * @param column column descriptor of column to be added
1188    * @throws IOException if a remote or network exception occurs
1189    */
1190   public void addColumn(final TableName tableName, final HColumnDescriptor column)
1191   throws IOException {
1192     executeCallable(new MasterCallable<Void>(getConnection()) {
1193       @Override
1194       public Void call() throws ServiceException {
1195         AddColumnRequest req = RequestConverter.buildAddColumnRequest(tableName, column);
1196         master.addColumn(null,req);
1197         return null;
1198       }
1199     });
1200   }
1201 
1202   /**
1203    * Delete a column from a table.
1204    * Asynchronous operation.
1205    *
1206    * @param tableName name of table
1207    * @param columnName name of column to be deleted
1208    * @throws IOException if a remote or network exception occurs
1209    */
1210   public void deleteColumn(final byte[] tableName, final String columnName)
1211   throws IOException {
1212     deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName));
1213   }
1214 
1215   /**
1216    * Delete a column from a table.
1217    * Asynchronous operation.
1218    *
1219    * @param tableName name of table
1220    * @param columnName name of column to be deleted
1221    * @throws IOException if a remote or network exception occurs
1222    */
1223   public void deleteColumn(final String tableName, final String columnName)
1224   throws IOException {
1225     deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName));
1226   }
1227 
1228   /**
1229    * Delete a column from a table.
1230    * Asynchronous operation.
1231    *
1232    * @param tableName name of table
1233    * @param columnName name of column to be deleted
1234    * @throws IOException if a remote or network exception occurs
1235    */
1236   public void deleteColumn(final TableName tableName, final byte [] columnName)
1237   throws IOException {
1238     executeCallable(new MasterCallable<Void>(getConnection()) {
1239       @Override
1240       public Void call() throws ServiceException {
1241         DeleteColumnRequest req = RequestConverter.buildDeleteColumnRequest(tableName, columnName);
1242         master.deleteColumn(null,req);
1243         return null;
1244       }
1245     });
1246   }
1247 
1248   /**
1249    * Modify an existing column family on a table.
1250    * Asynchronous operation.
1251    *
1252    * @param tableName name of table
1253    * @param descriptor new column descriptor to use
1254    * @throws IOException if a remote or network exception occurs
1255    */
1256   public void modifyColumn(final String tableName, HColumnDescriptor descriptor)
1257   throws IOException {
1258     modifyColumn(TableName.valueOf(tableName), descriptor);
1259   }
1260 
1261   /**
1262    * Modify an existing column family on a table.
1263    * Asynchronous operation.
1264    *
1265    * @param tableName name of table
1266    * @param descriptor new column descriptor to use
1267    * @throws IOException if a remote or network exception occurs
1268    */
1269   public void modifyColumn(final byte[] tableName, HColumnDescriptor descriptor)
1270   throws IOException {
1271     modifyColumn(TableName.valueOf(tableName), descriptor);
1272   }
1273 
1274 
1275 
1276   /**
1277    * Modify an existing column family on a table.
1278    * Asynchronous operation.
1279    *
1280    * @param tableName name of table
1281    * @param descriptor new column descriptor to use
1282    * @throws IOException if a remote or network exception occurs
1283    */
1284   public void modifyColumn(final TableName tableName, final HColumnDescriptor descriptor)
1285   throws IOException {
1286     executeCallable(new MasterCallable<Void>(getConnection()) {
1287       @Override
1288       public Void call() throws ServiceException {
1289         ModifyColumnRequest req = RequestConverter.buildModifyColumnRequest(tableName, descriptor);
1290         master.modifyColumn(null,req);
1291         return null;
1292       }
1293     });
1294   }
1295 
1296   /**
1297    * Close a region. For expert-admins.  Runs close on the regionserver.  The
1298    * master will not be informed of the close.
1299    * @param regionname region name to close
1300    * @param serverName If supplied, we'll use this location rather than
1301    * the one currently in <code>hbase:meta</code>
1302    * @throws IOException if a remote or network exception occurs
1303    */
1304   public void closeRegion(final String regionname, final String serverName)
1305   throws IOException {
1306     closeRegion(Bytes.toBytes(regionname), serverName);
1307   }
1308 
1309   /**
1310    * Close a region.  For expert-admins  Runs close on the regionserver.  The
1311    * master will not be informed of the close.
1312    * @param regionname region name to close
1313    * @param serverName The servername of the regionserver.  If passed null we
1314    * will use servername found in the hbase:meta table. A server name
1315    * is made of host, port and startcode.  Here is an example:
1316    * <code> host187.example.com,60020,1289493121758</code>
1317    * @throws IOException if a remote or network exception occurs
1318    */
1319   public void closeRegion(final byte [] regionname, final String serverName)
1320   throws IOException {
1321     CatalogTracker ct = getCatalogTracker();
1322     try {
1323       if (serverName != null) {
1324         Pair<HRegionInfo, ServerName> pair = MetaReader.getRegion(ct, regionname);
1325         if (pair == null || pair.getFirst() == null) {
1326           throw new UnknownRegionException(Bytes.toStringBinary(regionname));
1327         } else {
1328           closeRegion(ServerName.valueOf(serverName), pair.getFirst());
1329         }
1330       } else {
1331         Pair<HRegionInfo, ServerName> pair = MetaReader.getRegion(ct, regionname);
1332         if (pair == null) {
1333           throw new UnknownRegionException(Bytes.toStringBinary(regionname));
1334         } else if (pair.getSecond() == null) {
1335           throw new NoServerForRegionException(Bytes.toStringBinary(regionname));
1336         } else {
1337           closeRegion(pair.getSecond(), pair.getFirst());
1338         }
1339       }
1340     } finally {
1341       cleanupCatalogTracker(ct);
1342     }
1343   }
1344 
1345   /**
1346    * For expert-admins. Runs close on the regionserver. Closes a region based on
1347    * the encoded region name. The region server name is mandatory. If the
1348    * servername is provided then based on the online regions in the specified
1349    * regionserver the specified region will be closed. The master will not be
1350    * informed of the close. Note that the regionname is the encoded regionname.
1351    *
1352    * @param encodedRegionName
1353    *          The encoded region name; i.e. the hash that makes up the region
1354    *          name suffix: e.g. if regionname is
1355    *          <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>
1356    *          , then the encoded region name is:
1357    *          <code>527db22f95c8a9e0116f0cc13c680396</code>.
1358    * @param serverName
1359    *          The servername of the regionserver. A server name is made of host,
1360    *          port and startcode. This is mandatory. Here is an example:
1361    *          <code> host187.example.com,60020,1289493121758</code>
1362    * @return true if the region was closed, false if not.
1363    * @throws IOException
1364    *           if a remote or network exception occurs
1365    */
1366   public boolean closeRegionWithEncodedRegionName(final String encodedRegionName,
1367       final String serverName) throws IOException {
1368     if (null == serverName || ("").equals(serverName.trim())) {
1369       throw new IllegalArgumentException(
1370           "The servername cannot be null or empty.");
1371     }
1372     ServerName sn = ServerName.valueOf(serverName);
1373     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1374     // Close the region without updating zk state.
1375     CloseRegionRequest request =
1376       RequestConverter.buildCloseRegionRequest(sn, encodedRegionName, false);
1377     try {
1378       CloseRegionResponse response = admin.closeRegion(null, request);
1379       boolean isRegionClosed = response.getClosed();
1380       if (false == isRegionClosed) {
1381         LOG.error("Not able to close the region " + encodedRegionName + ".");
1382       }
1383       return isRegionClosed;
1384     } catch (ServiceException se) {
1385       throw ProtobufUtil.getRemoteException(se);
1386     }
1387   }
1388 
1389   /**
1390    * Close a region.  For expert-admins  Runs close on the regionserver.  The
1391    * master will not be informed of the close.
1392    * @param sn
1393    * @param hri
1394    * @throws IOException
1395    */
1396   public void closeRegion(final ServerName sn, final HRegionInfo hri)
1397   throws IOException {
1398     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1399     // Close the region without updating zk state.
1400     ProtobufUtil.closeRegion(admin, sn, hri.getRegionName(), false);
1401   }
1402 
1403   /**
1404    * Get all the online regions on a region server.
1405    */
1406   public List<HRegionInfo> getOnlineRegions(
1407       final ServerName sn) throws IOException {
1408     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1409     return ProtobufUtil.getOnlineRegions(admin);
1410   }
1411 
1412   /**
1413    * Flush a table or an individual region.
1414    * Synchronous operation.
1415    *
1416    * @param tableNameOrRegionName table or region to flush
1417    * @throws IOException if a remote or network exception occurs
1418    * @throws InterruptedException
1419    */
1420   public void flush(final String tableNameOrRegionName)
1421   throws IOException, InterruptedException {
1422     flush(Bytes.toBytes(tableNameOrRegionName));
1423   }
1424 
1425   /**
1426    * Flush a table or an individual region.
1427    * Synchronous operation.
1428    *
1429    * @param tableNameOrRegionName table or region to flush
1430    * @throws IOException if a remote or network exception occurs
1431    * @throws InterruptedException
1432    */
1433   public void flush(final byte[] tableNameOrRegionName)
1434   throws IOException, InterruptedException {
1435     CatalogTracker ct = getCatalogTracker();
1436     try {
1437       Pair<HRegionInfo, ServerName> regionServerPair
1438         = getRegion(tableNameOrRegionName, ct);
1439       if (regionServerPair != null) {
1440         if (regionServerPair.getSecond() == null) {
1441           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1442         } else {
1443           flush(regionServerPair.getSecond(), regionServerPair.getFirst());
1444         }
1445       } else {
1446         final TableName tableName = checkTableExists(
1447             TableName.valueOf(tableNameOrRegionName), ct);
1448         List<Pair<HRegionInfo, ServerName>> pairs =
1449           MetaReader.getTableRegionsAndLocations(ct,
1450               tableName);
1451         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1452           if (pair.getFirst().isOffline()) continue;
1453           if (pair.getSecond() == null) continue;
1454           try {
1455             flush(pair.getSecond(), pair.getFirst());
1456           } catch (NotServingRegionException e) {
1457             if (LOG.isDebugEnabled()) {
1458               LOG.debug("Trying to flush " + pair.getFirst() + ": " +
1459                 StringUtils.stringifyException(e));
1460             }
1461           }
1462         }
1463       }
1464     } finally {
1465       cleanupCatalogTracker(ct);
1466     }
1467   }
1468 
1469   private void flush(final ServerName sn, final HRegionInfo hri)
1470   throws IOException {
1471     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1472     FlushRegionRequest request =
1473       RequestConverter.buildFlushRegionRequest(hri.getRegionName());
1474     try {
1475       admin.flushRegion(null, request);
1476     } catch (ServiceException se) {
1477       throw ProtobufUtil.getRemoteException(se);
1478     }
1479   }
1480 
1481   /**
1482    * Compact a table or an individual region.
1483    * Asynchronous operation.
1484    *
1485    * @param tableNameOrRegionName table or region to compact
1486    * @throws IOException if a remote or network exception occurs
1487    * @throws InterruptedException
1488    */
1489   public void compact(final String tableNameOrRegionName)
1490   throws IOException, InterruptedException {
1491     compact(Bytes.toBytes(tableNameOrRegionName));
1492   }
1493 
1494   /**
1495    * Compact a table or an individual region.
1496    * Asynchronous operation.
1497    *
1498    * @param tableNameOrRegionName table or region to compact
1499    * @throws IOException if a remote or network exception occurs
1500    * @throws InterruptedException
1501    */
1502   public void compact(final byte[] tableNameOrRegionName)
1503   throws IOException, InterruptedException {
1504     compact(tableNameOrRegionName, null, false);
1505   }
1506 
1507   /**
1508    * Compact a column family within a table or region.
1509    * Asynchronous operation.
1510    *
1511    * @param tableOrRegionName table or region to compact
1512    * @param columnFamily column family within a table or region
1513    * @throws IOException if a remote or network exception occurs
1514    * @throws InterruptedException
1515    */
1516   public void compact(String tableOrRegionName, String columnFamily)
1517     throws IOException,  InterruptedException {
1518     compact(Bytes.toBytes(tableOrRegionName), Bytes.toBytes(columnFamily));
1519   }
1520 
1521   /**
1522    * Compact a column family within a table or region.
1523    * Asynchronous operation.
1524    *
1525    * @param tableNameOrRegionName table or region to compact
1526    * @param columnFamily column family within a table or region
1527    * @throws IOException if a remote or network exception occurs
1528    * @throws InterruptedException
1529    */
1530   public void compact(final byte[] tableNameOrRegionName, final byte[] columnFamily)
1531   throws IOException, InterruptedException {
1532     compact(tableNameOrRegionName, columnFamily, false);
1533   }
1534 
1535   /**
1536    * Major compact a table or an individual region.
1537    * Asynchronous operation.
1538    *
1539    * @param tableNameOrRegionName table or region to major compact
1540    * @throws IOException if a remote or network exception occurs
1541    * @throws InterruptedException
1542    */
1543   public void majorCompact(final String tableNameOrRegionName)
1544   throws IOException, InterruptedException {
1545     majorCompact(Bytes.toBytes(tableNameOrRegionName));
1546   }
1547 
1548   /**
1549    * Major compact a table or an individual region.
1550    * Asynchronous operation.
1551    *
1552    * @param tableNameOrRegionName table or region to major compact
1553    * @throws IOException if a remote or network exception occurs
1554    * @throws InterruptedException
1555    */
1556   public void majorCompact(final byte[] tableNameOrRegionName)
1557   throws IOException, InterruptedException {
1558     compact(tableNameOrRegionName, null, true);
1559   }
1560 
1561   /**
1562    * Major compact a column family within a table or region.
1563    * Asynchronous operation.
1564    *
1565    * @param tableNameOrRegionName table or region to major compact
1566    * @param columnFamily column family within a table or region
1567    * @throws IOException if a remote or network exception occurs
1568    * @throws InterruptedException
1569    */
1570   public void majorCompact(final String tableNameOrRegionName,
1571     final String columnFamily) throws IOException, InterruptedException {
1572     majorCompact(Bytes.toBytes(tableNameOrRegionName),
1573       Bytes.toBytes(columnFamily));
1574   }
1575 
1576   /**
1577    * Major compact a column family within a table or region.
1578    * Asynchronous operation.
1579    *
1580    * @param tableNameOrRegionName table or region to major compact
1581    * @param columnFamily column family within a table or region
1582    * @throws IOException if a remote or network exception occurs
1583    * @throws InterruptedException
1584    */
1585   public void majorCompact(final byte[] tableNameOrRegionName,
1586     final byte[] columnFamily) throws IOException, InterruptedException {
1587     compact(tableNameOrRegionName, columnFamily, true);
1588   }
1589 
1590   /**
1591    * Compact a table or an individual region.
1592    * Asynchronous operation.
1593    *
1594    * @param tableNameOrRegionName table or region to compact
1595    * @param columnFamily column family within a table or region
1596    * @param major True if we are to do a major compaction.
1597    * @throws IOException if a remote or network exception occurs
1598    * @throws InterruptedException
1599    */
1600   private void compact(final byte[] tableNameOrRegionName,
1601     final byte[] columnFamily,final boolean major)
1602   throws IOException, InterruptedException {
1603     CatalogTracker ct = getCatalogTracker();
1604     try {
1605       Pair<HRegionInfo, ServerName> regionServerPair
1606         = getRegion(tableNameOrRegionName, ct);
1607       if (regionServerPair != null) {
1608         if (regionServerPair.getSecond() == null) {
1609           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1610         } else {
1611           compact(regionServerPair.getSecond(), regionServerPair.getFirst(), major, columnFamily);
1612         }
1613       } else {
1614         final TableName tableName =
1615             checkTableExists(TableName.valueOf(tableNameOrRegionName), ct);
1616         List<Pair<HRegionInfo, ServerName>> pairs =
1617           MetaReader.getTableRegionsAndLocations(ct,
1618               tableName);
1619         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1620           if (pair.getFirst().isOffline()) continue;
1621           if (pair.getSecond() == null) continue;
1622           try {
1623             compact(pair.getSecond(), pair.getFirst(), major, columnFamily);
1624           } catch (NotServingRegionException e) {
1625             if (LOG.isDebugEnabled()) {
1626               LOG.debug("Trying to" + (major ? " major" : "") + " compact " +
1627                 pair.getFirst() + ": " +
1628                 StringUtils.stringifyException(e));
1629             }
1630           }
1631         }
1632       }
1633     } finally {
1634       cleanupCatalogTracker(ct);
1635     }
1636   }
1637 
1638   private void compact(final ServerName sn, final HRegionInfo hri,
1639       final boolean major, final byte [] family)
1640   throws IOException {
1641     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1642     CompactRegionRequest request =
1643       RequestConverter.buildCompactRegionRequest(hri.getRegionName(), major, family);
1644     try {
1645       admin.compactRegion(null, request);
1646     } catch (ServiceException se) {
1647       throw ProtobufUtil.getRemoteException(se);
1648     }
1649   }
1650 
1651   /**
1652    * Move the region <code>r</code> to <code>dest</code>.
1653    * @param encodedRegionName The encoded region name; i.e. the hash that makes
1654    * up the region name suffix: e.g. if regionname is
1655    * <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>,
1656    * then the encoded region name is: <code>527db22f95c8a9e0116f0cc13c680396</code>.
1657    * @param destServerName The servername of the destination regionserver.  If
1658    * passed the empty byte array we'll assign to a random server.  A server name
1659    * is made of host, port and startcode.  Here is an example:
1660    * <code> host187.example.com,60020,1289493121758</code>
1661    * @throws UnknownRegionException Thrown if we can't find a region named
1662    * <code>encodedRegionName</code>
1663    * @throws ZooKeeperConnectionException
1664    * @throws MasterNotRunningException
1665    */
1666   public void move(final byte [] encodedRegionName, final byte [] destServerName)
1667   throws HBaseIOException, MasterNotRunningException, ZooKeeperConnectionException {
1668     MasterKeepAliveConnection stub = connection.getKeepAliveMasterService();
1669     try {
1670       MoveRegionRequest request =
1671         RequestConverter.buildMoveRegionRequest(encodedRegionName, destServerName);
1672       stub.moveRegion(null,request);
1673     } catch (ServiceException se) {
1674       IOException ioe = ProtobufUtil.getRemoteException(se);
1675       if (ioe instanceof HBaseIOException) {
1676         throw (HBaseIOException)ioe;
1677       }
1678       LOG.error("Unexpected exception: " + se + " from calling HMaster.moveRegion");
1679     } catch (DeserializationException de) {
1680       LOG.error("Could not parse destination server name: " + de);
1681     } finally {
1682       stub.close();
1683     }
1684   }
1685 
1686   /**
1687    * @param regionName
1688    *          Region name to assign.
1689    * @throws MasterNotRunningException
1690    * @throws ZooKeeperConnectionException
1691    * @throws IOException
1692    */
1693   public void assign(final byte[] regionName) throws MasterNotRunningException,
1694       ZooKeeperConnectionException, IOException {
1695     final byte[] toBeAssigned = getRegionName(regionName);
1696     executeCallable(new MasterCallable<Void>(getConnection()) {
1697       @Override
1698       public Void call() throws ServiceException {
1699         AssignRegionRequest request =
1700           RequestConverter.buildAssignRegionRequest(toBeAssigned);
1701         master.assignRegion(null,request);
1702         return null;
1703       }
1704     });
1705   }
1706 
1707   /**
1708    * Unassign a region from current hosting regionserver.  Region will then be
1709    * assigned to a regionserver chosen at random.  Region could be reassigned
1710    * back to the same server.  Use {@link #move(byte[], byte[])} if you want
1711    * to control the region movement.
1712    * @param regionName Region to unassign. Will clear any existing RegionPlan
1713    * if one found.
1714    * @param force If true, force unassign (Will remove region from
1715    * regions-in-transition too if present. If results in double assignment
1716    * use hbck -fix to resolve. To be used by experts).
1717    * @throws MasterNotRunningException
1718    * @throws ZooKeeperConnectionException
1719    * @throws IOException
1720    */
1721   public void unassign(final byte [] regionName, final boolean force)
1722   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
1723     final byte[] toBeUnassigned = getRegionName(regionName);
1724     executeCallable(new MasterCallable<Void>(getConnection()) {
1725       @Override
1726       public Void call() throws ServiceException {
1727         UnassignRegionRequest request =
1728           RequestConverter.buildUnassignRegionRequest(toBeUnassigned, force);
1729         master.unassignRegion(null,request);
1730         return null;
1731       }
1732     });
1733   }
1734 
1735   /**
1736    * Offline specified region from master's in-memory state. It will not attempt to reassign the
1737    * region as in unassign. This API can be used when a region not served by any region server and
1738    * still online as per Master's in memory state. If this API is incorrectly used on active region
1739    * then master will loose track of that region.
1740    *
1741    * This is a special method that should be used by experts or hbck.
1742    *
1743    * @param regionName
1744    *          Region to offline.
1745    * @throws IOException
1746    */
1747   public void offline(final byte [] regionName)
1748   throws IOException {
1749     MasterKeepAliveConnection master = connection.getKeepAliveMasterService();
1750     try {
1751       master.offlineRegion(null,RequestConverter.buildOfflineRegionRequest(regionName));
1752     } catch (ServiceException se) {
1753       throw ProtobufUtil.getRemoteException(se);
1754     } finally {
1755       master.close();
1756     }
1757   }
1758 
1759   /**
1760    * Turn the load balancer on or off.
1761    * @param on If true, enable balancer. If false, disable balancer.
1762    * @param synchronous If true, it waits until current balance() call, if outstanding, to return.
1763    * @return Previous balancer value
1764    */
1765   public boolean setBalancerRunning(final boolean on, final boolean synchronous)
1766   throws MasterNotRunningException, ZooKeeperConnectionException {
1767     MasterKeepAliveConnection stub = connection.getKeepAliveMasterService();
1768     try {
1769       SetBalancerRunningRequest req =
1770         RequestConverter.buildSetBalancerRunningRequest(on, synchronous);
1771       return stub.setBalancerRunning(null, req).getPrevBalanceValue();
1772     } catch (ServiceException se) {
1773       IOException ioe = ProtobufUtil.getRemoteException(se);
1774       if (ioe instanceof MasterNotRunningException) {
1775         throw (MasterNotRunningException)ioe;
1776       }
1777       if (ioe instanceof ZooKeeperConnectionException) {
1778         throw (ZooKeeperConnectionException)ioe;
1779       }
1780 
1781       // Throwing MasterNotRunningException even though not really valid in order to not
1782       // break interface by adding additional exception type.
1783       throw new MasterNotRunningException("Unexpected exception when calling balanceSwitch",se);
1784     } finally {
1785       stub.close();
1786     }
1787   }
1788 
1789   /**
1790    * Invoke the balancer.  Will run the balancer and if regions to move, it will
1791    * go ahead and do the reassignments.  Can NOT run for various reasons.  Check
1792    * logs.
1793    * @return True if balancer ran, false otherwise.
1794    */
1795   public boolean balancer()
1796   throws MasterNotRunningException, ZooKeeperConnectionException, ServiceException {
1797     MasterKeepAliveConnection stub = connection.getKeepAliveMasterService();
1798     try {
1799       return stub.balance(null,RequestConverter.buildBalanceRequest()).getBalancerRan();
1800     } finally {
1801       stub.close();
1802     }
1803   }
1804 
1805   /**
1806    * Enable/Disable the catalog janitor
1807    * @param enable if true enables the catalog janitor
1808    * @return the previous state
1809    * @throws ServiceException
1810    * @throws MasterNotRunningException
1811    */
1812   public boolean enableCatalogJanitor(boolean enable)
1813       throws ServiceException, MasterNotRunningException {
1814     MasterKeepAliveConnection stub = connection.getKeepAliveMasterService();
1815     try {
1816       return stub.enableCatalogJanitor(null,
1817           RequestConverter.buildEnableCatalogJanitorRequest(enable)).getPrevValue();
1818     } finally {
1819       stub.close();
1820     }
1821   }
1822 
1823   /**
1824    * Ask for a scan of the catalog table
1825    * @return the number of entries cleaned
1826    * @throws ServiceException
1827    * @throws MasterNotRunningException
1828    */
1829   public int runCatalogScan() throws ServiceException, MasterNotRunningException {
1830     MasterKeepAliveConnection stub = connection.getKeepAliveMasterService();
1831     try {
1832       return stub.runCatalogScan(null,
1833           RequestConverter.buildCatalogScanRequest()).getScanResult();
1834     } finally {
1835       stub.close();
1836     }
1837   }
1838 
1839   /**
1840    * Query on the catalog janitor state (Enabled/Disabled?)
1841    * @throws ServiceException
1842    * @throws org.apache.hadoop.hbase.MasterNotRunningException
1843    */
1844   public boolean isCatalogJanitorEnabled() throws ServiceException, MasterNotRunningException {
1845     MasterKeepAliveConnection stub = connection.getKeepAliveMasterService();
1846     try {
1847       return stub.isCatalogJanitorEnabled(null,
1848           RequestConverter.buildIsCatalogJanitorEnabledRequest()).getValue();
1849     } finally {
1850       stub.close();
1851     }
1852   }
1853 
1854   /**
1855    * Merge two regions. Asynchronous operation.
1856    * @param encodedNameOfRegionA encoded name of region a
1857    * @param encodedNameOfRegionB encoded name of region b
1858    * @param forcible true if do a compulsory merge, otherwise we will only merge
1859    *          two adjacent regions
1860    * @throws IOException
1861    */
1862   public void mergeRegions(final byte[] encodedNameOfRegionA,
1863       final byte[] encodedNameOfRegionB, final boolean forcible)
1864       throws IOException {
1865     MasterKeepAliveConnection master = connection
1866         .getKeepAliveMasterService();
1867     try {
1868       DispatchMergingRegionsRequest request = RequestConverter
1869           .buildDispatchMergingRegionsRequest(encodedNameOfRegionA,
1870               encodedNameOfRegionB, forcible);
1871       master.dispatchMergingRegions(null, request);
1872     } catch (ServiceException se) {
1873       IOException ioe = ProtobufUtil.getRemoteException(se);
1874       if (ioe instanceof UnknownRegionException) {
1875         throw (UnknownRegionException) ioe;
1876       }
1877       if (ioe instanceof MergeRegionException) {
1878         throw (MergeRegionException) ioe;
1879       }
1880       LOG.error("Unexpected exception: " + se
1881           + " from calling HMaster.dispatchMergingRegions");
1882     } catch (DeserializationException de) {
1883       LOG.error("Could not parse destination server name: " + de);
1884     } finally {
1885       master.close();
1886     }
1887   }
1888 
1889   /**
1890    * Split a table or an individual region.
1891    * Asynchronous operation.
1892    *
1893    * @param tableNameOrRegionName table or region to split
1894    * @throws IOException if a remote or network exception occurs
1895    * @throws InterruptedException
1896    */
1897   public void split(final String tableNameOrRegionName)
1898   throws IOException, InterruptedException {
1899     split(Bytes.toBytes(tableNameOrRegionName));
1900   }
1901 
1902   /**
1903    * Split a table or an individual region.  Implicitly finds an optimal split
1904    * point.  Asynchronous operation.
1905    *
1906    * @param tableNameOrRegionName table to region to split
1907    * @throws IOException if a remote or network exception occurs
1908    * @throws InterruptedException
1909    */
1910   public void split(final byte[] tableNameOrRegionName)
1911   throws IOException, InterruptedException {
1912     split(tableNameOrRegionName, null);
1913   }
1914 
1915   public void split(final String tableNameOrRegionName,
1916     final String splitPoint) throws IOException, InterruptedException {
1917     split(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(splitPoint));
1918   }
1919 
1920   /**
1921    * Split a table or an individual region.
1922    * Asynchronous operation.
1923    *
1924    * @param tableNameOrRegionName table to region to split
1925    * @param splitPoint the explicit position to split on
1926    * @throws IOException if a remote or network exception occurs
1927    * @throws InterruptedException interrupt exception occurred
1928    */
1929   public void split(final byte[] tableNameOrRegionName,
1930       final byte [] splitPoint) throws IOException, InterruptedException {
1931     CatalogTracker ct = getCatalogTracker();
1932     try {
1933       Pair<HRegionInfo, ServerName> regionServerPair
1934         = getRegion(tableNameOrRegionName, ct);
1935       if (regionServerPair != null) {
1936         if (regionServerPair.getSecond() == null) {
1937             throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
1938         } else {
1939           split(regionServerPair.getSecond(), regionServerPair.getFirst(), splitPoint);
1940         }
1941       } else {
1942         final TableName tableName =
1943             checkTableExists(TableName.valueOf(tableNameOrRegionName), ct);
1944         List<Pair<HRegionInfo, ServerName>> pairs =
1945           MetaReader.getTableRegionsAndLocations(ct,
1946               tableName);
1947         for (Pair<HRegionInfo, ServerName> pair: pairs) {
1948           // May not be a server for a particular row
1949           if (pair.getSecond() == null) continue;
1950           HRegionInfo r = pair.getFirst();
1951           // check for parents
1952           if (r.isSplitParent()) continue;
1953           // if a split point given, only split that particular region
1954           if (splitPoint != null && !r.containsRow(splitPoint)) continue;
1955           // call out to region server to do split now
1956           split(pair.getSecond(), pair.getFirst(), splitPoint);
1957         }
1958       }
1959     } finally {
1960       cleanupCatalogTracker(ct);
1961     }
1962   }
1963 
1964   private void split(final ServerName sn, final HRegionInfo hri,
1965       byte[] splitPoint) throws IOException {
1966     if (hri.getStartKey() != null && splitPoint != null &&
1967          Bytes.compareTo(hri.getStartKey(), splitPoint) == 0) {
1968        throw new IOException("should not give a splitkey which equals to startkey!");
1969     }
1970     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1971     ProtobufUtil.split(admin, hri, splitPoint);
1972   }
1973 
1974   /**
1975    * Modify an existing table, more IRB friendly version.
1976    * Asynchronous operation.  This means that it may be a while before your
1977    * schema change is updated across all of the table.
1978    *
1979    * @param tableName name of table.
1980    * @param htd modified description of the table
1981    * @throws IOException if a remote or network exception occurs
1982    */
1983   public void modifyTable(final TableName tableName, final HTableDescriptor htd)
1984   throws IOException {
1985     if (!tableName.equals(htd.getTableName())) {
1986       throw new IllegalArgumentException("the specified table name '" + tableName +
1987         "' doesn't match with the HTD one: " + htd.getTableName());
1988     }
1989 
1990     executeCallable(new MasterCallable<Void>(getConnection()) {
1991       @Override
1992       public Void call() throws ServiceException {
1993         ModifyTableRequest request = RequestConverter.buildModifyTableRequest(tableName, htd);
1994         master.modifyTable(null, request);
1995         return null;
1996       }
1997     });
1998   }
1999 
2000   public void modifyTable(final byte[] tableName, final HTableDescriptor htd)
2001   throws IOException {
2002     modifyTable(TableName.valueOf(tableName), htd);
2003   }
2004 
2005   public void modifyTable(final String tableName, final HTableDescriptor htd)
2006   throws IOException {
2007     modifyTable(TableName.valueOf(tableName), htd);
2008   }
2009 
2010   /**
2011    * @param tableNameOrRegionName Name of a table or name of a region.
2012    * @param ct A {@link CatalogTracker} instance (caller of this method usually has one).
2013    * @return a pair of HRegionInfo and ServerName if <code>tableNameOrRegionName</code> is
2014    *  a verified region name (we call {@link  MetaReader#getRegion( CatalogTracker, byte[])}
2015    *  else null.
2016    * Throw an exception if <code>tableNameOrRegionName</code> is null.
2017    * @throws IOException
2018    */
2019   Pair<HRegionInfo, ServerName> getRegion(final byte[] tableNameOrRegionName,
2020       final CatalogTracker ct) throws IOException {
2021     if (tableNameOrRegionName == null) {
2022       throw new IllegalArgumentException("Pass a table name or region name");
2023     }
2024     Pair<HRegionInfo, ServerName> pair = MetaReader.getRegion(ct, tableNameOrRegionName);
2025     if (pair == null) {
2026       final AtomicReference<Pair<HRegionInfo, ServerName>> result =
2027         new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
2028       final String encodedName = Bytes.toString(tableNameOrRegionName);
2029       MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
2030         @Override
2031         public boolean processRow(Result data) throws IOException {
2032           HRegionInfo info = HRegionInfo.getHRegionInfo(data);
2033           if (info == null) {
2034             LOG.warn("No serialized HRegionInfo in " + data);
2035             return true;
2036           }
2037           if (!encodedName.equals(info.getEncodedName())) return true;
2038           ServerName sn = HRegionInfo.getServerName(data);
2039           result.set(new Pair<HRegionInfo, ServerName>(info, sn));
2040           return false; // found the region, stop
2041         }
2042       };
2043 
2044       MetaScanner.metaScan(conf, connection, visitor, null);
2045       pair = result.get();
2046     }
2047     return pair;
2048   }
2049 
2050   /**
2051    * If the input is a region name, it is returned as is. If it's an
2052    * encoded region name, the corresponding region is found from meta
2053    * and its region name is returned. If we can't find any region in
2054    * meta matching the input as either region name or encoded region
2055    * name, the input is returned as is. We don't throw unknown
2056    * region exception.
2057    */
2058   private byte[] getRegionName(
2059       final byte[] regionNameOrEncodedRegionName) throws IOException {
2060     if (Bytes.equals(regionNameOrEncodedRegionName,
2061         HRegionInfo.FIRST_META_REGIONINFO.getRegionName())
2062           || Bytes.equals(regionNameOrEncodedRegionName,
2063             HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes())) {
2064       return HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
2065     }
2066     CatalogTracker ct = getCatalogTracker();
2067     byte[] tmp = regionNameOrEncodedRegionName;
2068     try {
2069       Pair<HRegionInfo, ServerName> regionServerPair
2070         = getRegion(regionNameOrEncodedRegionName, ct);
2071       if (regionServerPair != null && regionServerPair.getFirst() != null) {
2072         tmp = regionServerPair.getFirst().getRegionName();
2073       }
2074     } finally {
2075       cleanupCatalogTracker(ct);
2076     }
2077     return tmp;
2078   }
2079 
2080   /**
2081    * Check if table exists or not
2082    * @param tableName Name of a table.
2083    * @param ct A {@link CatalogTracker} instance (caller of this method usually has one).
2084    * @return tableName instance
2085    * @throws IOException if a remote or network exception occurs.
2086    * @throws TableNotFoundException if table does not exist.
2087    */
2088   //TODO rename this method
2089   private TableName checkTableExists(
2090       final TableName tableName, CatalogTracker ct)
2091       throws IOException {
2092     if (!MetaReader.tableExists(ct, tableName)) {
2093       throw new TableNotFoundException(tableName);
2094     }
2095     return tableName;
2096   }
2097 
2098   /**
2099    * Shuts down the HBase cluster
2100    * @throws IOException if a remote or network exception occurs
2101    */
2102   public synchronized void shutdown() throws IOException {
2103     executeCallable(new MasterCallable<Void>(getConnection()) {
2104       @Override
2105       public Void call() throws ServiceException {
2106         master.shutdown(null,ShutdownRequest.newBuilder().build());
2107         return null;
2108       }
2109     });
2110   }
2111 
2112   /**
2113    * Shuts down the current HBase master only.
2114    * Does not shutdown the cluster.
2115    * @see #shutdown()
2116    * @throws IOException if a remote or network exception occurs
2117    */
2118   public synchronized void stopMaster() throws IOException {
2119     executeCallable(new MasterCallable<Void>(getConnection()) {
2120       @Override
2121       public Void call() throws ServiceException {
2122         master.stopMaster(null,StopMasterRequest.newBuilder().build());
2123         return null;
2124       }
2125     });
2126   }
2127 
2128   /**
2129    * Stop the designated regionserver
2130    * @param hostnamePort Hostname and port delimited by a <code>:</code> as in
2131    * <code>example.org:1234</code>
2132    * @throws IOException if a remote or network exception occurs
2133    */
2134   public synchronized void stopRegionServer(final String hostnamePort)
2135   throws IOException {
2136     String hostname = Addressing.parseHostname(hostnamePort);
2137     int port = Addressing.parsePort(hostnamePort);
2138     AdminService.BlockingInterface admin =
2139       this.connection.getAdmin(ServerName.valueOf(hostname, port, 0));
2140     StopServerRequest request = RequestConverter.buildStopServerRequest(
2141       "Called by admin client " + this.connection.toString());
2142     try {
2143       admin.stopServer(null, request);
2144     } catch (ServiceException se) {
2145       throw ProtobufUtil.getRemoteException(se);
2146     }
2147   }
2148 
2149 
2150   /**
2151    * @return cluster status
2152    * @throws IOException if a remote or network exception occurs
2153    */
2154   public ClusterStatus getClusterStatus() throws IOException {
2155     return executeCallable(new MasterCallable<ClusterStatus>(getConnection()) {
2156       @Override
2157       public ClusterStatus call() throws ServiceException {
2158         GetClusterStatusRequest req = RequestConverter.buildGetClusterStatusRequest();
2159         return ClusterStatus.convert(master.getClusterStatus(null,req).getClusterStatus());
2160       }
2161     });
2162   }
2163 
2164   private HRegionLocation getFirstMetaServerForTable(final TableName tableName)
2165   throws IOException {
2166     return connection.locateRegion(TableName.META_TABLE_NAME,
2167       HRegionInfo.createRegionName(tableName, null, HConstants.NINES, false));
2168   }
2169 
2170   /**
2171    * @return Configuration used by the instance.
2172    */
2173   public Configuration getConfiguration() {
2174     return this.conf;
2175   }
2176 
2177   /**
2178    * Create a new namespace
2179    * @param descriptor descriptor which describes the new namespace
2180    * @throws IOException
2181    */
2182   public void createNamespace(final NamespaceDescriptor descriptor) throws IOException {
2183     executeCallable(new MasterCallable<Void>(getConnection()) {
2184       @Override
2185       public Void call() throws Exception {
2186         master.createNamespace(null,
2187           CreateNamespaceRequest.newBuilder()
2188                 .setNamespaceDescriptor(ProtobufUtil
2189                     .toProtoNamespaceDescriptor(descriptor)).build());
2190         return null;
2191       }
2192     });
2193   }
2194 
2195   /**
2196    * Modify an existing namespace
2197    * @param descriptor descriptor which describes the new namespace
2198    * @throws IOException
2199    */
2200   public void modifyNamespace(final NamespaceDescriptor descriptor) throws IOException {
2201     executeCallable(new MasterCallable<Void>(getConnection()) {
2202       @Override
2203       public Void call() throws Exception {
2204         master.modifyNamespace(null, ModifyNamespaceRequest.newBuilder().
2205           setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(descriptor)).build());
2206         return null;
2207       }
2208     });
2209   }
2210 
2211   /**
2212    * Delete an existing namespace. Only empty namespaces (no tables) can be removed.
2213    * @param name namespace name
2214    * @throws IOException
2215    */
2216   public void deleteNamespace(final String name) throws IOException {
2217     executeCallable(new MasterCallable<Void>(getConnection()) {
2218       @Override
2219       public Void call() throws Exception {
2220         master.deleteNamespace(null, DeleteNamespaceRequest.newBuilder().
2221           setNamespaceName(name).build());
2222         return null;
2223       }
2224     });
2225   }
2226 
2227   /**
2228    * Get a namespace descriptor by name
2229    * @param name name of namespace descriptor
2230    * @return A descriptor
2231    * @throws IOException
2232    */
2233   public NamespaceDescriptor getNamespaceDescriptor(final String name) throws IOException {
2234     return
2235         executeCallable(new MasterCallable<NamespaceDescriptor>(getConnection()) {
2236           @Override
2237           public NamespaceDescriptor call() throws Exception {
2238             return ProtobufUtil.toNamespaceDescriptor(
2239               master.getNamespaceDescriptor(null, GetNamespaceDescriptorRequest.newBuilder().
2240                 setNamespaceName(name).build()).getNamespaceDescriptor());
2241           }
2242         });
2243   }
2244 
2245   /**
2246    * List available namespace descriptors
2247    * @return List of descriptors
2248    * @throws IOException
2249    */
2250   public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException {
2251     return
2252         executeCallable(new MasterCallable<NamespaceDescriptor[]>(getConnection()) {
2253           @Override
2254           public NamespaceDescriptor[] call() throws Exception {
2255             List<HBaseProtos.NamespaceDescriptor> list =
2256               master.listNamespaceDescriptors(null, ListNamespaceDescriptorsRequest.newBuilder().
2257                 build()).getNamespaceDescriptorList();
2258             NamespaceDescriptor[] res = new NamespaceDescriptor[list.size()];
2259             for(int i = 0; i < list.size(); i++) {
2260               res[i] = ProtobufUtil.toNamespaceDescriptor(list.get(i));
2261             }
2262             return res;
2263           }
2264         });
2265   }
2266 
2267   /**
2268    * Get list of table descriptors by namespace
2269    * @param name namespace name
2270    * @return A descriptor
2271    * @throws IOException
2272    */
2273   public HTableDescriptor[] listTableDescriptorsByNamespace(final String name) throws IOException {
2274     return
2275         executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
2276           @Override
2277           public HTableDescriptor[] call() throws Exception {
2278             List<TableSchema> list =
2279               master.listTableDescriptorsByNamespace(null, ListTableDescriptorsByNamespaceRequest.
2280                 newBuilder().setNamespaceName(name).build()).getTableSchemaList();
2281             HTableDescriptor[] res = new HTableDescriptor[list.size()];
2282             for(int i=0; i < list.size(); i++) {
2283 
2284               res[i] = HTableDescriptor.convert(list.get(i));
2285             }
2286             return res;
2287           }
2288         });
2289   }
2290 
2291   /**
2292    * Get list of table names by namespace
2293    * @param name namespace name
2294    * @return The list of table names in the namespace
2295    * @throws IOException
2296    */
2297   public TableName[] listTableNamesByNamespace(final String name) throws IOException {
2298     return
2299         executeCallable(new MasterCallable<TableName[]>(getConnection()) {
2300           @Override
2301           public TableName[] call() throws Exception {
2302             List<HBaseProtos.TableName> tableNames =
2303               master.listTableNamesByNamespace(null, ListTableNamesByNamespaceRequest.
2304                 newBuilder().setNamespaceName(name).build())
2305                 .getTableNameList();
2306             TableName[] result = new TableName[tableNames.size()];
2307             for (int i = 0; i < tableNames.size(); i++) {
2308               result[i] = ProtobufUtil.toTableName(tableNames.get(i));
2309             }
2310             return result;
2311           }
2312         });
2313   }
2314 
2315   /**
2316    * Check to see if HBase is running. Throw an exception if not.
2317    * We consider that HBase is running if ZooKeeper and Master are running.
2318    *
2319    * @param conf system configuration
2320    * @throws MasterNotRunningException if the master is not running
2321    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
2322    */
2323   public static void checkHBaseAvailable(Configuration conf)
2324     throws MasterNotRunningException, ZooKeeperConnectionException, ServiceException, IOException {
2325     Configuration copyOfConf = HBaseConfiguration.create(conf);
2326 
2327     // We set it to make it fail as soon as possible if HBase is not available
2328     copyOfConf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
2329     copyOfConf.setInt("zookeeper.recovery.retry", 0);
2330 
2331     HConnectionManager.HConnectionImplementation connection
2332       = (HConnectionManager.HConnectionImplementation)
2333       HConnectionManager.getConnection(copyOfConf);
2334 
2335     try {
2336       // Check ZK first.
2337       // If the connection exists, we may have a connection to ZK that does
2338       //  not work anymore
2339       ZooKeeperKeepAliveConnection zkw = null;
2340       try {
2341         zkw = connection.getKeepAliveZooKeeperWatcher();
2342         zkw.getRecoverableZooKeeper().getZooKeeper().exists(
2343           zkw.baseZNode, false);
2344 
2345       } catch (IOException e) {
2346         throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
2347       } catch (InterruptedException e) {
2348         Thread.currentThread().interrupt();
2349         throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
2350       } catch (KeeperException e) {
2351         throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
2352       } finally {
2353         if (zkw != null) {
2354           zkw.close();
2355         }
2356       }
2357 
2358       // Check Master
2359       connection.isMasterRunning();
2360 
2361     } finally {
2362       connection.close();
2363     }
2364   }
2365 
2366   /**
2367    * get the regions of a given table.
2368    *
2369    * @param tableName the name of the table
2370    * @return Ordered list of {@link HRegionInfo}.
2371    * @throws IOException
2372    */
2373   public List<HRegionInfo> getTableRegions(final TableName tableName)
2374   throws IOException {
2375     CatalogTracker ct = getCatalogTracker();
2376     List<HRegionInfo> Regions = null;
2377     try {
2378       Regions = MetaReader.getTableRegions(ct, tableName, true);
2379     } finally {
2380       cleanupCatalogTracker(ct);
2381     }
2382     return Regions;
2383   }
2384 
2385   public List<HRegionInfo> getTableRegions(final byte[] tableName)
2386   throws IOException {
2387     return getTableRegions(TableName.valueOf(tableName));
2388   }
2389 
2390   @Override
2391   public synchronized void close() throws IOException {
2392     if (cleanupConnectionOnClose && this.connection != null && !this.closed) {
2393       this.connection.close();
2394       this.closed = true;
2395     }
2396   }
2397 
2398   /**
2399    * Get tableDescriptors
2400    * @param tableNames List of table names
2401    * @return HTD[] the tableDescriptor
2402    * @throws IOException if a remote or network exception occurs
2403    */
2404   public HTableDescriptor[] getTableDescriptorsByTableName(List<TableName> tableNames)
2405   throws IOException {
2406     return this.connection.getHTableDescriptorsByTableName(tableNames);
2407   }
2408 
2409   /**
2410    * Get tableDescriptors
2411    * @param names List of table names
2412    * @return HTD[] the tableDescriptor
2413    * @throws IOException if a remote or network exception occurs
2414    */
2415   public HTableDescriptor[] getTableDescriptors(List<String> names)
2416   throws IOException {
2417     List<TableName> tableNames = new ArrayList<TableName>(names.size());
2418     for(String name : names) {
2419       tableNames.add(TableName.valueOf(name));
2420     }
2421     return getTableDescriptorsByTableName(tableNames);
2422   }
2423 
2424   /**
2425    * Roll the log writer. That is, start writing log messages to a new file.
2426    *
2427    * @param serverName
2428    *          The servername of the regionserver. A server name is made of host,
2429    *          port and startcode. This is mandatory. Here is an example:
2430    *          <code> host187.example.com,60020,1289493121758</code>
2431    * @return If lots of logs, flush the returned regions so next time through
2432    * we can clean logs. Returns null if nothing to flush.  Names are actual
2433    * region names as returned by {@link HRegionInfo#getEncodedName()}
2434    * @throws IOException if a remote or network exception occurs
2435    * @throws FailedLogCloseException
2436    */
2437  public synchronized  byte[][] rollHLogWriter(String serverName)
2438       throws IOException, FailedLogCloseException {
2439     ServerName sn = ServerName.valueOf(serverName);
2440     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
2441     RollWALWriterRequest request = RequestConverter.buildRollWALWriterRequest();
2442     try {
2443       RollWALWriterResponse response = admin.rollWALWriter(null, request);
2444       int regionCount = response.getRegionToFlushCount();
2445       byte[][] regionsToFlush = new byte[regionCount][];
2446       for (int i = 0; i < regionCount; i++) {
2447         ByteString region = response.getRegionToFlush(i);
2448         regionsToFlush[i] = region.toByteArray();
2449       }
2450       return regionsToFlush;
2451     } catch (ServiceException se) {
2452       throw ProtobufUtil.getRemoteException(se);
2453     }
2454   }
2455 
2456   public String[] getMasterCoprocessors() {
2457     try {
2458       return getClusterStatus().getMasterCoprocessors();
2459     } catch (IOException e) {
2460       LOG.error("Could not getClusterStatus()",e);
2461       return null;
2462     }
2463   }
2464 
2465   /**
2466    * Get the current compaction state of a table or region.
2467    * It could be in a major compaction, a minor compaction, both, or none.
2468    *
2469    * @param tableNameOrRegionName table or region to major compact
2470    * @throws IOException if a remote or network exception occurs
2471    * @throws InterruptedException
2472    * @return the current compaction state
2473    */
2474   public CompactionState getCompactionState(final String tableNameOrRegionName)
2475       throws IOException, InterruptedException {
2476     return getCompactionState(Bytes.toBytes(tableNameOrRegionName));
2477   }
2478 
2479   /**
2480    * Get the current compaction state of a table or region.
2481    * It could be in a major compaction, a minor compaction, both, or none.
2482    *
2483    * @param tableNameOrRegionName table or region to major compact
2484    * @throws IOException if a remote or network exception occurs
2485    * @throws InterruptedException
2486    * @return the current compaction state
2487    */
2488   public CompactionState getCompactionState(final byte[] tableNameOrRegionName)
2489       throws IOException, InterruptedException {
2490     CompactionState state = CompactionState.NONE;
2491     CatalogTracker ct = getCatalogTracker();
2492     try {
2493       Pair<HRegionInfo, ServerName> regionServerPair
2494         = getRegion(tableNameOrRegionName, ct);
2495       if (regionServerPair != null) {
2496         if (regionServerPair.getSecond() == null) {
2497           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));
2498         } else {
2499           ServerName sn = regionServerPair.getSecond();
2500           AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
2501           GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest(
2502             regionServerPair.getFirst().getRegionName(), true);
2503           GetRegionInfoResponse response = admin.getRegionInfo(null, request);
2504           return response.getCompactionState();
2505         }
2506       } else {
2507         final TableName tableName =
2508             checkTableExists(TableName.valueOf(tableNameOrRegionName), ct);
2509         List<Pair<HRegionInfo, ServerName>> pairs =
2510           MetaReader.getTableRegionsAndLocations(ct, tableName);
2511         for (Pair<HRegionInfo, ServerName> pair: pairs) {
2512           if (pair.getFirst().isOffline()) continue;
2513           if (pair.getSecond() == null) continue;
2514           try {
2515             ServerName sn = pair.getSecond();
2516             AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
2517             GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest(
2518               pair.getFirst().getRegionName(), true);
2519             GetRegionInfoResponse response = admin.getRegionInfo(null, request);
2520             switch (response.getCompactionState()) {
2521             case MAJOR_AND_MINOR:
2522               return CompactionState.MAJOR_AND_MINOR;
2523             case MAJOR:
2524               if (state == CompactionState.MINOR) {
2525                 return CompactionState.MAJOR_AND_MINOR;
2526               }
2527               state = CompactionState.MAJOR;
2528               break;
2529             case MINOR:
2530               if (state == CompactionState.MAJOR) {
2531                 return CompactionState.MAJOR_AND_MINOR;
2532               }
2533               state = CompactionState.MINOR;
2534               break;
2535             case NONE:
2536               default: // nothing, continue
2537             }
2538           } catch (NotServingRegionException e) {
2539             if (LOG.isDebugEnabled()) {
2540               LOG.debug("Trying to get compaction state of " +
2541                 pair.getFirst() + ": " +
2542                 StringUtils.stringifyException(e));
2543             }
2544           } catch (RemoteException e) {
2545             if (e.getMessage().indexOf(NotServingRegionException.class.getName()) >= 0) {
2546               if (LOG.isDebugEnabled()) {
2547                 LOG.debug("Trying to get compaction state of " + pair.getFirst() + ": "
2548                     + StringUtils.stringifyException(e));
2549               }
2550             } else {
2551               throw e;
2552             }
2553           }
2554         }
2555       }
2556     } catch (ServiceException se) {
2557       throw ProtobufUtil.getRemoteException(se);
2558     } finally {
2559       cleanupCatalogTracker(ct);
2560     }
2561     return state;
2562   }
2563 
2564   /**
2565    * Take a snapshot for the given table. If the table is enabled, a FLUSH-type snapshot will be
2566    * taken. If the table is disabled, an offline snapshot is taken.
2567    * <p>
2568    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
2569    * snapshot with the same name (even a different type or with different parameters) will fail with
2570    * a {@link SnapshotCreationException} indicating the duplicate naming.
2571    * <p>
2572    * Snapshot names follow the same naming constraints as tables in HBase. See
2573    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
2574    * @param snapshotName name of the snapshot to be created
2575    * @param tableName name of the table for which snapshot is created
2576    * @throws IOException if a remote or network exception occurs
2577    * @throws SnapshotCreationException if snapshot creation failed
2578    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2579    */
2580   public void snapshot(final String snapshotName,
2581                        final TableName tableName) throws IOException,
2582       SnapshotCreationException, IllegalArgumentException {
2583     snapshot(snapshotName, tableName, SnapshotDescription.Type.FLUSH);
2584   }
2585 
2586   public void snapshot(final String snapshotName,
2587                        final String tableName) throws IOException,
2588       SnapshotCreationException, IllegalArgumentException {
2589     snapshot(snapshotName, TableName.valueOf(tableName),
2590         SnapshotDescription.Type.FLUSH);
2591   }
2592 
2593   /**
2594    public void snapshot(final String snapshotName,
2595     * Create a timestamp consistent snapshot for the given table.
2596                         final byte[] tableName) throws IOException,
2597     * <p>
2598     * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
2599     * snapshot with the same name (even a different type or with different parameters) will fail with
2600     * a {@link SnapshotCreationException} indicating the duplicate naming.
2601     * <p>
2602     * Snapshot names follow the same naming constraints as tables in HBase.
2603     * @param snapshotName name of the snapshot to be created
2604     * @param tableName name of the table for which snapshot is created
2605     * @throws IOException if a remote or network exception occurs
2606     * @throws SnapshotCreationException if snapshot creation failed
2607     * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2608     */
2609   public void snapshot(final byte[] snapshotName,
2610                        final TableName tableName) throws IOException,
2611       SnapshotCreationException, IllegalArgumentException {
2612     snapshot(Bytes.toString(snapshotName), tableName, SnapshotDescription.Type.FLUSH);
2613   }
2614 
2615   public void snapshot(final byte[] snapshotName,
2616                        final byte[] tableName) throws IOException,
2617       SnapshotCreationException, IllegalArgumentException {
2618     snapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName),
2619         SnapshotDescription.Type.FLUSH);
2620   }
2621 
2622   /**
2623    * Create typed snapshot of the table.
2624    * <p>
2625    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
2626    * snapshot with the same name (even a different type or with different parameters) will fail with
2627    * a {@link SnapshotCreationException} indicating the duplicate naming.
2628    * <p>
2629    * Snapshot names follow the same naming constraints as tables in HBase. See
2630    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
2631    * <p>
2632    * @param snapshotName name to give the snapshot on the filesystem. Must be unique from all other
2633    *          snapshots stored on the cluster
2634    * @param tableName name of the table to snapshot
2635    * @param type type of snapshot to take
2636    * @throws IOException we fail to reach the master
2637    * @throws SnapshotCreationException if snapshot creation failed
2638    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2639    */
2640   public void snapshot(final String snapshotName,
2641                        final TableName tableName,
2642                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
2643       IllegalArgumentException {
2644     SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
2645     builder.setTable(tableName.getNameAsString());
2646     builder.setName(snapshotName);
2647     builder.setType(type);
2648     snapshot(builder.build());
2649   }
2650 
2651   public void snapshot(final String snapshotName,
2652                        final String tableName,
2653                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
2654       IllegalArgumentException {
2655     snapshot(snapshotName, TableName.valueOf(tableName), type);
2656   }
2657 
2658   public void snapshot(final String snapshotName,
2659                        final byte[] tableName,
2660                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
2661       IllegalArgumentException {
2662     snapshot(snapshotName, TableName.valueOf(tableName), type);
2663   }
2664 
2665   /**
2666    * Take a snapshot and wait for the server to complete that snapshot (blocking).
2667    * <p>
2668    * Only a single snapshot should be taken at a time for an instance of HBase, or results may be
2669    * undefined (you can tell multiple HBase clusters to snapshot at the same time, but only one at a
2670    * time for a single cluster).
2671    * <p>
2672    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
2673    * snapshot with the same name (even a different type or with different parameters) will fail with
2674    * a {@link SnapshotCreationException} indicating the duplicate naming.
2675    * <p>
2676    * Snapshot names follow the same naming constraints as tables in HBase. See
2677    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
2678    * <p>
2679    * You should probably use {@link #snapshot(String, String)} or {@link #snapshot(byte[], byte[])}
2680    * unless you are sure about the type of snapshot that you want to take.
2681    * @param snapshot snapshot to take
2682    * @throws IOException or we lose contact with the master.
2683    * @throws SnapshotCreationException if snapshot failed to be taken
2684    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2685    */
2686   public void snapshot(SnapshotDescription snapshot) throws IOException, SnapshotCreationException,
2687       IllegalArgumentException {
2688     // actually take the snapshot
2689     SnapshotResponse response = takeSnapshotAsync(snapshot);
2690     final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot)
2691         .build();
2692     IsSnapshotDoneResponse done = null;
2693     long start = EnvironmentEdgeManager.currentTimeMillis();
2694     long max = response.getExpectedTimeout();
2695     long maxPauseTime = max / this.numRetries;
2696     int tries = 0;
2697     LOG.debug("Waiting a max of " + max + " ms for snapshot '" +
2698         ClientSnapshotDescriptionUtils.toString(snapshot) + "'' to complete. (max " +
2699         maxPauseTime + " ms per retry)");
2700     while (tries == 0
2701         || ((EnvironmentEdgeManager.currentTimeMillis() - start) < max && !done.getDone())) {
2702       try {
2703         // sleep a backoff <= pauseTime amount
2704         long sleep = getPauseTime(tries++);
2705         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
2706         LOG.debug("(#" + tries + ") Sleeping: " + sleep +
2707           "ms while waiting for snapshot completion.");
2708         Thread.sleep(sleep);
2709 
2710       } catch (InterruptedException e) {
2711         LOG.debug("Interrupted while waiting for snapshot " + snapshot + " to complete");
2712         Thread.currentThread().interrupt();
2713       }
2714       LOG.debug("Getting current status of snapshot from master...");
2715       done = executeCallable(new MasterCallable<IsSnapshotDoneResponse>(getConnection()) {
2716         @Override
2717         public IsSnapshotDoneResponse call() throws ServiceException {
2718           return master.isSnapshotDone(null, request);
2719         }
2720       });
2721     };
2722     if (!done.getDone()) {
2723       throw new SnapshotCreationException("Snapshot '" + snapshot.getName()
2724           + "' wasn't completed in expectedTime:" + max + " ms", snapshot);
2725     }
2726   }
2727 
2728   /**
2729    * Take a snapshot without waiting for the server to complete that snapshot (asynchronous)
2730    * <p>
2731    * Only a single snapshot should be taken at a time, or results may be undefined.
2732    * @param snapshot snapshot to take
2733    * @return response from the server indicating the max time to wait for the snapshot
2734    * @throws IOException if the snapshot did not succeed or we lose contact with the master.
2735    * @throws SnapshotCreationException if snapshot creation failed
2736    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
2737    */
2738   public SnapshotResponse takeSnapshotAsync(SnapshotDescription snapshot) throws IOException,
2739       SnapshotCreationException {
2740     ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
2741     final SnapshotRequest request = SnapshotRequest.newBuilder().setSnapshot(snapshot)
2742         .build();
2743     // run the snapshot on the master
2744     return executeCallable(new MasterCallable<SnapshotResponse>(getConnection()) {
2745       @Override
2746       public SnapshotResponse call() throws ServiceException {
2747         return master.snapshot(null, request);
2748       }
2749     });
2750   }
2751 
2752   /**
2753    * Check the current state of the passed snapshot.
2754    * <p>
2755    * There are three possible states:
2756    * <ol>
2757    * <li>running - returns <tt>false</tt></li>
2758    * <li>finished - returns <tt>true</tt></li>
2759    * <li>finished with error - throws the exception that caused the snapshot to fail</li>
2760    * </ol>
2761    * <p>
2762    * The cluster only knows about the most recent snapshot. Therefore, if another snapshot has been
2763    * run/started since the snapshot your are checking, you will recieve an
2764    * {@link UnknownSnapshotException}.
2765    * @param snapshot description of the snapshot to check
2766    * @return <tt>true</tt> if the snapshot is completed, <tt>false</tt> if the snapshot is still
2767    *         running
2768    * @throws IOException if we have a network issue
2769    * @throws HBaseSnapshotException if the snapshot failed
2770    * @throws UnknownSnapshotException if the requested snapshot is unknown
2771    */
2772   public boolean isSnapshotFinished(final SnapshotDescription snapshot)
2773       throws IOException, HBaseSnapshotException, UnknownSnapshotException {
2774 
2775     return executeCallable(new MasterCallable<IsSnapshotDoneResponse>(getConnection()) {
2776       @Override
2777       public IsSnapshotDoneResponse call() throws ServiceException {
2778         return master.isSnapshotDone(null,
2779           IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot).build());
2780       }
2781     }).getDone();
2782   }
2783 
2784   /**
2785    * Restore the specified snapshot on the original table. (The table must be disabled)
2786    * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property
2787    * is set to true, a snapshot of the current table is taken
2788    * before executing the restore operation.
2789    * In case of restore failure, the failsafe snapshot will be restored.
2790    * If the restore completes without problem the failsafe snapshot is deleted.
2791    *
2792    * @param snapshotName name of the snapshot to restore
2793    * @throws IOException if a remote or network exception occurs
2794    * @throws RestoreSnapshotException if snapshot failed to be restored
2795    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2796    */
2797   public void restoreSnapshot(final byte[] snapshotName)
2798       throws IOException, RestoreSnapshotException {
2799     restoreSnapshot(Bytes.toString(snapshotName));
2800   }
2801 
2802   /**
2803    * Restore the specified snapshot on the original table. (The table must be disabled)
2804    * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property
2805    * is set to true, a snapshot of the current table is taken
2806    * before executing the restore operation.
2807    * In case of restore failure, the failsafe snapshot will be restored.
2808    * If the restore completes without problem the failsafe snapshot is deleted.
2809    *
2810    * @param snapshotName name of the snapshot to restore
2811    * @throws IOException if a remote or network exception occurs
2812    * @throws RestoreSnapshotException if snapshot failed to be restored
2813    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2814    */
2815   public void restoreSnapshot(final String snapshotName)
2816       throws IOException, RestoreSnapshotException {
2817     boolean takeFailSafeSnapshot =
2818       conf.getBoolean("hbase.snapshot.restore.take.failsafe.snapshot", false);
2819     restoreSnapshot(snapshotName, takeFailSafeSnapshot);
2820   }
2821 
2822   /**
2823    * Restore the specified snapshot on the original table. (The table must be disabled)
2824    * If 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken
2825    * before executing the restore operation.
2826    * In case of restore failure, the failsafe snapshot will be restored.
2827    * If the restore completes without problem the failsafe snapshot is deleted.
2828    *
2829    * The failsafe snapshot name is configurable by using the property
2830    * "hbase.snapshot.restore.failsafe.name".
2831    *
2832    * @param snapshotName name of the snapshot to restore
2833    * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken
2834    * @throws IOException if a remote or network exception occurs
2835    * @throws RestoreSnapshotException if snapshot failed to be restored
2836    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2837    */
2838   public void restoreSnapshot(final byte[] snapshotName, final boolean takeFailSafeSnapshot)
2839       throws IOException, RestoreSnapshotException {
2840     restoreSnapshot(Bytes.toString(snapshotName), takeFailSafeSnapshot);
2841   }
2842 
2843   /**
2844    * Restore the specified snapshot on the original table. (The table must be disabled)
2845    * If 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken
2846    * before executing the restore operation.
2847    * In case of restore failure, the failsafe snapshot will be restored.
2848    * If the restore completes without problem the failsafe snapshot is deleted.
2849    *
2850    * The failsafe snapshot name is configurable by using the property
2851    * "hbase.snapshot.restore.failsafe.name".
2852    *
2853    * @param snapshotName name of the snapshot to restore
2854    * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken
2855    * @throws IOException if a remote or network exception occurs
2856    * @throws RestoreSnapshotException if snapshot failed to be restored
2857    * @throws IllegalArgumentException if the restore request is formatted incorrectly
2858    */
2859   public void restoreSnapshot(final String snapshotName, boolean takeFailSafeSnapshot)
2860       throws IOException, RestoreSnapshotException {
2861     TableName tableName = null;
2862     for (SnapshotDescription snapshotInfo: listSnapshots()) {
2863       if (snapshotInfo.getName().equals(snapshotName)) {
2864         tableName = TableName.valueOf(snapshotInfo.getTable());
2865         break;
2866       }
2867     }
2868 
2869     if (tableName == null) {
2870       throw new RestoreSnapshotException(
2871         "Unable to find the table name for snapshot=" + snapshotName);
2872     }
2873 
2874     // The table does not exists, switch to clone.
2875     if (!tableExists(tableName)) {
2876       try {
2877         cloneSnapshot(snapshotName, tableName);
2878       } catch (InterruptedException e) {
2879         throw new InterruptedIOException("Interrupted when restoring a nonexistent table: " +
2880           e.getMessage());
2881       }
2882       return;
2883     }
2884 
2885     // Check if the table is disabled
2886     if (!isTableDisabled(tableName)) {
2887       throw new TableNotDisabledException(tableName);
2888     }
2889 
2890     // Take a snapshot of the current state
2891     String failSafeSnapshotSnapshotName = null;
2892     if (takeFailSafeSnapshot) {
2893       failSafeSnapshotSnapshotName = conf.get("hbase.snapshot.restore.failsafe.name",
2894         "hbase-failsafe-{snapshot.name}-{restore.timestamp}");
2895       failSafeSnapshotSnapshotName = failSafeSnapshotSnapshotName
2896         .replace("{snapshot.name}", snapshotName)
2897         .replace("{table.name}", tableName.toString().replace(TableName.NAMESPACE_DELIM, '.'))
2898         .replace("{restore.timestamp}", String.valueOf(EnvironmentEdgeManager.currentTimeMillis()));
2899       LOG.info("Taking restore-failsafe snapshot: " + failSafeSnapshotSnapshotName);
2900       snapshot(failSafeSnapshotSnapshotName, tableName);
2901     }
2902 
2903     try {
2904       // Restore snapshot
2905       internalRestoreSnapshot(snapshotName, tableName);
2906     } catch (IOException e) {
2907       // Somthing went wrong during the restore...
2908       // if the pre-restore snapshot is available try to rollback
2909       if (takeFailSafeSnapshot) {
2910         try {
2911           internalRestoreSnapshot(failSafeSnapshotSnapshotName, tableName);
2912           String msg = "Restore snapshot=" + snapshotName +
2913             " failed. Rollback to snapshot=" + failSafeSnapshotSnapshotName + " succeeded.";
2914           LOG.error(msg, e);
2915           throw new RestoreSnapshotException(msg, e);
2916         } catch (IOException ex) {
2917           String msg = "Failed to restore and rollback to snapshot=" + failSafeSnapshotSnapshotName;
2918           LOG.error(msg, ex);
2919           throw new RestoreSnapshotException(msg, e);
2920         }
2921       } else {
2922         throw new RestoreSnapshotException("Failed to restore snapshot=" + snapshotName, e);
2923       }
2924     }
2925 
2926     // If the restore is succeeded, delete the pre-restore snapshot
2927     if (takeFailSafeSnapshot) {
2928       try {
2929         LOG.info("Deleting restore-failsafe snapshot: " + failSafeSnapshotSnapshotName);
2930         deleteSnapshot(failSafeSnapshotSnapshotName);
2931       } catch (IOException e) {
2932         LOG.error("Unable to remove the failsafe snapshot: " + failSafeSnapshotSnapshotName, e);
2933       }
2934     }
2935   }
2936 
2937   /**
2938    * Create a new table by cloning the snapshot content.
2939    *
2940    * @param snapshotName name of the snapshot to be cloned
2941    * @param tableName name of the table where the snapshot will be restored
2942    * @throws IOException if a remote or network exception occurs
2943    * @throws TableExistsException if table to be created already exists
2944    * @throws RestoreSnapshotException if snapshot failed to be cloned
2945    * @throws IllegalArgumentException if the specified table has not a valid name
2946    */
2947   public void cloneSnapshot(final byte[] snapshotName, final byte[] tableName)
2948       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
2949     cloneSnapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName));
2950   }
2951 
2952   /**
2953    * Create a new table by cloning the snapshot content.
2954    *
2955    * @param snapshotName name of the snapshot to be cloned
2956    * @param tableName name of the table where the snapshot will be restored
2957    * @throws IOException if a remote or network exception occurs
2958    * @throws TableExistsException if table to be created already exists
2959    * @throws RestoreSnapshotException if snapshot failed to be cloned
2960    * @throws IllegalArgumentException if the specified table has not a valid name
2961    */
2962   public void cloneSnapshot(final byte[] snapshotName, final TableName tableName)
2963       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
2964     cloneSnapshot(Bytes.toString(snapshotName), tableName);
2965   }
2966 
2967 
2968 
2969   /**
2970    * Create a new table by cloning the snapshot content.
2971    *
2972    * @param snapshotName name of the snapshot to be cloned
2973    * @param tableName name of the table where the snapshot will be restored
2974    * @throws IOException if a remote or network exception occurs
2975    * @throws TableExistsException if table to be created already exists
2976    * @throws RestoreSnapshotException if snapshot failed to be cloned
2977    * @throws IllegalArgumentException if the specified table has not a valid name
2978    */
2979   public void cloneSnapshot(final String snapshotName, final String tableName)
2980       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
2981     cloneSnapshot(snapshotName, TableName.valueOf(tableName));
2982   }
2983 
2984  /**
2985   * Create a new table by cloning the snapshot content.
2986   *
2987   * @param snapshotName name of the snapshot to be cloned
2988   * @param tableName name of the table where the snapshot will be restored
2989   * @throws IOException if a remote or network exception occurs
2990   * @throws TableExistsException if table to be created already exists
2991   * @throws RestoreSnapshotException if snapshot failed to be cloned
2992   * @throws IllegalArgumentException if the specified table has not a valid name
2993   */
2994   public void cloneSnapshot(final String snapshotName, final TableName tableName)
2995       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
2996     if (tableExists(tableName)) {
2997       throw new TableExistsException("Table " + tableName + " already exists");
2998     }
2999     internalRestoreSnapshot(snapshotName, tableName);
3000     waitUntilTableIsEnabled(tableName);
3001   }
3002 
3003   /**
3004    * Execute Restore/Clone snapshot and wait for the server to complete (blocking).
3005    * To check if the cloned table exists, use {@link #isTableAvailable} -- it is not safe to
3006    * create an HTable instance to this table before it is available.
3007    * @param snapshotName snapshot to restore
3008    * @param tableName table name to restore the snapshot on
3009    * @throws IOException if a remote or network exception occurs
3010    * @throws RestoreSnapshotException if snapshot failed to be restored
3011    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3012    */
3013   private void internalRestoreSnapshot(final String snapshotName, final TableName
3014       tableName)
3015       throws IOException, RestoreSnapshotException {
3016     SnapshotDescription snapshot = SnapshotDescription.newBuilder()
3017         .setName(snapshotName).setTable(tableName.getNameAsString()).build();
3018 
3019     // actually restore the snapshot
3020     internalRestoreSnapshotAsync(snapshot);
3021 
3022     final IsRestoreSnapshotDoneRequest request = IsRestoreSnapshotDoneRequest.newBuilder()
3023         .setSnapshot(snapshot).build();
3024     IsRestoreSnapshotDoneResponse done = IsRestoreSnapshotDoneResponse.newBuilder()
3025         .setDone(false).buildPartial();
3026     final long maxPauseTime = 5000;
3027     int tries = 0;
3028     while (!done.getDone()) {
3029       try {
3030         // sleep a backoff <= pauseTime amount
3031         long sleep = getPauseTime(tries++);
3032         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
3033         LOG.debug(tries + ") Sleeping: " + sleep + " ms while we wait for snapshot restore to complete.");
3034         Thread.sleep(sleep);
3035       } catch (InterruptedException e) {
3036         LOG.debug("Interrupted while waiting for snapshot " + snapshot + " restore to complete");
3037         Thread.currentThread().interrupt();
3038       }
3039       LOG.debug("Getting current status of snapshot restore from master...");
3040       done = executeCallable(new MasterCallable<IsRestoreSnapshotDoneResponse>(
3041           getConnection()) {
3042         @Override
3043         public IsRestoreSnapshotDoneResponse call() throws ServiceException {
3044           return master.isRestoreSnapshotDone(null, request);
3045         }
3046       });
3047     }
3048     if (!done.getDone()) {
3049       throw new RestoreSnapshotException("Snapshot '" + snapshot.getName() + "' wasn't restored.");
3050     }
3051   }
3052 
3053   /**
3054    * Execute Restore/Clone snapshot and wait for the server to complete (asynchronous)
3055    * <p>
3056    * Only a single snapshot should be restored at a time, or results may be undefined.
3057    * @param snapshot snapshot to restore
3058    * @return response from the server indicating the max time to wait for the snapshot
3059    * @throws IOException if a remote or network exception occurs
3060    * @throws RestoreSnapshotException if snapshot failed to be restored
3061    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3062    */
3063   private RestoreSnapshotResponse internalRestoreSnapshotAsync(final SnapshotDescription snapshot)
3064       throws IOException, RestoreSnapshotException {
3065     ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
3066 
3067     final RestoreSnapshotRequest request = RestoreSnapshotRequest.newBuilder().setSnapshot(snapshot)
3068         .build();
3069 
3070     // run the snapshot restore on the master
3071     return executeCallable(new MasterCallable<RestoreSnapshotResponse>(getConnection()) {
3072       @Override
3073       public RestoreSnapshotResponse call() throws ServiceException {
3074         return master.restoreSnapshot(null, request);
3075       }
3076     });
3077   }
3078 
3079   /**
3080    * List completed snapshots.
3081    * @return a list of snapshot descriptors for completed snapshots
3082    * @throws IOException if a network error occurs
3083    */
3084   public List<SnapshotDescription> listSnapshots() throws IOException {
3085     return executeCallable(new MasterCallable<List<SnapshotDescription>>(getConnection()) {
3086       @Override
3087       public List<SnapshotDescription> call() throws ServiceException {
3088         return master.getCompletedSnapshots(null, GetCompletedSnapshotsRequest.newBuilder().build())
3089             .getSnapshotsList();
3090       }
3091     });
3092   }
3093 
3094   /**
3095    * List all the completed snapshots matching the given regular expression.
3096    *
3097    * @param regex The regular expression to match against
3098    * @return - returns a List of SnapshotDescription
3099    * @throws IOException if a remote or network exception occurs
3100    */
3101   public List<SnapshotDescription> listSnapshots(String regex) throws IOException {
3102     return listSnapshots(Pattern.compile(regex));
3103   }
3104 
3105   /**
3106    * List all the completed snapshots matching the given pattern.
3107    *
3108    * @param pattern The compiled regular expression to match against
3109    * @return - returns a List of SnapshotDescription
3110    * @throws IOException if a remote or network exception occurs
3111    */
3112   public List<SnapshotDescription> listSnapshots(Pattern pattern) throws IOException {
3113     List<SnapshotDescription> matched = new LinkedList<SnapshotDescription>();
3114     List<SnapshotDescription> snapshots = listSnapshots();
3115     for (SnapshotDescription snapshot : snapshots) {
3116       if (pattern.matcher(snapshot.getName()).matches()) {
3117         matched.add(snapshot);
3118       }
3119     }
3120     return matched;
3121   }
3122 
3123   /**
3124    * Delete an existing snapshot.
3125    * @param snapshotName name of the snapshot
3126    * @throws IOException if a remote or network exception occurs
3127    */
3128   public void deleteSnapshot(final byte[] snapshotName) throws IOException {
3129     deleteSnapshot(Bytes.toString(snapshotName));
3130   }
3131 
3132   /**
3133    * Delete an existing snapshot.
3134    * @param snapshotName name of the snapshot
3135    * @throws IOException if a remote or network exception occurs
3136    */
3137   public void deleteSnapshot(final String snapshotName) throws IOException {
3138     // make sure the snapshot is possibly valid
3139     TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(snapshotName));
3140     // do the delete
3141     executeCallable(new MasterCallable<Void>(getConnection()) {
3142       @Override
3143       public Void call() throws ServiceException {
3144         master.deleteSnapshot(null,
3145           DeleteSnapshotRequest.newBuilder().
3146             setSnapshot(SnapshotDescription.newBuilder().setName(snapshotName).build()).build());
3147         return null;
3148       }
3149     });
3150   }
3151 
3152   /**
3153    * Delete existing snapshots whose names match the pattern passed.
3154    * @param regex The regular expression to match against
3155    * @throws IOException if a remote or network exception occurs
3156    */
3157   public void deleteSnapshots(final String regex) throws IOException {
3158     deleteSnapshots(Pattern.compile(regex));
3159   }
3160 
3161   /**
3162    * Delete existing snapshots whose names match the pattern passed.
3163    * @param pattern pattern for names of the snapshot to match
3164    * @throws IOException if a remote or network exception occurs
3165    */
3166   public void deleteSnapshots(final Pattern pattern) throws IOException {
3167     List<SnapshotDescription> snapshots = listSnapshots(pattern);
3168     for (final SnapshotDescription snapshot : snapshots) {
3169       // do the delete
3170       executeCallable(new MasterCallable<Void>(getConnection()) {
3171         @Override
3172         public Void call() throws ServiceException {
3173           this.master.deleteSnapshot(null,
3174             DeleteSnapshotRequest.newBuilder().setSnapshot(snapshot).build());
3175           return null;
3176         }
3177       });
3178     }
3179   }
3180 
3181   /**
3182    * Parent of {@link MasterCallable} and {@link MasterCallable}.
3183    * Has common methods.
3184    * @param <V>
3185    */
3186   abstract static class MasterCallable<V> implements RetryingCallable<V>, Closeable {
3187     protected HConnection connection;
3188     protected MasterKeepAliveConnection master;
3189 
3190     public MasterCallable(final HConnection connection) {
3191       this.connection = connection;
3192     }
3193 
3194     @Override
3195     public void prepare(boolean reload) throws IOException {
3196       this.master = this.connection.getKeepAliveMasterService();
3197     }
3198 
3199     @Override
3200     public void close() throws IOException {
3201       // The above prepare could fail but this would still be called though masterAdmin is null
3202       if (this.master != null) this.master.close();
3203     }
3204 
3205     @Override
3206     public void throwable(Throwable t, boolean retrying) {
3207     }
3208 
3209     @Override
3210     public String getExceptionMessageAdditionalDetail() {
3211       return "";
3212     }
3213 
3214     @Override
3215     public long sleep(long pause, int tries) {
3216       return ConnectionUtils.getPauseTime(pause, tries);
3217     }
3218   }
3219 
3220   private <V> V executeCallable(MasterCallable<V> callable) throws IOException {
3221     RpcRetryingCaller<V> caller = rpcCallerFactory.newCaller();
3222     try {
3223       return caller.callWithRetries(callable);
3224     } finally {
3225       callable.close();
3226     }
3227   }
3228 
3229   /**
3230    * Creates and returns a {@link com.google.protobuf.RpcChannel} instance
3231    * connected to the active master.
3232    *
3233    * <p>
3234    * The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published
3235    * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations:
3236    * </p>
3237    *
3238    * <div style="background-color: #cccccc; padding: 2px">
3239    * <blockquote><pre>
3240    * CoprocessorRpcChannel channel = myAdmin.coprocessorService();
3241    * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
3242    * MyCallRequest request = MyCallRequest.newBuilder()
3243    *     ...
3244    *     .build();
3245    * MyCallResponse response = service.myCall(null, request);
3246    * </pre></blockquote></div>
3247    *
3248    * @return A MasterCoprocessorRpcChannel instance
3249    */
3250   public CoprocessorRpcChannel coprocessorService() {
3251     return new MasterCoprocessorRpcChannel(connection);
3252   }
3253 }