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