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