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