View Javadoc

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