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 com.google.common.annotations.VisibleForTesting;
22  import com.google.protobuf.ByteString;
23  import com.google.protobuf.ServiceException;
24  
25  import java.io.IOException;
26  import java.io.InterruptedIOException;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.HashMap;
30  import java.util.LinkedList;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Map.Entry;
34  import java.util.concurrent.ExecutionException;
35  import java.util.concurrent.Future;
36  import java.util.concurrent.TimeUnit;
37  import java.util.concurrent.TimeoutException;
38  import java.util.concurrent.atomic.AtomicInteger;
39  import java.util.concurrent.atomic.AtomicReference;
40  import java.util.regex.Pattern;
41  
42  import org.apache.commons.logging.Log;
43  import org.apache.commons.logging.LogFactory;
44  import org.apache.hadoop.conf.Configuration;
45  import org.apache.hadoop.hbase.Abortable;
46  import org.apache.hadoop.hbase.ClusterStatus;
47  import org.apache.hadoop.hbase.DoNotRetryIOException;
48  import org.apache.hadoop.hbase.HBaseConfiguration;
49  import org.apache.hadoop.hbase.HColumnDescriptor;
50  import org.apache.hadoop.hbase.HConstants;
51  import org.apache.hadoop.hbase.HRegionInfo;
52  import org.apache.hadoop.hbase.HRegionLocation;
53  import org.apache.hadoop.hbase.HTableDescriptor;
54  import org.apache.hadoop.hbase.MasterNotRunningException;
55  import org.apache.hadoop.hbase.MetaTableAccessor;
56  import org.apache.hadoop.hbase.NamespaceDescriptor;
57  import org.apache.hadoop.hbase.NamespaceNotFoundException;
58  import org.apache.hadoop.hbase.NotServingRegionException;
59  import org.apache.hadoop.hbase.ProcedureInfo;
60  import org.apache.hadoop.hbase.RegionLocations;
61  import org.apache.hadoop.hbase.ServerName;
62  import org.apache.hadoop.hbase.TableExistsException;
63  import org.apache.hadoop.hbase.TableName;
64  import org.apache.hadoop.hbase.TableNotDisabledException;
65  import org.apache.hadoop.hbase.TableNotFoundException;
66  import org.apache.hadoop.hbase.UnknownRegionException;
67  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
68  import org.apache.hadoop.hbase.classification.InterfaceAudience;
69  import org.apache.hadoop.hbase.classification.InterfaceStability;
70  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
71  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
72  import org.apache.hadoop.hbase.client.security.SecurityCapability;
73  import org.apache.hadoop.hbase.exceptions.DeserializationException;
74  import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
75  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
76  import org.apache.hadoop.hbase.ipc.HBaseRpcController;
77  import org.apache.hadoop.hbase.ipc.MasterCoprocessorRpcChannel;
78  import org.apache.hadoop.hbase.ipc.RegionServerCoprocessorRpcChannel;
79  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
80  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
81  import org.apache.hadoop.hbase.protobuf.RequestConverter;
82  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
83  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionRequest;
84  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionResponse;
85  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CompactRegionRequest;
86  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.FlushRegionRequest;
87  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoRequest;
88  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse;
89  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
90  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterRequest;
91  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterResponse;
92  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.StopServerRequest;
93  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.UpdateConfigurationRequest;
94  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
95  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
96  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ProcedureDescription;
97  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
98  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
99  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableSchema;
100 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
101 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AbortProcedureRequest;
102 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AbortProcedureResponse;
103 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AddColumnRequest;
104 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AssignRegionRequest;
105 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ClearDeadServersRequest;
106 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateNamespaceRequest;
107 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableRequest;
108 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableResponse;
109 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteColumnRequest;
110 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteNamespaceRequest;
111 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteSnapshotRequest;
112 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableRequest;
113 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableResponse;
114 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableRequest;
115 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableResponse;
116 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DispatchMergingRegionsRequest;
117 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableRequest;
118 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableResponse;
119 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ExecProcedureRequest;
120 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ExecProcedureResponse;
121 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterStatusRequest;
122 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest;
123 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetNamespaceDescriptorRequest;
124 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultRequest;
125 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultResponse;
126 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusRequest;
127 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusResponse;
128 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest;
129 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
130 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableNamesRequest;
131 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsInMaintenanceModeRequest;
132 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsInMaintenanceModeResponse;
133 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsProcedureDoneRequest;
134 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsProcedureDoneResponse;
135 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneRequest;
136 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneResponse;
137 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
138 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
139 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListNamespaceDescriptorsRequest;
140 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListProceduresRequest;
141 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableDescriptorsByNamespaceRequest;
142 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableNamesByNamespaceRequest;
143 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MajorCompactionTimestampForRegionRequest;
144 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MajorCompactionTimestampRequest;
145 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyColumnRequest;
146 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyNamespaceRequest;
147 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyTableRequest;
148 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MoveRegionRequest;
149 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotRequest;
150 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotResponse;
151 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SecurityCapabilitiesRequest;
152 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
153 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetNormalizerRunningRequest;
154 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownRequest;
155 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotRequest;
156 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotResponse;
157 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterRequest;
158 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableRequest;
159 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionRequest;
160 import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos;
161 import org.apache.hadoop.hbase.protobuf.generated.TableProtos;
162 import org.apache.hadoop.hbase.quotas.QuotaFilter;
163 import org.apache.hadoop.hbase.quotas.QuotaRetriever;
164 import org.apache.hadoop.hbase.quotas.QuotaSettings;
165 import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
166 import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
167 import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
168 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
169 import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
170 import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
171 import org.apache.hadoop.hbase.util.Addressing;
172 import org.apache.hadoop.hbase.util.Bytes;
173 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
174 import org.apache.hadoop.hbase.util.ForeignExceptionUtil;
175 import org.apache.hadoop.hbase.util.Pair;
176 import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
177 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
178 import org.apache.hadoop.ipc.RemoteException;
179 import org.apache.hadoop.util.StringUtils;
180 import org.apache.zookeeper.KeeperException;
181 
182 /**
183  * HBaseAdmin is no longer a client API. It is marked InterfaceAudience.Private indicating that
184  * this is an HBase-internal class as defined in
185  * https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/InterfaceClassification.html
186  * There are no guarantees for backwards source / binary compatibility and methods or class can
187  * change or go away without deprecation.
188  * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead of constructing
189  * an HBaseAdmin directly.
190  *
191  * <p>Connection should be an <i>unmanaged</i> connection obtained via
192  * {@link ConnectionFactory#createConnection(Configuration)}
193  *
194  * @see ConnectionFactory
195  * @see Connection
196  * @see Admin
197  */
198 @InterfaceAudience.Private
199 @InterfaceStability.Evolving
200 public class HBaseAdmin implements Admin {
201   private static final Log LOG = LogFactory.getLog(HBaseAdmin.class);
202 
203   private static final String ZK_IDENTIFIER_PREFIX =  "hbase-admin-on-";
204 
205   private ClusterConnection connection;
206 
207   private volatile Configuration conf;
208   private final long pause;
209   private final int numRetries;
210   // Some operations can take a long time such as disable of big table.
211   // numRetries is for 'normal' stuff... Multiply by this factor when
212   // want to wait a long time.
213   private final int retryLongerMultiplier;
214   private final int syncWaitTimeout;
215   private boolean aborted;
216   private boolean cleanupConnectionOnClose = false; // close the connection in close()
217   private boolean closed = false;
218   private int operationTimeout;
219   private int rpcTimeout;
220 
221   private RpcRetryingCallerFactory rpcCallerFactory;
222   private RpcControllerFactory rpcControllerFactory;
223 
224   private NonceGenerator ng;
225 
226   /**
227    * Constructor.
228    * See {@link #HBaseAdmin(Connection connection)}
229    *
230    * @param c Configuration object. Copied internally.
231    * @deprecated Constructing HBaseAdmin objects manually has been deprecated.
232    * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead.
233    */
234   @Deprecated
235   public HBaseAdmin(Configuration c)
236   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
237     // Will not leak connections, as the new implementation of the constructor
238     // does not throw exceptions anymore.
239     this(ConnectionManager.getConnectionInternal(new Configuration(c)));
240     this.cleanupConnectionOnClose = true;
241   }
242 
243   @Override
244   public int getOperationTimeout() {
245     return operationTimeout;
246   }
247 
248 
249   /**
250    * Constructor for externally managed Connections.
251    * The connection to master will be created when required by admin functions.
252    *
253    * @param connection The Connection instance to use
254    * @throws MasterNotRunningException
255    * @throws ZooKeeperConnectionException are not
256    *  thrown anymore but kept into the interface for backward api compatibility
257    * @deprecated Constructing HBaseAdmin objects manually has been deprecated.
258    * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead.
259    */
260   @Deprecated
261   public HBaseAdmin(Connection connection)
262       throws MasterNotRunningException, ZooKeeperConnectionException {
263     this((ClusterConnection)connection);
264   }
265 
266   HBaseAdmin(ClusterConnection connection) {
267     this.conf = connection.getConfiguration();
268     this.connection = connection;
269 
270     // TODO: receive ConnectionConfiguration here rather than re-parsing these configs every time.
271     this.pause = this.conf.getLong(HConstants.HBASE_CLIENT_PAUSE,
272         HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
273     this.numRetries = this.conf.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
274         HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
275     this.retryLongerMultiplier = this.conf.getInt(
276         "hbase.client.retries.longer.multiplier", 10);
277     this.operationTimeout = this.conf.getInt(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT,
278         HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT);
279     this.rpcTimeout = this.conf.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
280         HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
281     this.syncWaitTimeout = this.conf.getInt(
282       "hbase.client.sync.wait.timeout.msec", 10 * 60000); // 10min
283 
284     this.rpcCallerFactory = connection.getRpcRetryingCallerFactory();
285     this.rpcControllerFactory = connection.getRpcControllerFactory();
286 
287     this.ng = this.connection.getNonceGenerator();
288   }
289 
290   @Override
291   public void abort(String why, Throwable e) {
292     // Currently does nothing but throw the passed message and exception
293     this.aborted = true;
294     throw new RuntimeException(why, e);
295   }
296 
297   @Override
298   public boolean isAborted(){
299     return this.aborted;
300   }
301 
302   /**
303    * Abort a procedure
304    * @param procId ID of the procedure to abort
305    * @param mayInterruptIfRunning if the proc completed at least one step, should it be aborted?
306    * @return true if aborted, false if procedure already completed or does not exist
307    * @throws IOException
308    */
309   @Override
310   public boolean abortProcedure(
311       final long procId,
312       final boolean mayInterruptIfRunning) throws IOException {
313     Future<Boolean> future = abortProcedureAsync(procId, mayInterruptIfRunning);
314     try {
315       return future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
316     } catch (InterruptedException e) {
317       throw new InterruptedIOException("Interrupted when waiting for procedure to be cancelled");
318     } catch (TimeoutException e) {
319       throw new TimeoutIOException(e);
320     } catch (ExecutionException e) {
321       if (e.getCause() instanceof IOException) {
322         throw (IOException)e.getCause();
323       } else {
324         throw new IOException(e.getCause());
325       }
326     }
327   }
328 
329   /**
330    * Abort a procedure but does not block and wait for it be completely removed.
331    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
332    * It may throw ExecutionException if there was an error while executing the operation
333    * or TimeoutException in case the wait timeout was not long enough to allow the
334    * operation to complete.
335    *
336    * @param procId ID of the procedure to abort
337    * @param mayInterruptIfRunning if the proc completed at least one step, should it be aborted?
338    * @return true if aborted, false if procedure already completed or does not exist
339    * @throws IOException
340    */
341   @Override
342   public Future<Boolean> abortProcedureAsync(
343       final long procId,
344       final boolean mayInterruptIfRunning) throws IOException {
345     Boolean abortProcResponse = executeCallable(
346       new MasterCallable<AbortProcedureResponse>(getConnection()) {
347         @Override
348         public AbortProcedureResponse call(int callTimeout) throws ServiceException {
349           HBaseRpcController controller = rpcControllerFactory.newController();
350           controller.setCallTimeout(callTimeout);
351           AbortProcedureRequest abortProcRequest =
352               AbortProcedureRequest.newBuilder().setProcId(procId).build();
353           return master.abortProcedure(controller, abortProcRequest);
354         }
355       }).getIsProcedureAborted();
356 
357     AbortProcedureFuture abortProcFuture =
358         new AbortProcedureFuture(this, procId, abortProcResponse);
359     return abortProcFuture;
360   }
361 
362   private static class AbortProcedureFuture extends ProcedureFuture<Boolean> {
363     private boolean isAbortInProgress;
364 
365     public AbortProcedureFuture(
366         final HBaseAdmin admin,
367         final Long procId,
368         final Boolean abortProcResponse) {
369       super(admin, procId);
370       this.isAbortInProgress = abortProcResponse;
371     }
372 
373     @Override
374     public Boolean get(long timeout, TimeUnit unit)
375         throws InterruptedException, ExecutionException, TimeoutException {
376       if (!this.isAbortInProgress) {
377         return false;
378       }
379       super.get(timeout, unit);
380       return true;
381     }
382   }
383 
384   /** @return HConnection used by this object. */
385   @Override
386   public HConnection getConnection() {
387     return connection;
388   }
389 
390   /** @return - true if the master server is running. Throws an exception
391    *  otherwise.
392    * @throws ZooKeeperConnectionException
393    * @throws MasterNotRunningException
394    * @deprecated this has been deprecated without a replacement
395    */
396   @Deprecated
397   public boolean isMasterRunning()
398   throws MasterNotRunningException, ZooKeeperConnectionException {
399     return connection.isMasterRunning();
400   }
401 
402   /**
403    * @param tableName Table to check.
404    * @return True if table exists already.
405    * @throws IOException
406    */
407   @Override
408   public boolean tableExists(final TableName tableName) throws IOException {
409     return MetaTableAccessor.tableExists(connection, tableName);
410   }
411 
412   public boolean tableExists(final byte[] tableName)
413   throws IOException {
414     return tableExists(TableName.valueOf(tableName));
415   }
416 
417   public boolean tableExists(final String tableName)
418   throws IOException {
419     return tableExists(TableName.valueOf(tableName));
420   }
421 
422   @Override
423   public HTableDescriptor[] listTables() throws IOException {
424     return listTables((Pattern)null, false);
425   }
426 
427   @Override
428   public HTableDescriptor[] listTables(Pattern pattern) throws IOException {
429     return listTables(pattern, false);
430   }
431 
432   @Override
433   public HTableDescriptor[] listTables(String regex) throws IOException {
434     return listTables(Pattern.compile(regex), false);
435   }
436 
437   @Override
438   public HTableDescriptor[] listTables(final Pattern pattern, final boolean includeSysTables)
439       throws IOException {
440     return executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
441       @Override
442       public HTableDescriptor[] call(int callTimeout) throws ServiceException {
443         HBaseRpcController controller = rpcControllerFactory.newController();
444         controller.setCallTimeout(callTimeout);
445         GetTableDescriptorsRequest req =
446             RequestConverter.buildGetTableDescriptorsRequest(pattern, includeSysTables);
447         return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(controller, req));
448       }
449     });
450   }
451 
452   @Override
453   public HTableDescriptor[] listTables(String regex, boolean includeSysTables)
454       throws IOException {
455     return listTables(Pattern.compile(regex), includeSysTables);
456   }
457 
458   /**
459    * List all of the names of userspace tables.
460    * @return String[] table names
461    * @throws IOException if a remote or network exception occurs
462    * @deprecated Use {@link Admin#listTableNames()} instead
463    */
464   @Deprecated
465   public String[] getTableNames() throws IOException {
466     TableName[] tableNames = listTableNames();
467     String result[] = new String[tableNames.length];
468     for (int i = 0; i < tableNames.length; i++) {
469       result[i] = tableNames[i].getNameAsString();
470     }
471     return result;
472   }
473 
474   /**
475    * List all of the names of userspace tables matching the given regular expression.
476    * @param pattern The regular expression to match against
477    * @return String[] table names
478    * @throws IOException if a remote or network exception occurs
479    * @deprecated Use {@link Admin#listTableNames(Pattern)} instead.
480    */
481   @Deprecated
482   public String[] getTableNames(Pattern pattern) throws IOException {
483     TableName[] tableNames = listTableNames(pattern);
484     String result[] = new String[tableNames.length];
485     for (int i = 0; i < tableNames.length; i++) {
486       result[i] = tableNames[i].getNameAsString();
487     }
488     return result;
489   }
490 
491   /**
492    * List all of the names of userspace tables matching the given regular expression.
493    * @param regex The regular expression to match against
494    * @return String[] table names
495    * @throws IOException if a remote or network exception occurs
496    * @deprecated Use {@link Admin#listTableNames(Pattern)} instead.
497    */
498   @Deprecated
499   public String[] getTableNames(String regex) throws IOException {
500     return getTableNames(Pattern.compile(regex));
501   }
502 
503   @Override
504   public TableName[] listTableNames() throws IOException {
505     return listTableNames((Pattern)null, false);
506   }
507 
508   @Override
509   public TableName[] listTableNames(Pattern pattern) throws IOException {
510     return listTableNames(pattern, false);
511   }
512 
513   @Override
514   public TableName[] listTableNames(String regex) throws IOException {
515     return listTableNames(Pattern.compile(regex), false);
516   }
517 
518   @Override
519   public TableName[] listTableNames(final Pattern pattern, final boolean includeSysTables)
520       throws IOException {
521     return executeCallable(new MasterCallable<TableName[]>(getConnection()) {
522       @Override
523       public TableName[] call(int callTimeout) throws ServiceException {
524         HBaseRpcController controller = rpcControllerFactory.newController();
525         controller.setCallTimeout(callTimeout);
526         GetTableNamesRequest req =
527             RequestConverter.buildGetTableNamesRequest(pattern, includeSysTables);
528         return ProtobufUtil.getTableNameArray(master.getTableNames(controller, req)
529             .getTableNamesList());
530       }
531     });
532   }
533 
534   @Override
535   public TableName[] listTableNames(final String regex, final boolean includeSysTables)
536       throws IOException {
537     return listTableNames(Pattern.compile(regex), includeSysTables);
538   }
539 
540   /**
541    * Method for getting the tableDescriptor
542    * @param tableName as a byte []
543    * @return the tableDescriptor
544    * @throws TableNotFoundException
545    * @throws IOException if a remote or network exception occurs
546    */
547   @Override
548   public HTableDescriptor getTableDescriptor(final TableName tableName)
549   throws TableNotFoundException, IOException {
550      return getTableDescriptor(tableName, getConnection(), rpcCallerFactory, rpcControllerFactory,
551        operationTimeout, rpcTimeout);
552   }
553 
554   static HTableDescriptor getTableDescriptor(final TableName tableName, HConnection connection,
555       RpcRetryingCallerFactory rpcCallerFactory, final RpcControllerFactory rpcControllerFactory,
556          int operationTimeout, int rpcTimeout) throws TableNotFoundException, IOException {
557 
558       if (tableName == null) return null;
559       HTableDescriptor htd = executeCallable(new MasterCallable<HTableDescriptor>(connection) {
560         @Override
561         public HTableDescriptor call(int callTimeout) throws ServiceException {
562           HBaseRpcController controller = rpcControllerFactory.newController();
563           controller.setCallTimeout(callTimeout);
564           GetTableDescriptorsResponse htds;
565           GetTableDescriptorsRequest req =
566                   RequestConverter.buildGetTableDescriptorsRequest(tableName);
567           htds = master.getTableDescriptors(controller, req);
568 
569           if (!htds.getTableSchemaList().isEmpty()) {
570             return HTableDescriptor.convert(htds.getTableSchemaList().get(0));
571           }
572           return null;
573         }
574       }, rpcCallerFactory, operationTimeout, rpcTimeout);
575       if (htd != null) {
576         return htd;
577       }
578       throw new TableNotFoundException(tableName.getNameAsString());
579   }
580 
581   public HTableDescriptor getTableDescriptor(final byte[] tableName)
582   throws TableNotFoundException, IOException {
583     return getTableDescriptor(TableName.valueOf(tableName));
584   }
585 
586   private long getPauseTime(int tries) {
587     int triesCount = tries;
588     if (triesCount >= HConstants.RETRY_BACKOFF.length) {
589       triesCount = HConstants.RETRY_BACKOFF.length - 1;
590     }
591     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
592   }
593 
594   /**
595    * Creates a new table.
596    * Synchronous operation.
597    *
598    * @param desc table descriptor for table
599    *
600    * @throws IllegalArgumentException if the table name is reserved
601    * @throws MasterNotRunningException if master is not running
602    * @throws TableExistsException if table already exists (If concurrent
603    * threads, the table may have been created between test-for-existence
604    * and attempt-at-creation).
605    * @throws IOException if a remote or network exception occurs
606    */
607   @Override
608   public void createTable(HTableDescriptor desc)
609   throws IOException {
610     createTable(desc, null);
611   }
612 
613   /**
614    * Creates a new table with the specified number of regions.  The start key
615    * specified will become the end key of the first region of the table, and
616    * the end key specified will become the start key of the last region of the
617    * table (the first region has a null start key and the last region has a
618    * null end key).
619    *
620    * BigInteger math will be used to divide the key range specified into
621    * enough segments to make the required number of total regions.
622    *
623    * Synchronous operation.
624    *
625    * @param desc table descriptor for table
626    * @param startKey beginning of key range
627    * @param endKey end of key range
628    * @param numRegions the total number of regions to create
629    *
630    * @throws IllegalArgumentException if the table name is reserved
631    * @throws MasterNotRunningException if master is not running
632    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
633    * threads, the table may have been created between test-for-existence
634    * and attempt-at-creation).
635    * @throws IOException
636    */
637   @Override
638   public void createTable(HTableDescriptor desc, byte [] startKey,
639       byte [] endKey, int numRegions)
640   throws IOException {
641     if(numRegions < 3) {
642       throw new IllegalArgumentException("Must create at least three regions");
643     } else if(Bytes.compareTo(startKey, endKey) >= 0) {
644       throw new IllegalArgumentException("Start key must be smaller than end key");
645     }
646     if (numRegions == 3) {
647       createTable(desc, new byte[][]{startKey, endKey});
648       return;
649     }
650     byte [][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
651     if(splitKeys == null || splitKeys.length != numRegions - 1) {
652       throw new IllegalArgumentException("Unable to split key range into enough regions");
653     }
654     createTable(desc, splitKeys);
655   }
656 
657   /**
658    * Creates a new table with an initial set of empty regions defined by the
659    * specified split keys.  The total number of regions created will be the
660    * number of split keys plus one. Synchronous operation.
661    * Note : Avoid passing empty split key.
662    *
663    * @param desc table descriptor for table
664    * @param splitKeys array of split keys for the initial regions of the table
665    *
666    * @throws IllegalArgumentException if the table name is reserved, if the split keys
667    * are repeated and if the split key has empty byte array.
668    * @throws MasterNotRunningException if master is not running
669    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
670    * threads, the table may have been created between test-for-existence
671    * and attempt-at-creation).
672    * @throws IOException
673    */
674   @Override
675   public void createTable(final HTableDescriptor desc, byte [][] splitKeys)
676       throws IOException {
677     Future<Void> future = createTableAsyncV2(desc, splitKeys);
678     try {
679       // TODO: how long should we wait? spin forever?
680       future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
681     } catch (InterruptedException e) {
682       throw new InterruptedIOException("Interrupted when waiting" +
683           " for table to be enabled; meta scan was done");
684     } catch (TimeoutException e) {
685       throw new TimeoutIOException(e);
686     } catch (ExecutionException e) {
687       if (e.getCause() instanceof IOException) {
688         throw (IOException)e.getCause();
689       } else {
690         throw new IOException(e.getCause());
691       }
692     }
693   }
694 
695   /**
696    * Creates a new table but does not block and wait for it to come online.
697    * Asynchronous operation.  To check if the table exists, use
698    * {@link #isTableAvailable} -- it is not safe to create an HTable
699    * instance to this table before it is available.
700    * Note : Avoid passing empty split key.
701    * @param desc table descriptor for table
702    *
703    * @throws IllegalArgumentException Bad table name, if the split keys
704    * are repeated and if the split key has empty byte array.
705    * @throws MasterNotRunningException if master is not running
706    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
707    * threads, the table may have been created between test-for-existence
708    * and attempt-at-creation).
709    * @throws IOException
710    */
711   @Override
712   public void createTableAsync(final HTableDescriptor desc, final byte [][] splitKeys)
713       throws IOException {
714     createTableAsyncV2(desc, splitKeys);
715   }
716 
717   /**
718    * Creates a new table but does not block and wait for it to come online.
719    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
720    * It may throw ExecutionException if there was an error while executing the operation
721    * or TimeoutException in case the wait timeout was not long enough to allow the
722    * operation to complete.
723    *
724    * @param desc table descriptor for table
725    * @param splitKeys keys to check if the table has been created with all split keys
726    * @throws IllegalArgumentException Bad table name, if the split keys
727    *    are repeated and if the split key has empty byte array.
728    * @throws IOException if a remote or network exception occurs
729    * @return the result of the async creation. You can use Future.get(long, TimeUnit)
730    *    to wait on the operation to complete.
731    */
732   // TODO: This should be called Async but it will break binary compatibility
733   private Future<Void> createTableAsyncV2(final HTableDescriptor desc, final byte[][] splitKeys)
734       throws IOException {
735     if (desc.getTableName() == null) {
736       throw new IllegalArgumentException("TableName cannot be null");
737     }
738     if (splitKeys != null && splitKeys.length > 0) {
739       Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
740       // Verify there are no duplicate split keys
741       byte[] lastKey = null;
742       for (byte[] splitKey : splitKeys) {
743         if (Bytes.compareTo(splitKey, HConstants.EMPTY_BYTE_ARRAY) == 0) {
744           throw new IllegalArgumentException(
745               "Empty split key must not be passed in the split keys.");
746         }
747         if (lastKey != null && Bytes.equals(splitKey, lastKey)) {
748           throw new IllegalArgumentException("All split keys must be unique, " +
749             "found duplicate: " + Bytes.toStringBinary(splitKey) +
750             ", " + Bytes.toStringBinary(lastKey));
751         }
752         lastKey = splitKey;
753       }
754     }
755 
756     CreateTableResponse response = executeCallable(
757       new MasterCallable<CreateTableResponse>(getConnection()) {
758         @Override
759         public CreateTableResponse call(int callTimeout) throws ServiceException {
760           HBaseRpcController controller = rpcControllerFactory.newController();
761           controller.setCallTimeout(callTimeout);
762           controller.setPriority(desc.getTableName());
763           CreateTableRequest request = RequestConverter.buildCreateTableRequest(
764             desc, splitKeys, ng.getNonceGroup(), ng.newNonce());
765           return master.createTable(controller, request);
766         }
767       });
768     return new CreateTableFuture(this, desc, splitKeys, response);
769   }
770 
771   private static class CreateTableFuture extends ProcedureFuture<Void> {
772     private final HTableDescriptor desc;
773     private final byte[][] splitKeys;
774 
775     public CreateTableFuture(final HBaseAdmin admin, final HTableDescriptor desc,
776         final byte[][] splitKeys, final CreateTableResponse response) {
777       super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null, true);
778       this.splitKeys = splitKeys;
779       this.desc = desc;
780     }
781 
782     @Override
783     protected Void waitOperationResult(final long deadlineTs)
784         throws IOException, TimeoutException {
785       waitForTableEnabled(deadlineTs);
786       waitForAllRegionsOnline(deadlineTs);
787       return null;
788     }
789 
790     @Override
791     protected Void postOperationResult(final Void result, final long deadlineTs)
792         throws IOException, TimeoutException {
793       LOG.info("Created " + desc.getTableName());
794       return result;
795     }
796 
797     private void waitForTableEnabled(final long deadlineTs)
798         throws IOException, TimeoutException {
799       waitForState(deadlineTs, new WaitForStateCallable() {
800         @Override
801         public boolean checkState(int tries) throws IOException {
802           try {
803             if (getAdmin().isTableAvailable(desc.getTableName())) {
804               return true;
805             }
806           } catch (TableNotFoundException tnfe) {
807             LOG.debug("Table "+ desc.getTableName() +" was not enabled, sleeping. tries="+  tries);
808           }
809           return false;
810         }
811 
812         @Override
813         public void throwInterruptedException() throws InterruptedIOException {
814           throw new InterruptedIOException("Interrupted when waiting for table " +
815               desc.getTableName() + " to be enabled");
816         }
817 
818         @Override
819         public void throwTimeoutException(long elapsedTime) throws TimeoutException {
820           throw new TimeoutException("Table " + desc.getTableName() +
821             " not enabled after " + elapsedTime + "msec");
822         }
823       });
824     }
825 
826     private void waitForAllRegionsOnline(final long deadlineTs)
827         throws IOException, TimeoutException {
828       final AtomicInteger actualRegCount = new AtomicInteger(0);
829       final MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
830         @Override
831         public boolean processRow(Result rowResult) throws IOException {
832           RegionLocations list = MetaTableAccessor.getRegionLocations(rowResult);
833           if (list == null) {
834             LOG.warn("No serialized HRegionInfo in " + rowResult);
835             return true;
836           }
837           HRegionLocation l = list.getRegionLocation();
838           if (l == null) {
839             return true;
840           }
841           if (!l.getRegionInfo().getTable().equals(desc.getTableName())) {
842             return false;
843           }
844           if (l.getRegionInfo().isOffline() || l.getRegionInfo().isSplit()) return true;
845           HRegionLocation[] locations = list.getRegionLocations();
846           for (HRegionLocation location : locations) {
847             if (location == null) continue;
848             ServerName serverName = location.getServerName();
849             // Make sure that regions are assigned to server
850             if (serverName != null && serverName.getHostAndPort() != null) {
851               actualRegCount.incrementAndGet();
852             }
853           }
854           return true;
855         }
856       };
857 
858       int tries = 0;
859       IOException serverEx = null;
860       int numRegs = (splitKeys == null ? 1 : splitKeys.length + 1) * desc.getRegionReplication();
861       while (EnvironmentEdgeManager.currentTime() < deadlineTs) {
862         actualRegCount.set(0);
863         MetaScanner.metaScan(getAdmin().getConnection(), visitor, desc.getTableName());
864         if (actualRegCount.get() == numRegs) {
865           // all the regions are online
866           return;
867         }
868 
869         try {
870           Thread.sleep(getAdmin().getPauseTime(tries++));
871         } catch (InterruptedException e) {
872           throw new InterruptedIOException("Interrupted when opening" +
873             " regions; " + actualRegCount.get() + " of " + numRegs +
874             " regions processed so far");
875         }
876       }
877       throw new TimeoutException("Only " + actualRegCount.get() +
878               " of " + numRegs + " regions are online; retries exhausted.");
879     }
880   }
881 
882   public void deleteTable(final String tableName) throws IOException {
883     deleteTable(TableName.valueOf(tableName));
884   }
885 
886   public void deleteTable(final byte[] tableName) throws IOException {
887     deleteTable(TableName.valueOf(tableName));
888   }
889 
890   /**
891    * Deletes a table.
892    * Synchronous operation.
893    *
894    * @param tableName name of table to delete
895    * @throws IOException if a remote or network exception occurs
896    */
897   @Override
898   public void deleteTable(final TableName tableName) throws IOException {
899     Future<Void> future = deleteTableAsyncV2(tableName);
900     try {
901       future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
902     } catch (InterruptedException e) {
903       throw new InterruptedIOException("Interrupted when waiting for table to be deleted");
904     } catch (TimeoutException e) {
905       throw new TimeoutIOException(e);
906     } catch (ExecutionException e) {
907       if (e.getCause() instanceof IOException) {
908         throw (IOException)e.getCause();
909       } else {
910         throw new IOException(e.getCause());
911       }
912     }
913   }
914 
915   /**
916    * Deletes the table but does not block and wait for it be completely removed.
917    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
918    * It may throw ExecutionException if there was an error while executing the operation
919    * or TimeoutException in case the wait timeout was not long enough to allow the
920    * operation to complete.
921    *
922    * @param tableName name of table to delete
923    * @throws IOException if a remote or network exception occurs
924    * @return the result of the async delete. You can use Future.get(long, TimeUnit)
925    *    to wait on the operation to complete.
926    */
927   // TODO: This should be called Async but it will break binary compatibility
928   private Future<Void> deleteTableAsyncV2(final TableName tableName) throws IOException {
929     DeleteTableResponse response = executeCallable(
930       new MasterCallable<DeleteTableResponse>(getConnection()) {
931         @Override
932         public DeleteTableResponse call(int callTimeout) throws ServiceException {
933           HBaseRpcController controller = rpcControllerFactory.newController();
934           controller.setCallTimeout(callTimeout);
935           controller.setPriority(tableName);
936           DeleteTableRequest req =
937               RequestConverter.buildDeleteTableRequest(tableName, ng.getNonceGroup(),ng.newNonce());
938           return master.deleteTable(controller,req);
939         }
940       });
941     return new DeleteTableFuture(this, tableName, response);
942   }
943 
944   private static class DeleteTableFuture extends ProcedureFuture<Void> {
945     private final TableName tableName;
946 
947     public DeleteTableFuture(final HBaseAdmin admin, final TableName tableName,
948         final DeleteTableResponse response) {
949       super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null);
950       this.tableName = tableName;
951     }
952 
953     @Override
954     protected Void waitOperationResult(final long deadlineTs)
955         throws IOException, TimeoutException {
956       waitTableNotFound(deadlineTs);
957       return null;
958     }
959 
960     @Override
961     protected Void postOperationResult(final Void result, final long deadlineTs)
962         throws IOException, TimeoutException {
963       // Delete cached information to prevent clients from using old locations
964       getAdmin().getConnection().clearRegionCache(tableName);
965       LOG.info("Deleted " + tableName);
966       return result;
967     }
968 
969     private void waitTableNotFound(final long deadlineTs)
970         throws IOException, TimeoutException {
971       waitForState(deadlineTs, new WaitForStateCallable() {
972         @Override
973         public boolean checkState(int tries) throws IOException {
974           return !getAdmin().tableExists(tableName);
975         }
976 
977         @Override
978         public void throwInterruptedException() throws InterruptedIOException {
979           throw new InterruptedIOException("Interrupted when waiting for table to be deleted");
980         }
981 
982         @Override
983         public void throwTimeoutException(long elapsedTime) throws TimeoutException {
984           throw new TimeoutException("Table " + tableName + " not yet deleted after " +
985               elapsedTime + "msec");
986         }
987       });
988     }
989   }
990 
991   /**
992    * Deletes tables matching the passed in pattern and wait on completion.
993    *
994    * Warning: Use this method carefully, there is no prompting and the effect is
995    * immediate. Consider using {@link #listTables(java.lang.String)} and
996    * {@link #deleteTable(byte[])}
997    *
998    * @param regex The regular expression to match table names against
999    * @return Table descriptors for tables that couldn't be deleted
1000    * @throws IOException
1001    * @see #deleteTables(java.util.regex.Pattern)
1002    * @see #deleteTable(java.lang.String)
1003    */
1004   @Override
1005   public HTableDescriptor[] deleteTables(String regex) throws IOException {
1006     return deleteTables(Pattern.compile(regex));
1007   }
1008 
1009   /**
1010    * Delete tables matching the passed in pattern and wait on completion.
1011    *
1012    * Warning: Use this method carefully, there is no prompting and the effect is
1013    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
1014    * {@link #deleteTable(byte[])}
1015    *
1016    * @param pattern The pattern to match table names against
1017    * @return Table descriptors for tables that couldn't be deleted
1018    * @throws IOException
1019    */
1020   @Override
1021   public HTableDescriptor[] deleteTables(Pattern pattern) throws IOException {
1022     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
1023     for (HTableDescriptor table : listTables(pattern)) {
1024       try {
1025         deleteTable(table.getTableName());
1026       } catch (IOException ex) {
1027         LOG.info("Failed to delete table " + table.getTableName(), ex);
1028         failed.add(table);
1029       }
1030     }
1031     return failed.toArray(new HTableDescriptor[failed.size()]);
1032   }
1033 
1034   /**
1035    * Truncate a table.
1036    * Synchronous operation.
1037    *
1038    * @param tableName name of table to truncate
1039    * @param preserveSplits True if the splits should be preserved
1040    * @throws IOException if a remote or network exception occurs
1041    */
1042   @Override
1043   public void truncateTable(final TableName tableName, final boolean preserveSplits)
1044       throws IOException {
1045     executeCallable(new MasterCallable<Void>(getConnection()) {
1046       @Override
1047       public Void call(int callTimeout) throws ServiceException {
1048         TruncateTableRequest req = RequestConverter.buildTruncateTableRequest(
1049           tableName, preserveSplits, ng.getNonceGroup(), ng.newNonce());
1050         master.truncateTable(null, req);
1051         return null;
1052       }
1053     });
1054   }
1055 
1056   /**
1057    * Enable a table.  May timeout.  Use {@link #enableTableAsync(byte[])}
1058    * and {@link #isTableEnabled(byte[])} instead.
1059    * The table has to be in disabled state for it to be enabled.
1060    * @param tableName name of the table
1061    * @throws IOException if a remote or network exception occurs
1062    * There could be couple types of IOException
1063    * TableNotFoundException means the table doesn't exist.
1064    * TableNotDisabledException means the table isn't in disabled state.
1065    * @see #isTableEnabled(byte[])
1066    * @see #disableTable(byte[])
1067    * @see #enableTableAsync(byte[])
1068    */
1069   @Override
1070   public void enableTable(final TableName tableName)
1071   throws IOException {
1072     Future<Void> future = enableTableAsyncV2(tableName);
1073     try {
1074       future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
1075     } catch (InterruptedException e) {
1076       throw new InterruptedIOException("Interrupted when waiting for table to be disabled");
1077     } catch (TimeoutException e) {
1078       throw new TimeoutIOException(e);
1079     } catch (ExecutionException e) {
1080       if (e.getCause() instanceof IOException) {
1081         throw (IOException)e.getCause();
1082       } else {
1083         throw new IOException(e.getCause());
1084       }
1085     }
1086   }
1087 
1088   public void enableTable(final byte[] tableName)
1089   throws IOException {
1090     enableTable(TableName.valueOf(tableName));
1091   }
1092 
1093   public void enableTable(final String tableName)
1094   throws IOException {
1095     enableTable(TableName.valueOf(tableName));
1096   }
1097 
1098   /**
1099    * Wait for the table to be enabled and available
1100    * If enabling the table exceeds the retry period, an exception is thrown.
1101    * @param tableName name of the table
1102    * @throws IOException if a remote or network exception occurs or
1103    *    table is not enabled after the retries period.
1104    */
1105   private void waitUntilTableIsEnabled(final TableName tableName) throws IOException {
1106     boolean enabled = false;
1107     long start = EnvironmentEdgeManager.currentTime();
1108     for (int tries = 0; tries < (this.numRetries * this.retryLongerMultiplier); tries++) {
1109       try {
1110         enabled = isTableEnabled(tableName);
1111       } catch (TableNotFoundException tnfe) {
1112         // wait for table to be created
1113         enabled = false;
1114       }
1115       enabled = enabled && isTableAvailable(tableName);
1116       if (enabled) {
1117         break;
1118       }
1119       long sleep = getPauseTime(tries);
1120       if (LOG.isDebugEnabled()) {
1121         LOG.debug("Sleeping= " + sleep + "ms, waiting for all regions to be " +
1122           "enabled in " + tableName);
1123       }
1124       try {
1125         Thread.sleep(sleep);
1126       } catch (InterruptedException e) {
1127         // Do this conversion rather than let it out because do not want to
1128         // change the method signature.
1129         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
1130       }
1131     }
1132     if (!enabled) {
1133       long msec = EnvironmentEdgeManager.currentTime() - start;
1134       throw new IOException("Table '" + tableName +
1135         "' not yet enabled, after " + msec + "ms.");
1136     }
1137   }
1138 
1139   /**
1140    * Brings a table on-line (enables it).  Method returns immediately though
1141    * enable of table may take some time to complete, especially if the table
1142    * is large (All regions are opened as part of enabling process).  Check
1143    * {@link #isTableEnabled(byte[])} to learn when table is fully online.  If
1144    * table is taking too long to online, check server logs.
1145    * @param tableName
1146    * @throws IOException
1147    * @since 0.90.0
1148    */
1149   @Override
1150   public void enableTableAsync(final TableName tableName)
1151   throws IOException {
1152     enableTableAsyncV2(tableName);
1153   }
1154 
1155   public void enableTableAsync(final byte[] tableName)
1156   throws IOException {
1157     enableTable(TableName.valueOf(tableName));
1158   }
1159 
1160   public void enableTableAsync(final String tableName)
1161   throws IOException {
1162     enableTableAsync(TableName.valueOf(tableName));
1163   }
1164 
1165   /**
1166    * Enable the table but does not block and wait for it be completely enabled.
1167    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
1168    * It may throw ExecutionException if there was an error while executing the operation
1169    * or TimeoutException in case the wait timeout was not long enough to allow the
1170    * operation to complete.
1171    *
1172    * @param tableName name of table to delete
1173    * @throws IOException if a remote or network exception occurs
1174    * @return the result of the async enable. You can use Future.get(long, TimeUnit)
1175    *    to wait on the operation to complete.
1176    */
1177   // TODO: This should be called Async but it will break binary compatibility
1178   private Future<Void> enableTableAsyncV2(final TableName tableName) throws IOException {
1179     TableName.isLegalFullyQualifiedTableName(tableName.getName());
1180     EnableTableResponse response = executeCallable(
1181       new MasterCallable<EnableTableResponse>(getConnection()) {
1182         @Override
1183         public EnableTableResponse call(int callTimeout) throws ServiceException {
1184           HBaseRpcController controller = rpcControllerFactory.newController();
1185           controller.setCallTimeout(callTimeout);
1186           controller.setPriority(tableName);
1187 
1188           LOG.info("Started enable of " + tableName);
1189           EnableTableRequest req =
1190               RequestConverter.buildEnableTableRequest(tableName, ng.getNonceGroup(),ng.newNonce());
1191           return master.enableTable(controller,req);
1192         }
1193       });
1194     return new EnableTableFuture(this, tableName, response);
1195   }
1196 
1197   private static class EnableTableFuture extends ProcedureFuture<Void> {
1198     private final TableName tableName;
1199 
1200     public EnableTableFuture(final HBaseAdmin admin, final TableName tableName,
1201         final EnableTableResponse response) {
1202       super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null, true);
1203       this.tableName = tableName;
1204     }
1205 
1206     @Override
1207     protected Void waitOperationResult(final long deadlineTs)
1208         throws IOException, TimeoutException {
1209       waitTableEnabled(deadlineTs);
1210       return null;
1211     }
1212 
1213     @Override
1214     protected Void postOperationResult(final Void result, final long deadlineTs)
1215         throws IOException, TimeoutException {
1216       LOG.info("Enabled " + tableName);
1217       return result;
1218     }
1219 
1220     private void waitTableEnabled(final long deadlineTs)
1221         throws IOException, TimeoutException {
1222       waitForState(deadlineTs, new WaitForStateCallable() {
1223         @Override
1224         public boolean checkState(int tries) throws IOException {
1225           boolean enabled;
1226           try {
1227             enabled = getAdmin().isTableEnabled(tableName);
1228           } catch (TableNotFoundException tnfe) {
1229             return false;
1230           }
1231           return enabled && getAdmin().isTableAvailable(tableName);
1232         }
1233 
1234         @Override
1235         public void throwInterruptedException() throws InterruptedIOException {
1236           throw new InterruptedIOException("Interrupted when waiting for table to be enabled");
1237         }
1238 
1239         @Override
1240         public void throwTimeoutException(long elapsedTime) throws TimeoutException {
1241           throw new TimeoutException("Table " + tableName + " not yet enabled after " +
1242               elapsedTime + "msec");
1243         }
1244       });
1245     }
1246   }
1247 
1248   /**
1249    * Enable tables matching the passed in pattern and wait on completion.
1250    *
1251    * Warning: Use this method carefully, there is no prompting and the effect is
1252    * immediate. Consider using {@link #listTables(java.lang.String)} and
1253    * {@link #enableTable(byte[])}
1254    *
1255    * @param regex The regular expression to match table names against
1256    * @throws IOException
1257    * @see #enableTables(java.util.regex.Pattern)
1258    * @see #enableTable(java.lang.String)
1259    */
1260   @Override
1261   public HTableDescriptor[] enableTables(String regex) throws IOException {
1262     return enableTables(Pattern.compile(regex));
1263   }
1264 
1265   /**
1266    * Enable tables matching the passed in pattern and wait on completion.
1267    *
1268    * Warning: Use this method carefully, there is no prompting and the effect is
1269    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
1270    * {@link #enableTable(byte[])}
1271    *
1272    * @param pattern The pattern to match table names against
1273    * @throws IOException
1274    */
1275   @Override
1276   public HTableDescriptor[] enableTables(Pattern pattern) throws IOException {
1277     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
1278     for (HTableDescriptor table : listTables(pattern)) {
1279       if (isTableDisabled(table.getTableName())) {
1280         try {
1281           enableTable(table.getTableName());
1282         } catch (IOException ex) {
1283           LOG.info("Failed to enable table " + table.getTableName(), ex);
1284           failed.add(table);
1285         }
1286       }
1287     }
1288     return failed.toArray(new HTableDescriptor[failed.size()]);
1289   }
1290 
1291   /**
1292    * Starts the disable of a table.  If it is being served, the master
1293    * will tell the servers to stop serving it.  This method returns immediately.
1294    * The disable of a table can take some time if the table is large (all
1295    * regions are closed as part of table disable operation).
1296    * Call {@link #isTableDisabled(byte[])} to check for when disable completes.
1297    * If table is taking too long to online, check server logs.
1298    * @param tableName name of table
1299    * @throws IOException if a remote or network exception occurs
1300    * @see #isTableDisabled(byte[])
1301    * @see #isTableEnabled(byte[])
1302    * @since 0.90.0
1303    */
1304   @Override
1305   public void disableTableAsync(final TableName tableName) throws IOException {
1306     disableTableAsyncV2(tableName);
1307   }
1308 
1309   public void disableTableAsync(final byte[] tableName) throws IOException {
1310     disableTableAsync(TableName.valueOf(tableName));
1311   }
1312 
1313   public void disableTableAsync(final String tableName) throws IOException {
1314     disableTableAsync(TableName.valueOf(tableName));
1315   }
1316 
1317   /**
1318    * Disable table and wait on completion.  May timeout eventually.  Use
1319    * {@link #disableTableAsync(byte[])} and {@link #isTableDisabled(String)}
1320    * instead.
1321    * The table has to be in enabled state for it to be disabled.
1322    * @param tableName
1323    * @throws IOException
1324    * There could be couple types of IOException
1325    * TableNotFoundException means the table doesn't exist.
1326    * TableNotEnabledException means the table isn't in enabled state.
1327    */
1328   @Override
1329   public void disableTable(final TableName tableName)
1330   throws IOException {
1331     Future<Void> future = disableTableAsyncV2(tableName);
1332     try {
1333       future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
1334     } catch (InterruptedException e) {
1335       throw new InterruptedIOException("Interrupted when waiting for table to be disabled");
1336     } catch (TimeoutException e) {
1337       throw new TimeoutIOException(e);
1338     } catch (ExecutionException e) {
1339       if (e.getCause() instanceof IOException) {
1340         throw (IOException)e.getCause();
1341       } else {
1342         throw new IOException(e.getCause());
1343       }
1344     }
1345   }
1346 
1347   public void disableTable(final byte[] tableName)
1348   throws IOException {
1349     disableTable(TableName.valueOf(tableName));
1350   }
1351 
1352   public void disableTable(final String tableName)
1353   throws IOException {
1354     disableTable(TableName.valueOf(tableName));
1355   }
1356 
1357   /**
1358    * Disable the table but does not block and wait for it be completely disabled.
1359    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
1360    * It may throw ExecutionException if there was an error while executing the operation
1361    * or TimeoutException in case the wait timeout was not long enough to allow the
1362    * operation to complete.
1363    *
1364    * @param tableName name of table to delete
1365    * @throws IOException if a remote or network exception occurs
1366    * @return the result of the async disable. You can use Future.get(long, TimeUnit)
1367    *    to wait on the operation to complete.
1368    */
1369   // TODO: This should be called Async but it will break binary compatibility
1370   private Future<Void> disableTableAsyncV2(final TableName tableName) throws IOException {
1371     TableName.isLegalFullyQualifiedTableName(tableName.getName());
1372     DisableTableResponse response = executeCallable(
1373       new MasterCallable<DisableTableResponse>(getConnection()) {
1374         @Override
1375         public DisableTableResponse call(int callTimeout) throws ServiceException {
1376           HBaseRpcController controller = rpcControllerFactory.newController();
1377           controller.setCallTimeout(callTimeout);
1378           controller.setPriority(tableName);
1379 
1380           LOG.info("Started disable of " + tableName);
1381           DisableTableRequest req =
1382               RequestConverter.buildDisableTableRequest(
1383                 tableName, ng.getNonceGroup(), ng.newNonce());
1384           return master.disableTable(controller, req);
1385         }
1386       });
1387     return new DisableTableFuture(this, tableName, response);
1388   }
1389 
1390   private static class DisableTableFuture extends ProcedureFuture<Void> {
1391     private final TableName tableName;
1392 
1393     public DisableTableFuture(final HBaseAdmin admin, final TableName tableName,
1394         final DisableTableResponse response) {
1395       super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null);
1396       this.tableName = tableName;
1397     }
1398 
1399     @Override
1400     protected Void waitOperationResult(final long deadlineTs)
1401         throws IOException, TimeoutException {
1402       waitTableDisabled(deadlineTs);
1403       return null;
1404     }
1405 
1406     @Override
1407     protected Void postOperationResult(final Void result, final long deadlineTs)
1408         throws IOException, TimeoutException {
1409       LOG.info("Disabled " + tableName);
1410       return result;
1411     }
1412 
1413     private void waitTableDisabled(final long deadlineTs)
1414         throws IOException, TimeoutException {
1415       waitForState(deadlineTs, new WaitForStateCallable() {
1416         @Override
1417         public boolean checkState(int tries) throws IOException {
1418           return getAdmin().isTableDisabled(tableName);
1419         }
1420 
1421         @Override
1422         public void throwInterruptedException() throws InterruptedIOException {
1423           throw new InterruptedIOException("Interrupted when waiting for table to be disabled");
1424         }
1425 
1426         @Override
1427         public void throwTimeoutException(long elapsedTime) throws TimeoutException {
1428           throw new TimeoutException("Table " + tableName + " not yet disabled after " +
1429               elapsedTime + "msec");
1430         }
1431       });
1432     }
1433   }
1434 
1435   /**
1436    * Disable tables matching the passed in pattern and wait on completion.
1437    *
1438    * Warning: Use this method carefully, there is no prompting and the effect is
1439    * immediate. Consider using {@link #listTables(java.lang.String)} and
1440    * {@link #disableTable(byte[])}
1441    *
1442    * @param regex The regular expression to match table names against
1443    * @return Table descriptors for tables that couldn't be disabled
1444    * @throws IOException
1445    * @see #disableTables(java.util.regex.Pattern)
1446    * @see #disableTable(java.lang.String)
1447    */
1448   @Override
1449   public HTableDescriptor[] disableTables(String regex) throws IOException {
1450     return disableTables(Pattern.compile(regex));
1451   }
1452 
1453   /**
1454    * Disable tables matching the passed in pattern and wait on completion.
1455    *
1456    * Warning: Use this method carefully, there is no prompting and the effect is
1457    * immediate. Consider using {@link #listTables(java.util.regex.Pattern) } and
1458    * {@link #disableTable(byte[])}
1459    *
1460    * @param pattern The pattern to match table names against
1461    * @return Table descriptors for tables that couldn't be disabled
1462    * @throws IOException
1463    */
1464   @Override
1465   public HTableDescriptor[] disableTables(Pattern pattern) throws IOException {
1466     List<HTableDescriptor> failed = new LinkedList<HTableDescriptor>();
1467     for (HTableDescriptor table : listTables(pattern)) {
1468       if (isTableEnabled(table.getTableName())) {
1469         try {
1470           disableTable(table.getTableName());
1471         } catch (IOException ex) {
1472           LOG.info("Failed to disable table " + table.getTableName(), ex);
1473           failed.add(table);
1474         }
1475       }
1476     }
1477     return failed.toArray(new HTableDescriptor[failed.size()]);
1478   }
1479 
1480   /*
1481    * Checks whether table exists. If not, throws TableNotFoundException
1482    * @param tableName
1483    */
1484   private void checkTableExistence(TableName tableName) throws IOException {
1485     if (!tableExists(tableName)) {
1486       throw new TableNotFoundException(tableName);
1487     }
1488   }
1489 
1490   /**
1491    * @param tableName name of table to check
1492    * @return true if table is on-line
1493    * @throws IOException if a remote or network exception occurs
1494    */
1495   @Override
1496   public boolean isTableEnabled(TableName tableName) throws IOException {
1497     checkTableExistence(tableName);
1498     return connection.isTableEnabled(tableName);
1499   }
1500 
1501   public boolean isTableEnabled(byte[] tableName) throws IOException {
1502     return isTableEnabled(TableName.valueOf(tableName));
1503   }
1504 
1505   public boolean isTableEnabled(String tableName) throws IOException {
1506     return isTableEnabled(TableName.valueOf(tableName));
1507   }
1508 
1509 
1510 
1511   /**
1512    * @param tableName name of table to check
1513    * @return true if table is off-line
1514    * @throws IOException if a remote or network exception occurs
1515    */
1516   @Override
1517   public boolean isTableDisabled(TableName tableName) throws IOException {
1518     checkTableExistence(tableName);
1519     return connection.isTableDisabled(tableName);
1520   }
1521 
1522   public boolean isTableDisabled(byte[] tableName) throws IOException {
1523     return isTableDisabled(TableName.valueOf(tableName));
1524   }
1525 
1526   public boolean isTableDisabled(String tableName) throws IOException {
1527     return isTableDisabled(TableName.valueOf(tableName));
1528   }
1529 
1530   /**
1531    * @param tableName name of table to check
1532    * @return true if all regions of the table are available
1533    * @throws IOException if a remote or network exception occurs
1534    */
1535   @Override
1536   public boolean isTableAvailable(TableName tableName) throws IOException {
1537     return connection.isTableAvailable(tableName);
1538   }
1539 
1540   public boolean isTableAvailable(byte[] tableName) throws IOException {
1541     return isTableAvailable(TableName.valueOf(tableName));
1542   }
1543 
1544   public boolean isTableAvailable(String tableName) throws IOException {
1545     return isTableAvailable(TableName.valueOf(tableName));
1546   }
1547 
1548   /**
1549    * Use this api to check if the table has been created with the specified number of
1550    * splitkeys which was used while creating the given table.
1551    * Note : If this api is used after a table's region gets splitted, the api may return
1552    * false.
1553    * @param tableName
1554    *          name of table to check
1555    * @param splitKeys
1556    *          keys to check if the table has been created with all split keys
1557    * @throws IOException
1558    *           if a remote or network excpetion occurs
1559    */
1560   @Override
1561   public boolean isTableAvailable(TableName tableName,
1562                                   byte[][] splitKeys) throws IOException {
1563     return connection.isTableAvailable(tableName, splitKeys);
1564   }
1565 
1566   public boolean isTableAvailable(byte[] tableName,
1567                                   byte[][] splitKeys) throws IOException {
1568     return isTableAvailable(TableName.valueOf(tableName), splitKeys);
1569   }
1570 
1571   public boolean isTableAvailable(String tableName,
1572                                   byte[][] splitKeys) throws IOException {
1573     return isTableAvailable(TableName.valueOf(tableName), splitKeys);
1574   }
1575 
1576   /**
1577    * Get the status of alter command - indicates how many regions have received
1578    * the updated schema Asynchronous operation.
1579    *
1580    * @param tableName TableName instance
1581    * @return Pair indicating the number of regions updated Pair.getFirst() is the
1582    *         regions that are yet to be updated Pair.getSecond() is the total number
1583    *         of regions of the table
1584    * @throws IOException
1585    *           if a remote or network exception occurs
1586    */
1587   @Override
1588   public Pair<Integer, Integer> getAlterStatus(final TableName tableName)
1589   throws IOException {
1590     return executeCallable(new MasterCallable<Pair<Integer, Integer>>(getConnection()) {
1591       @Override
1592       public Pair<Integer, Integer> call(int callTimeout) throws ServiceException {
1593         HBaseRpcController controller = rpcControllerFactory.newController();
1594         controller.setCallTimeout(callTimeout);
1595         controller.setPriority(tableName);
1596 
1597         GetSchemaAlterStatusRequest req = RequestConverter
1598             .buildGetSchemaAlterStatusRequest(tableName);
1599         GetSchemaAlterStatusResponse ret = master.getSchemaAlterStatus(controller, req);
1600         Pair<Integer, Integer> pair = new Pair<Integer, Integer>(Integer.valueOf(ret
1601             .getYetToUpdateRegions()), Integer.valueOf(ret.getTotalRegions()));
1602         return pair;
1603       }
1604     });
1605   }
1606 
1607   /**
1608    * Get the status of alter command - indicates how many regions have received
1609    * the updated schema Asynchronous operation.
1610    *
1611    * @param tableName
1612    *          name of the table to get the status of
1613    * @return Pair indicating the number of regions updated Pair.getFirst() is the
1614    *         regions that are yet to be updated Pair.getSecond() is the total number
1615    *         of regions of the table
1616    * @throws IOException
1617    *           if a remote or network exception occurs
1618    */
1619   @Override
1620   public Pair<Integer, Integer> getAlterStatus(final byte[] tableName)
1621    throws IOException {
1622     return getAlterStatus(TableName.valueOf(tableName));
1623   }
1624 
1625   /**
1626    * Add a column to an existing table.
1627    * Asynchronous operation.
1628    *
1629    * @param tableName name of the table to add column to
1630    * @param column column descriptor of column to be added
1631    * @throws IOException if a remote or network exception occurs
1632    */
1633   public void addColumn(final byte[] tableName, HColumnDescriptor column)
1634   throws IOException {
1635     addColumn(TableName.valueOf(tableName), column);
1636   }
1637 
1638   /**
1639    * Add a column to an existing table.
1640    * Asynchronous operation.
1641    *
1642    * @param tableName name of the table to add column to
1643    * @param column column descriptor of column to be added
1644    * @throws IOException if a remote or network exception occurs
1645    */
1646   public void addColumn(final String tableName, HColumnDescriptor column)
1647   throws IOException {
1648     addColumn(TableName.valueOf(tableName), column);
1649   }
1650 
1651   /**
1652    * Add a column to an existing table.
1653    * Asynchronous operation.
1654    *
1655    * @param tableName name of the table to add column to
1656    * @param column column descriptor of column to be added
1657    * @throws IOException if a remote or network exception occurs
1658    */
1659   @Override
1660   public void addColumn(final TableName tableName, final HColumnDescriptor column)
1661   throws IOException {
1662     executeCallable(new MasterCallable<Void>(getConnection()) {
1663       @Override
1664       public Void call(int callTimeout) throws ServiceException {
1665         HBaseRpcController controller = rpcControllerFactory.newController();
1666         controller.setCallTimeout(callTimeout);
1667         controller.setPriority(tableName);
1668         AddColumnRequest req = RequestConverter.buildAddColumnRequest(
1669           tableName, column, ng.getNonceGroup(), ng.newNonce());
1670         master.addColumn(controller,req);
1671         return null;
1672       }
1673     });
1674   }
1675 
1676   /**
1677    * Delete a column from a table.
1678    * Asynchronous operation.
1679    *
1680    * @param tableName name of table
1681    * @param columnName name of column to be deleted
1682    * @throws IOException if a remote or network exception occurs
1683    */
1684   public void deleteColumn(final byte[] tableName, final String columnName)
1685   throws IOException {
1686     deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName));
1687   }
1688 
1689   /**
1690    * Delete a column from a table.
1691    * Asynchronous operation.
1692    *
1693    * @param tableName name of table
1694    * @param columnName name of column to be deleted
1695    * @throws IOException if a remote or network exception occurs
1696    */
1697   public void deleteColumn(final String tableName, final String columnName)
1698   throws IOException {
1699     deleteColumn(TableName.valueOf(tableName), Bytes.toBytes(columnName));
1700   }
1701 
1702   /**
1703    * Delete a column from a table.
1704    * Asynchronous operation.
1705    *
1706    * @param tableName name of table
1707    * @param columnName name of column to be deleted
1708    * @throws IOException if a remote or network exception occurs
1709    */
1710   @Override
1711   public void deleteColumn(final TableName tableName, final byte [] columnName)
1712   throws IOException {
1713     executeCallable(new MasterCallable<Void>(getConnection()) {
1714       @Override
1715       public Void call(int callTimeout) throws ServiceException {
1716         HBaseRpcController controller = rpcControllerFactory.newController();
1717         controller.setCallTimeout(callTimeout);
1718         controller.setPriority(tableName);
1719         DeleteColumnRequest req = RequestConverter.buildDeleteColumnRequest(
1720           tableName, columnName, ng.getNonceGroup(), ng.newNonce());
1721         master.deleteColumn(controller, req);
1722         return null;
1723       }
1724     });
1725   }
1726 
1727   /**
1728    * Modify an existing column family on a table.
1729    * Asynchronous operation.
1730    *
1731    * @param tableName name of table
1732    * @param descriptor new column descriptor to use
1733    * @throws IOException if a remote or network exception occurs
1734    */
1735   public void modifyColumn(final String tableName, HColumnDescriptor descriptor)
1736   throws IOException {
1737     modifyColumn(TableName.valueOf(tableName), descriptor);
1738   }
1739 
1740   /**
1741    * Modify an existing column family on a table.
1742    * Asynchronous operation.
1743    *
1744    * @param tableName name of table
1745    * @param descriptor new column descriptor to use
1746    * @throws IOException if a remote or network exception occurs
1747    */
1748   public void modifyColumn(final byte[] tableName, HColumnDescriptor descriptor)
1749   throws IOException {
1750     modifyColumn(TableName.valueOf(tableName), descriptor);
1751   }
1752 
1753   /**
1754    * Modify an existing column family on a table.
1755    * Asynchronous operation.
1756    *
1757    * @param tableName name of table
1758    * @param descriptor new column descriptor to use
1759    * @throws IOException if a remote or network exception occurs
1760    */
1761   @Override
1762   public void modifyColumn(final TableName tableName, final HColumnDescriptor descriptor)
1763   throws IOException {
1764     executeCallable(new MasterCallable<Void>(getConnection()) {
1765       @Override
1766       public Void call(int callTimeout) throws ServiceException {
1767         HBaseRpcController controller = rpcControllerFactory.newController();
1768         controller.setCallTimeout(callTimeout);
1769         controller.setPriority(tableName);
1770         ModifyColumnRequest req = RequestConverter.buildModifyColumnRequest(
1771           tableName, descriptor, ng.getNonceGroup(), ng.newNonce());
1772         master.modifyColumn(controller, req);
1773         return null;
1774       }
1775     });
1776   }
1777 
1778   /**
1779    * Close a region. For expert-admins.  Runs close on the regionserver.  The
1780    * master will not be informed of the close.
1781    * @param regionname region name to close
1782    * @param serverName If supplied, we'll use this location rather than
1783    * the one currently in <code>hbase:meta</code>
1784    * @throws IOException if a remote or network exception occurs
1785    */
1786   @Override
1787   public void closeRegion(final String regionname, final String serverName)
1788   throws IOException {
1789     closeRegion(Bytes.toBytes(regionname), serverName);
1790   }
1791 
1792   /**
1793    * Close a region.  For expert-admins  Runs close on the regionserver.  The
1794    * master will not be informed of the close.
1795    * @param regionname region name to close
1796    * @param serverName The servername of the regionserver.  If passed null we
1797    * will use servername found in the hbase:meta table. A server name
1798    * is made of host, port and startcode.  Here is an example:
1799    * <code> host187.example.com,60020,1289493121758</code>
1800    * @throws IOException if a remote or network exception occurs
1801    */
1802   @Override
1803   public void closeRegion(final byte [] regionname, final String serverName)
1804       throws IOException {
1805     if (serverName != null) {
1806       Pair<HRegionInfo, ServerName> pair = MetaTableAccessor.getRegion(connection, regionname);
1807       if (pair == null || pair.getFirst() == null) {
1808         throw new UnknownRegionException(Bytes.toStringBinary(regionname));
1809       } else {
1810         closeRegion(ServerName.valueOf(serverName), pair.getFirst());
1811       }
1812     } else {
1813       Pair<HRegionInfo, ServerName> pair = MetaTableAccessor.getRegion(connection, regionname);
1814       if (pair == null) {
1815         throw new UnknownRegionException(Bytes.toStringBinary(regionname));
1816       } else if (pair.getSecond() == null) {
1817         throw new NoServerForRegionException(Bytes.toStringBinary(regionname));
1818       } else {
1819         closeRegion(pair.getSecond(), pair.getFirst());
1820       }
1821     }
1822   }
1823 
1824   /**
1825    * For expert-admins. Runs close on the regionserver. Closes a region based on
1826    * the encoded region name. The region server name is mandatory. If the
1827    * servername is provided then based on the online regions in the specified
1828    * regionserver the specified region will be closed. The master will not be
1829    * informed of the close. Note that the regionname is the encoded regionname.
1830    *
1831    * @param encodedRegionName
1832    *          The encoded region name; i.e. the hash that makes up the region
1833    *          name suffix: e.g. if regionname is
1834    *          <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>
1835    *          , then the encoded region name is:
1836    *          <code>527db22f95c8a9e0116f0cc13c680396</code>.
1837    * @param serverName
1838    *          The servername of the regionserver. A server name is made of host,
1839    *          port and startcode. This is mandatory. Here is an example:
1840    *          <code> host187.example.com,60020,1289493121758</code>
1841    * @return true if the region was closed, false if not.
1842    * @throws IOException
1843    *           if a remote or network exception occurs
1844    */
1845   @Override
1846   public boolean closeRegionWithEncodedRegionName(final String encodedRegionName,
1847       final String serverName) throws IOException {
1848     if (null == serverName || ("").equals(serverName.trim())) {
1849       throw new IllegalArgumentException(
1850           "The servername cannot be null or empty.");
1851     }
1852     ServerName sn = ServerName.valueOf(serverName);
1853     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1854     // Close the region without updating zk state.
1855     CloseRegionRequest request =
1856       RequestConverter.buildCloseRegionRequest(sn, encodedRegionName, false);
1857     try {
1858       HBaseRpcController controller = rpcControllerFactory.newController();
1859 
1860       // TODO: this does not do retries, it should. Set priority and timeout in controller
1861       CloseRegionResponse response = admin.closeRegion(controller, request);
1862       boolean isRegionClosed = response.getClosed();
1863       if (false == isRegionClosed) {
1864         LOG.error("Not able to close the region " + encodedRegionName + ".");
1865       }
1866       return isRegionClosed;
1867     } catch (ServiceException se) {
1868       throw ProtobufUtil.getRemoteException(se);
1869     }
1870   }
1871 
1872   /**
1873    * Close a region.  For expert-admins  Runs close on the regionserver.  The
1874    * master will not be informed of the close.
1875    * @param sn
1876    * @param hri
1877    * @throws IOException
1878    */
1879   @Override
1880   public void closeRegion(final ServerName sn, final HRegionInfo hri)
1881   throws IOException {
1882     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1883     HBaseRpcController controller = rpcControllerFactory.newController();
1884 
1885     // Close the region without updating zk state.
1886     ProtobufUtil.closeRegion(controller, admin, sn, hri.getRegionName(), false);
1887   }
1888 
1889   /**
1890    * Get all the online regions on a region server.
1891    */
1892   @Override
1893   public List<HRegionInfo> getOnlineRegions(final ServerName sn) throws IOException {
1894     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1895     HBaseRpcController controller = rpcControllerFactory.newController();
1896     return ProtobufUtil.getOnlineRegions(controller, admin);
1897   }
1898 
1899   /**
1900    * {@inheritDoc}
1901    */
1902   @Override
1903   public void flush(final TableName tableName) throws IOException {
1904     checkTableExists(tableName);
1905     if (isTableDisabled(tableName)) {
1906       LOG.info("Table is disabled: " + tableName.getNameAsString());
1907       return;
1908     }
1909     execProcedure("flush-table-proc", tableName.getNameAsString(),
1910       new HashMap<String, String>());
1911   }
1912 
1913   /**
1914    * {@inheritDoc}
1915    */
1916   @Override
1917   public void flushRegion(final byte[] regionName) throws IOException {
1918     Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionName);
1919     if (regionServerPair == null) {
1920       throw new IllegalArgumentException("Unknown regionname: " + Bytes.toStringBinary(regionName));
1921     }
1922     if (regionServerPair.getSecond() == null) {
1923       throw new NoServerForRegionException(Bytes.toStringBinary(regionName));
1924     }
1925     flush(regionServerPair.getSecond(), regionServerPair.getFirst());
1926   }
1927 
1928   /**
1929    * @deprecated Use {@link #flush(org.apache.hadoop.hbase.TableName)} or {@link #flushRegion
1930    * (byte[])} instead.
1931    */
1932   @Deprecated
1933   public void flush(final String tableNameOrRegionName)
1934   throws IOException, InterruptedException {
1935     flush(Bytes.toBytes(tableNameOrRegionName));
1936   }
1937 
1938   /**
1939    * @deprecated Use {@link #flush(org.apache.hadoop.hbase.TableName)} or {@link #flushRegion
1940    * (byte[])} instead.
1941    */
1942   @Deprecated
1943   public void flush(final byte[] tableNameOrRegionName)
1944   throws IOException, InterruptedException {
1945     try {
1946       flushRegion(tableNameOrRegionName);
1947     } catch (IllegalArgumentException e) {
1948       // Unknown region.  Try table.
1949       flush(TableName.valueOf(tableNameOrRegionName));
1950     }
1951   }
1952 
1953   private void flush(final ServerName sn, final HRegionInfo hri)
1954   throws IOException {
1955     HBaseRpcController controller = rpcControllerFactory.newController();
1956     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
1957     FlushRegionRequest request =
1958       RequestConverter.buildFlushRegionRequest(hri.getRegionName());
1959     try {
1960       admin.flushRegion(controller, request);
1961     } catch (ServiceException se) {
1962       throw ProtobufUtil.getRemoteException(se);
1963     }
1964   }
1965 
1966   /**
1967    * {@inheritDoc}
1968    */
1969   @Override
1970   public void compact(final TableName tableName)
1971     throws IOException {
1972     compact(tableName, null, false);
1973   }
1974 
1975   /**
1976    * {@inheritDoc}
1977    */
1978   @Override
1979   public void compactRegion(final byte[] regionName)
1980     throws IOException {
1981     compactRegion(regionName, null, false);
1982   }
1983 
1984   /**
1985    * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion
1986    * (byte[])} instead.
1987    */
1988   @Deprecated
1989   public void compact(final String tableNameOrRegionName)
1990   throws IOException {
1991     compact(Bytes.toBytes(tableNameOrRegionName));
1992   }
1993 
1994   /**
1995    * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion
1996    * (byte[])} instead.
1997    */
1998   @Deprecated
1999   public void compact(final byte[] tableNameOrRegionName)
2000   throws IOException {
2001     try {
2002       compactRegion(tableNameOrRegionName, null, false);
2003     } catch (IllegalArgumentException e) {
2004       compact(TableName.valueOf(tableNameOrRegionName), null, false);
2005     }
2006   }
2007 
2008   /**
2009    * {@inheritDoc}
2010    */
2011   @Override
2012   public void compact(final TableName tableName, final byte[] columnFamily)
2013     throws IOException {
2014     compact(tableName, columnFamily, false);
2015   }
2016 
2017   /**
2018    * {@inheritDoc}
2019    */
2020   @Override
2021   public void compactRegion(final byte[] regionName, final byte[] columnFamily)
2022     throws IOException {
2023     compactRegion(regionName, columnFamily, false);
2024   }
2025 
2026   /**
2027    * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion
2028    * (byte[], byte[])} instead.
2029    */
2030   @Deprecated
2031   public void compact(String tableOrRegionName, String columnFamily)
2032     throws IOException {
2033     compact(Bytes.toBytes(tableOrRegionName), Bytes.toBytes(columnFamily));
2034   }
2035 
2036   /**
2037    * @deprecated Use {@link #compact(org.apache.hadoop.hbase.TableName)} or {@link #compactRegion
2038    * (byte[], byte[])} instead.
2039    */
2040   @Deprecated
2041   public void compact(final byte[] tableNameOrRegionName, final byte[] columnFamily)
2042   throws IOException {
2043     try {
2044       compactRegion(tableNameOrRegionName, columnFamily, false);
2045     } catch (IllegalArgumentException e) {
2046       // Bad region, try table
2047       compact(TableName.valueOf(tableNameOrRegionName), columnFamily, false);
2048     }
2049   }
2050 
2051   /**
2052    * {@inheritDoc}
2053    */
2054   @Override
2055   public void compactRegionServer(final ServerName sn, boolean major)
2056   throws IOException, InterruptedException {
2057     for (HRegionInfo region : getOnlineRegions(sn)) {
2058       compact(sn, region, major, null);
2059     }
2060   }
2061 
2062   /**
2063    * {@inheritDoc}
2064    */
2065   @Override
2066   public void majorCompact(final TableName tableName)
2067   throws IOException {
2068     compact(tableName, null, true);
2069   }
2070 
2071   /**
2072    * {@inheritDoc}
2073    */
2074   @Override
2075   public void majorCompactRegion(final byte[] regionName)
2076   throws IOException {
2077     compactRegion(regionName, null, true);
2078   }
2079 
2080   /**
2081    * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName)} or {@link
2082    * #majorCompactRegion(byte[])} instead.
2083    */
2084   @Deprecated
2085   public void majorCompact(final String tableNameOrRegionName)
2086   throws IOException {
2087     majorCompact(Bytes.toBytes(tableNameOrRegionName));
2088   }
2089 
2090   /**
2091    * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName)} or {@link
2092    * #majorCompactRegion(byte[])} instead.
2093    */
2094   @Deprecated
2095   public void majorCompact(final byte[] tableNameOrRegionName)
2096   throws IOException {
2097     try {
2098       compactRegion(tableNameOrRegionName, null, true);
2099     } catch (IllegalArgumentException e) {
2100       // Invalid region, try table
2101       compact(TableName.valueOf(tableNameOrRegionName), null, true);
2102     }
2103   }
2104 
2105   /**
2106    * {@inheritDoc}
2107    */
2108   @Override
2109   public void majorCompact(final TableName tableName, final byte[] columnFamily)
2110   throws IOException {
2111     compact(tableName, columnFamily, true);
2112   }
2113 
2114   /**
2115    * {@inheritDoc}
2116    */
2117   @Override
2118   public void majorCompactRegion(final byte[] regionName, final byte[] columnFamily)
2119   throws IOException {
2120     compactRegion(regionName, columnFamily, true);
2121   }
2122 
2123   /**
2124    * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName,
2125    * byte[])} or {@link #majorCompactRegion(byte[], byte[])} instead.
2126    */
2127   @Deprecated
2128   public void majorCompact(final String tableNameOrRegionName, final String columnFamily)
2129   throws IOException {
2130     majorCompact(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(columnFamily));
2131   }
2132 
2133   /**
2134    * @deprecated Use {@link #majorCompact(org.apache.hadoop.hbase.TableName,
2135    * byte[])} or {@link #majorCompactRegion(byte[], byte[])} instead.
2136    */
2137   @Deprecated
2138   public void majorCompact(final byte[] tableNameOrRegionName, final byte[] columnFamily)
2139   throws IOException {
2140     try {
2141       compactRegion(tableNameOrRegionName, columnFamily, true);
2142     } catch (IllegalArgumentException e) {
2143       // Invalid region, try table
2144       compact(TableName.valueOf(tableNameOrRegionName), columnFamily, true);
2145     }
2146   }
2147 
2148   /**
2149    * Compact a table.
2150    * Asynchronous operation.
2151    *
2152    * @param tableName table or region to compact
2153    * @param columnFamily column family within a table or region
2154    * @param major True if we are to do a major compaction.
2155    * @throws IOException if a remote or network exception occurs
2156    * @throws InterruptedException
2157    */
2158   private void compact(final TableName tableName, final byte[] columnFamily,final boolean major)
2159   throws IOException {
2160     ZooKeeperWatcher zookeeper = null;
2161     try {
2162       checkTableExists(tableName);
2163       zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
2164           new ThrowableAbortable());
2165       List<Pair<HRegionInfo, ServerName>> pairs =
2166         MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName);
2167       for (Pair<HRegionInfo, ServerName> pair: pairs) {
2168         if (pair.getFirst().isOffline()) continue;
2169         if (pair.getSecond() == null) continue;
2170         try {
2171           compact(pair.getSecond(), pair.getFirst(), major, columnFamily);
2172         } catch (NotServingRegionException e) {
2173           if (LOG.isDebugEnabled()) {
2174             LOG.debug("Trying to" + (major ? " major" : "") + " compact " +
2175               pair.getFirst() + ": " +
2176               StringUtils.stringifyException(e));
2177           }
2178         }
2179       }
2180     } finally {
2181       if (zookeeper != null) {
2182         zookeeper.close();
2183       }
2184     }
2185   }
2186 
2187   /**
2188    * Compact an individual region.
2189    * Asynchronous operation.
2190    *
2191    * @param regionName region to compact
2192    * @param columnFamily column family within a table or region
2193    * @param major True if we are to do a major compaction.
2194    * @throws IOException if a remote or network exception occurs
2195    * @throws InterruptedException
2196    */
2197   private void compactRegion(final byte[] regionName, final byte[] columnFamily,final boolean major)
2198   throws IOException {
2199     Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionName);
2200     if (regionServerPair == null) {
2201       throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName));
2202     }
2203     if (regionServerPair.getSecond() == null) {
2204       throw new NoServerForRegionException(Bytes.toStringBinary(regionName));
2205     }
2206     compact(regionServerPair.getSecond(), regionServerPair.getFirst(), major, columnFamily);
2207   }
2208 
2209   private void compact(final ServerName sn, final HRegionInfo hri,
2210       final boolean major, final byte [] family)
2211   throws IOException {
2212     HBaseRpcController controller = rpcControllerFactory.newController();
2213     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
2214     CompactRegionRequest request =
2215       RequestConverter.buildCompactRegionRequest(hri.getRegionName(), major, family);
2216     try {
2217       // TODO: this does not do retries, it should. Set priority and timeout in controller
2218       admin.compactRegion(controller, request);
2219     } catch (ServiceException se) {
2220       throw ProtobufUtil.getRemoteException(se);
2221     }
2222   }
2223 
2224   /**
2225    * Move the region <code>r</code> to <code>dest</code>.
2226    * @param encodedRegionName The encoded region name; i.e. the hash that makes
2227    * up the region name suffix: e.g. if regionname is
2228    * <code>TestTable,0094429456,1289497600452.527db22f95c8a9e0116f0cc13c680396.</code>,
2229    * then the encoded region name is: <code>527db22f95c8a9e0116f0cc13c680396</code>.
2230    * @param destServerName The servername of the destination regionserver.  If
2231    * passed the empty byte array we'll assign to a random server.  A server name
2232    * is made of host, port and startcode.  Here is an example:
2233    * <code> host187.example.com,60020,1289493121758</code>
2234    * @throws UnknownRegionException Thrown if we can't find a region named
2235    * <code>encodedRegionName</code>
2236    */
2237   @Override
2238   public void move(final byte [] encodedRegionName, final byte [] destServerName)
2239       throws IOException {
2240 
2241     executeCallable(new MasterCallable<Void>(getConnection()) {
2242       @Override
2243       public Void call(int callTimeout) throws ServiceException {
2244         HBaseRpcController controller = rpcControllerFactory.newController();
2245         controller.setCallTimeout(callTimeout);
2246         // Hard to know the table name, at least check if meta
2247         if (isMetaRegion(encodedRegionName)) {
2248           controller.setPriority(TableName.META_TABLE_NAME);
2249         }
2250 
2251         try {
2252           MoveRegionRequest request =
2253               RequestConverter.buildMoveRegionRequest(encodedRegionName, destServerName);
2254             master.moveRegion(controller, request);
2255         } catch (DeserializationException de) {
2256           LOG.error("Could not parse destination server name: " + de);
2257           throw new ServiceException(new DoNotRetryIOException(de));
2258         }
2259         return null;
2260       }
2261     });
2262   }
2263 
2264   private boolean isMetaRegion(final byte[] regionName) {
2265     return Bytes.equals(regionName, HRegionInfo.FIRST_META_REGIONINFO.getRegionName())
2266         || Bytes.equals(regionName, HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes());
2267   }
2268 
2269   /**
2270    * @param regionName
2271    *          Region name to assign.
2272    * @throws MasterNotRunningException
2273    * @throws ZooKeeperConnectionException
2274    * @throws IOException
2275    */
2276   @Override
2277   public void assign(final byte[] regionName) throws MasterNotRunningException,
2278       ZooKeeperConnectionException, IOException {
2279     final byte[] toBeAssigned = getRegionName(regionName);
2280     executeCallable(new MasterCallable<Void>(getConnection()) {
2281       @Override
2282       public Void call(int callTimeout) throws ServiceException {
2283         HBaseRpcController controller = rpcControllerFactory.newController();
2284         controller.setCallTimeout(callTimeout);
2285         // Hard to know the table name, at least check if meta
2286         if (isMetaRegion(regionName)) {
2287           controller.setPriority(TableName.META_TABLE_NAME);
2288         }
2289 
2290         AssignRegionRequest request =
2291           RequestConverter.buildAssignRegionRequest(toBeAssigned);
2292         master.assignRegion(controller,request);
2293         return null;
2294       }
2295     });
2296   }
2297 
2298   /**
2299    * Unassign a region from current hosting regionserver.  Region will then be
2300    * assigned to a regionserver chosen at random.  Region could be reassigned
2301    * back to the same server.  Use {@link #move(byte[], byte[])} if you want
2302    * to control the region movement.
2303    * @param regionName Region to unassign. Will clear any existing RegionPlan
2304    * if one found.
2305    * @param force If true, force unassign (Will remove region from
2306    * regions-in-transition too if present. If results in double assignment
2307    * use hbck -fix to resolve. To be used by experts).
2308    * @throws MasterNotRunningException
2309    * @throws ZooKeeperConnectionException
2310    * @throws IOException
2311    */
2312   @Override
2313   public void unassign(final byte [] regionName, final boolean force)
2314   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
2315     final byte[] toBeUnassigned = getRegionName(regionName);
2316     executeCallable(new MasterCallable<Void>(getConnection()) {
2317       @Override
2318       public Void call(int callTimeout) throws ServiceException {
2319         HBaseRpcController controller = rpcControllerFactory.newController();
2320         controller.setCallTimeout(callTimeout);
2321         // Hard to know the table name, at least check if meta
2322         if (isMetaRegion(regionName)) {
2323           controller.setPriority(TableName.META_TABLE_NAME);
2324         }
2325         UnassignRegionRequest request =
2326           RequestConverter.buildUnassignRegionRequest(toBeUnassigned, force);
2327         master.unassignRegion(controller, request);
2328         return null;
2329       }
2330     });
2331   }
2332 
2333   /**
2334    * Offline specified region from master's in-memory state. It will not attempt to reassign the
2335    * region as in unassign. This API can be used when a region not served by any region server and
2336    * still online as per Master's in memory state. If this API is incorrectly used on active region
2337    * then master will loose track of that region.
2338    *
2339    * This is a special method that should be used by experts or hbck.
2340    *
2341    * @param regionName
2342    *          Region to offline.
2343    * @throws IOException
2344    */
2345   @Override
2346   public void offline(final byte [] regionName)
2347   throws IOException {
2348     executeCallable(new MasterCallable<Void>(getConnection()) {
2349       @Override
2350       public Void call(int callTimeout) throws ServiceException {
2351         HBaseRpcController controller = rpcControllerFactory.newController();
2352         controller.setCallTimeout(callTimeout);
2353         // Hard to know the table name, at least check if meta
2354         if (isMetaRegion(regionName)) {
2355           controller.setPriority(TableName.META_TABLE_NAME);
2356         }
2357         master.offlineRegion(controller, RequestConverter.buildOfflineRegionRequest(regionName));
2358         return null;
2359       }
2360     });
2361   }
2362 
2363   /**
2364    * Turn the load balancer on or off.
2365    * @param on If true, enable balancer. If false, disable balancer.
2366    * @param synchronous If true, it waits until current balance() call, if outstanding, to return.
2367    * @return Previous balancer value
2368    */
2369   @Override
2370   public boolean setBalancerRunning(final boolean on, final boolean synchronous)
2371   throws IOException {
2372     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2373       @Override
2374       public Boolean call(int callTimeout) throws ServiceException {
2375         HBaseRpcController controller = rpcControllerFactory.newController();
2376         controller.setCallTimeout(callTimeout);
2377 
2378         SetBalancerRunningRequest req =
2379             RequestConverter.buildSetBalancerRunningRequest(on, synchronous);
2380         return master.setBalancerRunning(controller, req).getPrevBalanceValue();
2381       }
2382     });
2383   }
2384 
2385   /**
2386    * Invoke the balancer.  Will run the balancer and if regions to move, it will
2387    * go ahead and do the reassignments.  Can NOT run for various reasons.  Check
2388    * logs.
2389    * @return True if balancer ran, false otherwise.
2390    */
2391   @Override
2392   public boolean balancer() throws IOException {
2393     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2394       @Override
2395       public Boolean call(int callTimeout) throws ServiceException {
2396         HBaseRpcController controller = rpcControllerFactory.newController();
2397         controller.setCallTimeout(callTimeout);
2398 
2399         return master.balance(controller,
2400           RequestConverter.buildBalanceRequest(false)).getBalancerRan();
2401       }
2402     });
2403   }
2404 
2405   @Override
2406   public boolean balancer(final boolean force) throws IOException {
2407     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2408       @Override
2409       public Boolean call(int callTimeout) throws ServiceException {
2410         HBaseRpcController controller = rpcControllerFactory.newController();
2411         controller.setCallTimeout(callTimeout);
2412 
2413         return master.balance(controller,
2414           RequestConverter.buildBalanceRequest(force)).getBalancerRan();
2415       }
2416     });
2417   }
2418 
2419   /**
2420    * Query the state of the balancer from the Master. It's not a guarantee that the balancer is
2421    * actually running this very moment, but that it will run.
2422    *
2423    * @return True if the balancer is enabled, false otherwise.
2424    */
2425   @Override
2426   public boolean isBalancerEnabled() throws IOException {
2427     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2428       @Override
2429       public Boolean call(int callTimeout) throws ServiceException {
2430         HBaseRpcController controller = rpcControllerFactory.newController();
2431         controller.setCallTimeout(callTimeout);
2432 
2433         return master.isBalancerEnabled(controller,
2434           RequestConverter.buildIsBalancerEnabledRequest()).getEnabled();
2435       }
2436     });
2437   }
2438 
2439   /**
2440    * Invoke region normalizer. Can NOT run for various reasons.  Check logs.
2441    *
2442    * @return True if region normalizer ran, false otherwise.
2443    */
2444   @Override
2445   public boolean normalize() throws IOException {
2446     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2447       @Override
2448       public Boolean call(int callTimeout) throws ServiceException {
2449         HBaseRpcController controller = rpcControllerFactory.newController();
2450         controller.setCallTimeout(callTimeout);
2451 
2452         return master.normalize(controller,
2453           RequestConverter.buildNormalizeRequest()).getNormalizerRan();
2454       }
2455     });
2456   }
2457 
2458   /**
2459    * Query the current state of the region normalizer
2460    *
2461    * @return true if region normalizer is enabled, false otherwise.
2462    */
2463   @Override
2464   public boolean isNormalizerEnabled() throws IOException {
2465     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2466       @Override
2467       public Boolean call(int callTimeout) throws ServiceException {
2468         HBaseRpcController controller = rpcControllerFactory.newController();
2469         controller.setCallTimeout(callTimeout);
2470 
2471         return master.isNormalizerEnabled(controller,
2472           RequestConverter.buildIsNormalizerEnabledRequest()).getEnabled();
2473       }
2474     });
2475   }
2476 
2477   /**
2478    * Turn region normalizer on or off.
2479    *
2480    * @return Previous normalizer value
2481    */
2482   @Override
2483   public boolean setNormalizerRunning(final boolean on) throws IOException {
2484     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2485       @Override
2486       public Boolean call(int callTimeout) throws ServiceException {
2487         HBaseRpcController controller = rpcControllerFactory.newController();
2488         controller.setCallTimeout(callTimeout);
2489 
2490         SetNormalizerRunningRequest req =
2491           RequestConverter.buildSetNormalizerRunningRequest(on);
2492         return master.setNormalizerRunning(controller, req).getPrevNormalizerValue();
2493       }
2494     });
2495   }
2496 
2497   /**
2498    * Enable/Disable the catalog janitor
2499    * @param enable if true enables the catalog janitor
2500    * @return the previous state
2501    * @throws MasterNotRunningException
2502    */
2503   @Override
2504   public boolean enableCatalogJanitor(final boolean enable)
2505       throws IOException {
2506     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2507       @Override
2508       public Boolean call(int callTimeout) throws ServiceException {
2509         HBaseRpcController controller = rpcControllerFactory.newController();
2510         controller.setCallTimeout(callTimeout);
2511 
2512         return master.enableCatalogJanitor(controller,
2513           RequestConverter.buildEnableCatalogJanitorRequest(enable)).getPrevValue();
2514       }
2515     });
2516   }
2517 
2518   /**
2519    * Ask for a scan of the catalog table
2520    * @return the number of entries cleaned
2521    * @throws MasterNotRunningException
2522    */
2523   @Override
2524   public int runCatalogScan() throws IOException {
2525     return executeCallable(new MasterCallable<Integer>(getConnection()) {
2526       @Override
2527       public Integer call(int callTimeout) throws ServiceException {
2528         HBaseRpcController controller = rpcControllerFactory.newController();
2529         controller.setCallTimeout(callTimeout);
2530 
2531         return master.runCatalogScan(controller,
2532           RequestConverter.buildCatalogScanRequest()).getScanResult();
2533       }
2534     });
2535   }
2536 
2537   /**
2538    * Query on the catalog janitor state (Enabled/Disabled?)
2539    * @throws org.apache.hadoop.hbase.MasterNotRunningException
2540    */
2541   @Override
2542   public boolean isCatalogJanitorEnabled() throws IOException {
2543     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2544       @Override
2545       public Boolean call(int callTimeout) throws ServiceException {
2546         HBaseRpcController controller = rpcControllerFactory.newController();
2547         controller.setCallTimeout(callTimeout);
2548 
2549         return master.isCatalogJanitorEnabled(controller,
2550           RequestConverter.buildIsCatalogJanitorEnabledRequest()).getValue();
2551       }
2552     });
2553   }
2554 
2555   @Override
2556   public boolean setCleanerChoreRunning(final boolean on) throws IOException {
2557     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2558       @Override
2559       public Boolean call(int callTimeout) throws ServiceException {
2560         return master.setCleanerChoreRunning(null,
2561             RequestConverter.buildSetCleanerChoreRunningRequest(on)).getPrevValue();
2562       }
2563     });
2564   }
2565 
2566   @Override
2567   public boolean runCleanerChore() throws IOException {
2568     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2569       @Override
2570       public Boolean call(int callTimeout) throws ServiceException {
2571         return master.runCleanerChore(null, RequestConverter.buildCleanerChoreRequest())
2572             .getCleanerChoreRan();
2573       }
2574     });
2575   }
2576 
2577   @Override
2578   public boolean isCleanerChoreEnabled() throws IOException {
2579     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2580       @Override
2581       public Boolean call(int callTimeout) throws ServiceException {
2582         return master.isCleanerChoreEnabled(null,
2583             RequestConverter.buildIsCleanerChoreEnabledRequest()).getValue();
2584       }
2585     });
2586   }
2587 
2588   private boolean isEncodedRegionName(byte[] regionName) throws IOException {
2589     try {
2590       HRegionInfo.parseRegionName(regionName);
2591       return false;
2592     } catch (IOException e) {
2593       if (StringUtils.stringifyException(e)
2594         .contains(HRegionInfo.INVALID_REGION_NAME_FORMAT_MESSAGE)) {
2595         return true;
2596       }
2597       throw e;
2598     }
2599   }
2600 
2601   /**
2602    * Merge two regions. Asynchronous operation.
2603    * @param nameOfRegionA encoded or full name of region a
2604    * @param nameOfRegionB encoded or full name of region b
2605    * @param forcible true if do a compulsory merge, otherwise we will only merge
2606    *          two adjacent regions
2607    * @throws IOException
2608    */
2609   @Override
2610   public void mergeRegions(final byte[] nameOfRegionA,
2611       final byte[] nameOfRegionB, final boolean forcible)
2612       throws IOException {
2613     final byte[] encodedNameOfRegionA = isEncodedRegionName(nameOfRegionA) ? nameOfRegionA :
2614       Bytes.toBytes(HRegionInfo.encodeRegionName(nameOfRegionA));
2615     final byte[] encodedNameOfRegionB = isEncodedRegionName(nameOfRegionB) ? nameOfRegionB :
2616       Bytes.toBytes(HRegionInfo.encodeRegionName(nameOfRegionB));
2617 
2618     Pair<HRegionInfo, ServerName> pair = getRegion(nameOfRegionA);
2619     if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID)
2620       throw new IllegalArgumentException("Can't invoke merge on non-default regions directly");
2621     pair = getRegion(nameOfRegionB);
2622     if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID)
2623       throw new IllegalArgumentException("Can't invoke merge on non-default regions directly");
2624     executeCallable(new MasterCallable<Void>(getConnection()) {
2625       @Override
2626       public Void call(int callTimeout) throws ServiceException {
2627         HBaseRpcController controller = rpcControllerFactory.newController();
2628         controller.setCallTimeout(callTimeout);
2629 
2630         try {
2631           DispatchMergingRegionsRequest request = RequestConverter
2632               .buildDispatchMergingRegionsRequest(encodedNameOfRegionA,
2633                 encodedNameOfRegionB, forcible);
2634           master.dispatchMergingRegions(controller, request);
2635         } catch (DeserializationException de) {
2636           LOG.error("Could not parse destination server name: " + de);
2637         }
2638         return null;
2639       }
2640     });
2641   }
2642 
2643   /**
2644    * {@inheritDoc}
2645    */
2646   @Override
2647   public void split(final TableName tableName)
2648     throws IOException {
2649     split(tableName, null);
2650   }
2651 
2652   /**
2653    * {@inheritDoc}
2654    */
2655   @Override
2656   public void splitRegion(final byte[] regionName)
2657     throws IOException {
2658     splitRegion(regionName, null);
2659   }
2660 
2661   /**
2662    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName)} or {@link #splitRegion
2663    * (byte[])} instead.
2664    */
2665   @Deprecated
2666   public void split(final String tableNameOrRegionName)
2667   throws IOException, InterruptedException {
2668     split(Bytes.toBytes(tableNameOrRegionName));
2669   }
2670 
2671   /**
2672    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName)} or {@link #splitRegion
2673    * (byte[])} instead.
2674    */
2675   @Deprecated
2676   public void split(final byte[] tableNameOrRegionName)
2677   throws IOException, InterruptedException {
2678     split(tableNameOrRegionName, null);
2679   }
2680 
2681   /**
2682    * {@inheritDoc}
2683    */
2684   @Override
2685   public void split(final TableName tableName, final byte [] splitPoint)
2686   throws IOException {
2687     ZooKeeperWatcher zookeeper = null;
2688     try {
2689       checkTableExists(tableName);
2690       zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
2691         new ThrowableAbortable());
2692       List<Pair<HRegionInfo, ServerName>> pairs =
2693         MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName);
2694       for (Pair<HRegionInfo, ServerName> pair: pairs) {
2695         // May not be a server for a particular row
2696         if (pair.getSecond() == null) continue;
2697         HRegionInfo r = pair.getFirst();
2698         // check for parents
2699         if (r.isSplitParent()) continue;
2700         // if a split point given, only split that particular region
2701         if (r.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID ||
2702            (splitPoint != null && !r.containsRow(splitPoint))) continue;
2703         // call out to region server to do split now
2704         split(pair.getSecond(), pair.getFirst(), splitPoint);
2705       }
2706     } finally {
2707       if (zookeeper != null) {
2708         zookeeper.close();
2709       }
2710     }
2711   }
2712 
2713   /**
2714    * {@inheritDoc}
2715    */
2716   @Override
2717   public void splitRegion(final byte[] regionName, final byte [] splitPoint)
2718   throws IOException {
2719     Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionName);
2720     if (regionServerPair == null) {
2721       throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName));
2722     }
2723     if (regionServerPair.getFirst() != null &&
2724         regionServerPair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) {
2725       throw new IllegalArgumentException("Can't split replicas directly. "
2726           + "Replicas are auto-split when their primary is split.");
2727     }
2728     if (regionServerPair.getSecond() == null) {
2729       throw new NoServerForRegionException(Bytes.toStringBinary(regionName));
2730     }
2731     split(regionServerPair.getSecond(), regionServerPair.getFirst(), splitPoint);
2732   }
2733 
2734   /**
2735    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName,
2736    * byte[])} or {@link #splitRegion(byte[], byte[])} instead.
2737    */
2738   @Deprecated
2739   public void split(final String tableNameOrRegionName,
2740     final String splitPoint) throws IOException {
2741     split(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(splitPoint));
2742   }
2743 
2744   /**
2745    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName,
2746    * byte[])} or {@link #splitRegion(byte[], byte[])} instead.
2747    */
2748   @Deprecated
2749   public void split(final byte[] tableNameOrRegionName,
2750       final byte [] splitPoint) throws IOException {
2751     try {
2752       splitRegion(tableNameOrRegionName, splitPoint);
2753     } catch (IllegalArgumentException e) {
2754       // Bad region, try table
2755       split(TableName.valueOf(tableNameOrRegionName), splitPoint);
2756     }
2757   }
2758 
2759   @VisibleForTesting
2760   public void split(final ServerName sn, final HRegionInfo hri,
2761       byte[] splitPoint) throws IOException {
2762     if (hri.getStartKey() != null && splitPoint != null &&
2763          Bytes.compareTo(hri.getStartKey(), splitPoint) == 0) {
2764        throw new IOException("should not give a splitkey which equals to startkey!");
2765     }
2766     HBaseRpcController controller = rpcControllerFactory.newController();
2767     controller.setPriority(hri.getTable());
2768 
2769     // TODO: this does not do retries, it should. Set priority and timeout in controller
2770     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
2771     ProtobufUtil.split(controller, admin, hri, splitPoint);
2772   }
2773 
2774   /**
2775    * Modify an existing table, more IRB friendly version.
2776    * Asynchronous operation.  This means that it may be a while before your
2777    * schema change is updated across all of the table.
2778    *
2779    * @param tableName name of table.
2780    * @param htd modified description of the table
2781    * @throws IOException if a remote or network exception occurs
2782    */
2783   @Override
2784   public void modifyTable(final TableName tableName, final HTableDescriptor htd)
2785   throws IOException {
2786     if (!tableName.equals(htd.getTableName())) {
2787       throw new IllegalArgumentException("the specified table name '" + tableName +
2788         "' doesn't match with the HTD one: " + htd.getTableName());
2789     }
2790 
2791     executeCallable(new MasterCallable<Void>(getConnection()) {
2792       @Override
2793       public Void call(int callTimeout) throws ServiceException {
2794         HBaseRpcController controller = rpcControllerFactory.newController();
2795         controller.setCallTimeout(callTimeout);
2796         controller.setPriority(tableName);
2797         ModifyTableRequest request = RequestConverter.buildModifyTableRequest(
2798           tableName, htd, ng.getNonceGroup(), ng.newNonce());
2799         master.modifyTable(controller, request);
2800         return null;
2801       }
2802     });
2803   }
2804 
2805   public void modifyTable(final byte[] tableName, final HTableDescriptor htd)
2806   throws IOException {
2807     modifyTable(TableName.valueOf(tableName), htd);
2808   }
2809 
2810   public void modifyTable(final String tableName, final HTableDescriptor htd)
2811   throws IOException {
2812     modifyTable(TableName.valueOf(tableName), htd);
2813   }
2814 
2815   /**
2816    * @param regionName Name of a region.
2817    * @return a pair of HRegionInfo and ServerName if <code>regionName</code> is
2818    *  a verified region name (we call {@link
2819    *  MetaTableAccessor#getRegion(HConnection, byte[])}
2820    *  else null.
2821    * Throw IllegalArgumentException if <code>regionName</code> is null.
2822    * @throws IOException
2823    */
2824   Pair<HRegionInfo, ServerName> getRegion(final byte[] regionName) throws IOException {
2825     if (regionName == null) {
2826       throw new IllegalArgumentException("Pass a table name or region name");
2827     }
2828     Pair<HRegionInfo, ServerName> pair =
2829       MetaTableAccessor.getRegion(connection, regionName);
2830     if (pair == null) {
2831       final AtomicReference<Pair<HRegionInfo, ServerName>> result =
2832         new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
2833       final String encodedName = Bytes.toString(regionName);
2834       MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
2835         @Override
2836         public boolean processRow(Result data) throws IOException {
2837           HRegionInfo info = HRegionInfo.getHRegionInfo(data);
2838           if (info == null) {
2839             LOG.warn("No serialized HRegionInfo in " + data);
2840             return true;
2841           }
2842           RegionLocations rl = MetaTableAccessor.getRegionLocations(data);
2843           boolean matched = false;
2844           ServerName sn = null;
2845           for (HRegionLocation h : rl.getRegionLocations()) {
2846             if (h != null && encodedName.equals(h.getRegionInfo().getEncodedName())) {
2847               sn = h.getServerName();
2848               info = h.getRegionInfo();
2849               matched = true;
2850             }
2851           }
2852           if (!matched) return true;
2853           result.set(new Pair<HRegionInfo, ServerName>(info, sn));
2854           return false; // found the region, stop
2855         }
2856       };
2857 
2858       MetaScanner.metaScan(connection, visitor, null);
2859       pair = result.get();
2860     }
2861     return pair;
2862   }
2863 
2864   /**
2865    * If the input is a region name, it is returned as is. If it's an
2866    * encoded region name, the corresponding region is found from meta
2867    * and its region name is returned. If we can't find any region in
2868    * meta matching the input as either region name or encoded region
2869    * name, the input is returned as is. We don't throw unknown
2870    * region exception.
2871    */
2872   private byte[] getRegionName(
2873       final byte[] regionNameOrEncodedRegionName) throws IOException {
2874     if (Bytes.equals(regionNameOrEncodedRegionName,
2875         HRegionInfo.FIRST_META_REGIONINFO.getRegionName())
2876           || Bytes.equals(regionNameOrEncodedRegionName,
2877             HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes())) {
2878       return HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
2879     }
2880     byte[] tmp = regionNameOrEncodedRegionName;
2881     Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionNameOrEncodedRegionName);
2882     if (regionServerPair != null && regionServerPair.getFirst() != null) {
2883       tmp = regionServerPair.getFirst().getRegionName();
2884     }
2885     return tmp;
2886   }
2887 
2888   /**
2889    * Check if table exists or not
2890    * @param tableName Name of a table.
2891    * @return tableName instance
2892    * @throws IOException if a remote or network exception occurs.
2893    * @throws TableNotFoundException if table does not exist.
2894    */
2895   private TableName checkTableExists(final TableName tableName)
2896       throws IOException {
2897     if (!MetaTableAccessor.tableExists(connection, tableName)) {
2898       throw new TableNotFoundException(tableName);
2899     }
2900     return tableName;
2901   }
2902 
2903   /**
2904    * Shuts down the HBase cluster
2905    * @throws IOException if a remote or network exception occurs
2906    */
2907   @Override
2908   public synchronized void shutdown() throws IOException {
2909     executeCallable(new MasterCallable<Void>(getConnection()) {
2910       @Override
2911       public Void call(int callTimeout) throws ServiceException {
2912         HBaseRpcController controller = rpcControllerFactory.newController();
2913         controller.setCallTimeout(callTimeout);
2914         controller.setPriority(HConstants.HIGH_QOS);
2915         master.shutdown(controller, ShutdownRequest.newBuilder().build());
2916         return null;
2917       }
2918     });
2919   }
2920 
2921   /**
2922    * Shuts down the current HBase master only.
2923    * Does not shutdown the cluster.
2924    * @see #shutdown()
2925    * @throws IOException if a remote or network exception occurs
2926    */
2927   @Override
2928   public synchronized void stopMaster() throws IOException {
2929     executeCallable(new MasterCallable<Void>(getConnection()) {
2930       @Override
2931       public Void call(int callTimeout) throws ServiceException {
2932         HBaseRpcController controller = rpcControllerFactory.newController();
2933         controller.setCallTimeout(callTimeout);
2934         controller.setPriority(HConstants.HIGH_QOS);
2935         master.stopMaster(controller, StopMasterRequest.newBuilder().build());
2936         return null;
2937       }
2938     });
2939   }
2940 
2941   /**
2942    * Stop the designated regionserver
2943    * @param hostnamePort Hostname and port delimited by a <code>:</code> as in
2944    * <code>example.org:1234</code>
2945    * @throws IOException if a remote or network exception occurs
2946    */
2947   @Override
2948   public synchronized void stopRegionServer(final String hostnamePort)
2949   throws IOException {
2950     String hostname = Addressing.parseHostname(hostnamePort);
2951     int port = Addressing.parsePort(hostnamePort);
2952     AdminService.BlockingInterface admin =
2953       this.connection.getAdmin(ServerName.valueOf(hostname, port, 0));
2954     StopServerRequest request = RequestConverter.buildStopServerRequest(
2955       "Called by admin client " + this.connection.toString());
2956     HBaseRpcController controller = rpcControllerFactory.newController();
2957 
2958     controller.setPriority(HConstants.HIGH_QOS);
2959     try {
2960       // TODO: this does not do retries, it should. Set priority and timeout in controller
2961       admin.stopServer(controller, request);
2962     } catch (ServiceException se) {
2963       throw ProtobufUtil.getRemoteException(se);
2964     }
2965   }
2966 
2967 
2968   /**
2969    * @return cluster status
2970    * @throws IOException if a remote or network exception occurs
2971    */
2972   @Override
2973   public boolean isMasterInMaintenanceMode() throws IOException {
2974     return executeCallable(new MasterCallable<IsInMaintenanceModeResponse>(getConnection()) {
2975       @Override
2976       public IsInMaintenanceModeResponse call(int callTimeout) throws ServiceException {
2977         HBaseRpcController controller = rpcControllerFactory.newController();
2978         controller.setCallTimeout(callTimeout);
2979         return master.isMasterInMaintenanceMode(
2980           controller, IsInMaintenanceModeRequest.newBuilder().build());
2981       }
2982     }).getInMaintenanceMode();
2983   }
2984 
2985   @Override
2986   public ClusterStatus getClusterStatus() throws IOException {
2987     return executeCallable(new MasterCallable<ClusterStatus>(getConnection()) {
2988       @Override
2989       public ClusterStatus call(int callTimeout) throws ServiceException {
2990         HBaseRpcController controller = rpcControllerFactory.newController();
2991         controller.setCallTimeout(callTimeout);
2992         GetClusterStatusRequest req = RequestConverter.buildGetClusterStatusRequest();
2993         return ClusterStatus.convert(master.getClusterStatus(controller, req).getClusterStatus());
2994       }
2995     });
2996   }
2997 
2998   /**
2999    * @return Configuration used by the instance.
3000    */
3001   @Override
3002   public Configuration getConfiguration() {
3003     return this.conf;
3004   }
3005 
3006   /**
3007    * Create a new namespace
3008    * @param descriptor descriptor which describes the new namespace
3009    * @throws IOException
3010    */
3011   @Override
3012   public void createNamespace(final NamespaceDescriptor descriptor) throws IOException {
3013     executeCallable(new MasterCallable<Void>(getConnection()) {
3014       @Override
3015       public Void call(int callTimeout) throws Exception {
3016         HBaseRpcController controller = rpcControllerFactory.newController();
3017         controller.setCallTimeout(callTimeout);
3018         // TODO: set priority based on NS?
3019         master.createNamespace(controller,
3020           CreateNamespaceRequest.newBuilder()
3021             .setNamespaceDescriptor(ProtobufUtil
3022               .toProtoNamespaceDescriptor(descriptor)).build()
3023         );
3024         return null;
3025       }
3026     });
3027   }
3028 
3029   /**
3030    * Modify an existing namespace
3031    * @param descriptor descriptor which describes the new namespace
3032    * @throws IOException
3033    */
3034   @Override
3035   public void modifyNamespace(final NamespaceDescriptor descriptor) throws IOException {
3036     executeCallable(new MasterCallable<Void>(getConnection()) {
3037       @Override
3038       public Void call(int callTimeout) throws Exception {
3039         HBaseRpcController controller = rpcControllerFactory.newController();
3040         controller.setCallTimeout(callTimeout);
3041         master.modifyNamespace(controller, ModifyNamespaceRequest.newBuilder().
3042           setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(descriptor)).build());
3043         return null;
3044       }
3045     });
3046   }
3047 
3048   /**
3049    * Delete an existing namespace. Only empty namespaces (no tables) can be removed.
3050    * @param name namespace name
3051    * @throws IOException
3052    */
3053   @Override
3054   public void deleteNamespace(final String name) throws IOException {
3055     executeCallable(new MasterCallable<Void>(getConnection()) {
3056       @Override
3057       public Void call(int callTimeout) throws Exception {
3058         HBaseRpcController controller = rpcControllerFactory.newController();
3059         controller.setCallTimeout(callTimeout);
3060         master.deleteNamespace(controller, DeleteNamespaceRequest.newBuilder().
3061           setNamespaceName(name).build());
3062         return null;
3063       }
3064     });
3065   }
3066 
3067   /**
3068    * Get a namespace descriptor by name
3069    * @param name name of namespace descriptor
3070    * @return A descriptor
3071    * @throws IOException
3072    */
3073   @Override
3074   public NamespaceDescriptor getNamespaceDescriptor(final String name)
3075       throws NamespaceNotFoundException, IOException {
3076     return
3077         executeCallable(new MasterCallable<NamespaceDescriptor>(getConnection()) {
3078           @Override
3079           public NamespaceDescriptor call(int callTimeout) throws Exception {
3080             HBaseRpcController controller = rpcControllerFactory.newController();
3081             controller.setCallTimeout(callTimeout);
3082             return ProtobufUtil.toNamespaceDescriptor(
3083               master.getNamespaceDescriptor(controller, GetNamespaceDescriptorRequest.newBuilder().
3084                 setNamespaceName(name).build()).getNamespaceDescriptor());
3085           }
3086         });
3087   }
3088 
3089   /**
3090    * List available namespace descriptors
3091    * @return List of descriptors
3092    * @throws IOException
3093    */
3094   @Override
3095   public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException {
3096     return
3097         executeCallable(new MasterCallable<NamespaceDescriptor[]>(getConnection()) {
3098           @Override
3099           public NamespaceDescriptor[] call(int callTimeout) throws Exception {
3100             HBaseRpcController controller = rpcControllerFactory.newController();
3101             controller.setCallTimeout(callTimeout);
3102             List<HBaseProtos.NamespaceDescriptor> list =
3103                 master.listNamespaceDescriptors(controller,
3104                   ListNamespaceDescriptorsRequest.newBuilder().build())
3105                 .getNamespaceDescriptorList();
3106             NamespaceDescriptor[] res = new NamespaceDescriptor[list.size()];
3107             for(int i = 0; i < list.size(); i++) {
3108               res[i] = ProtobufUtil.toNamespaceDescriptor(list.get(i));
3109             }
3110             return res;
3111           }
3112         });
3113   }
3114 
3115   /**
3116    * List procedures
3117    * @return procedure list
3118    * @throws IOException
3119    */
3120   @Override
3121   public ProcedureInfo[] listProcedures() throws IOException {
3122     return
3123         executeCallable(new MasterCallable<ProcedureInfo[]>(getConnection()) {
3124           @Override
3125           public ProcedureInfo[] call(int callTimeout) throws Exception {
3126             HBaseRpcController controller = rpcControllerFactory.newController();
3127             controller.setCallTimeout(callTimeout);
3128             List<ProcedureProtos.Procedure> procList = master.listProcedures(
3129               controller, ListProceduresRequest.newBuilder().build()).getProcedureList();
3130             ProcedureInfo[] procInfoList = new ProcedureInfo[procList.size()];
3131             for (int i = 0; i < procList.size(); i++) {
3132               procInfoList[i] = ProcedureInfo.convert(procList.get(i));
3133             }
3134             return procInfoList;
3135           }
3136         });
3137   }
3138 
3139   /**
3140    * Get list of table descriptors by namespace
3141    * @param name namespace name
3142    * @return A descriptor
3143    * @throws IOException
3144    */
3145   @Override
3146   public HTableDescriptor[] listTableDescriptorsByNamespace(final String name) throws IOException {
3147     return
3148         executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
3149           @Override
3150           public HTableDescriptor[] call(int callTimeout) throws Exception {
3151             HBaseRpcController controller = rpcControllerFactory.newController();
3152             controller.setCallTimeout(callTimeout);
3153             List<TableSchema> list =
3154                 master.listTableDescriptorsByNamespace(controller,
3155                   ListTableDescriptorsByNamespaceRequest.newBuilder().setNamespaceName(name)
3156                   .build()).getTableSchemaList();
3157             HTableDescriptor[] res = new HTableDescriptor[list.size()];
3158             for(int i=0; i < list.size(); i++) {
3159 
3160               res[i] = HTableDescriptor.convert(list.get(i));
3161             }
3162             return res;
3163           }
3164         });
3165   }
3166 
3167   /**
3168    * Get list of table names by namespace
3169    * @param name namespace name
3170    * @return The list of table names in the namespace
3171    * @throws IOException
3172    */
3173   @Override
3174   public TableName[] listTableNamesByNamespace(final String name) throws IOException {
3175     return
3176         executeCallable(new MasterCallable<TableName[]>(getConnection()) {
3177           @Override
3178           public TableName[] call(int callTimeout) throws Exception {
3179             HBaseRpcController controller = rpcControllerFactory.newController();
3180             controller.setCallTimeout(callTimeout);
3181             List<TableProtos.TableName> tableNames =
3182               master.listTableNamesByNamespace(controller, ListTableNamesByNamespaceRequest.
3183                 newBuilder().setNamespaceName(name).build())
3184                 .getTableNameList();
3185             TableName[] result = new TableName[tableNames.size()];
3186             for (int i = 0; i < tableNames.size(); i++) {
3187               result[i] = ProtobufUtil.toTableName(tableNames.get(i));
3188             }
3189             return result;
3190           }
3191         });
3192   }
3193 
3194   /**
3195    * Check to see if HBase is running. Throw an exception if not.
3196    * @param conf system configuration
3197    * @throws MasterNotRunningException if the master is not running
3198    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
3199    */
3200   // Used by tests and by the Merge tool. Merge tool uses it to figure if HBase is up or not.
3201   public static void checkHBaseAvailable(Configuration conf)
3202   throws MasterNotRunningException, ZooKeeperConnectionException, ServiceException, IOException {
3203     Configuration copyOfConf = HBaseConfiguration.create(conf);
3204     // We set it to make it fail as soon as possible if HBase is not available
3205     copyOfConf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
3206     copyOfConf.setInt("zookeeper.recovery.retry", 0);
3207     try (ClusterConnection connection =
3208         (ClusterConnection)ConnectionFactory.createConnection(copyOfConf)) {
3209         // Check ZK first.
3210         // If the connection exists, we may have a connection to ZK that does not work anymore
3211         ZooKeeperKeepAliveConnection zkw = null;
3212         try {
3213           // This is NASTY. FIX!!!! Dependent on internal implementation! TODO
3214           zkw = ((ConnectionManager.HConnectionImplementation)connection).
3215             getKeepAliveZooKeeperWatcher();
3216           zkw.getRecoverableZooKeeper().getZooKeeper().exists(zkw.baseZNode, false);
3217         } catch (IOException e) {
3218           throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
3219         } catch (InterruptedException e) {
3220           throw (InterruptedIOException)
3221             new InterruptedIOException("Can't connect to ZooKeeper").initCause(e);
3222         } catch (KeeperException e) {
3223           throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
3224         } finally {
3225           if (zkw != null) {
3226             zkw.close();
3227           }
3228         }
3229       connection.isMasterRunning();
3230     }
3231   }
3232 
3233   /**
3234    * get the regions of a given table.
3235    *
3236    * @param tableName the name of the table
3237    * @return Ordered list of {@link HRegionInfo}.
3238    * @throws IOException
3239    */
3240   @Override
3241   public List<HRegionInfo> getTableRegions(final TableName tableName)
3242   throws IOException {
3243     ZooKeeperWatcher zookeeper =
3244       new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
3245         new ThrowableAbortable());
3246     List<HRegionInfo> Regions = null;
3247     try {
3248       Regions = MetaTableAccessor.getTableRegions(zookeeper, connection, tableName, true);
3249     } finally {
3250       zookeeper.close();
3251     }
3252     return Regions;
3253   }
3254 
3255   public List<HRegionInfo> getTableRegions(final byte[] tableName)
3256   throws IOException {
3257     return getTableRegions(TableName.valueOf(tableName));
3258   }
3259 
3260   @Override
3261   public synchronized void close() throws IOException {
3262     if (cleanupConnectionOnClose && this.connection != null && !this.closed) {
3263       this.connection.close();
3264       this.closed = true;
3265     }
3266   }
3267 
3268   /**
3269    * Get tableDescriptors
3270    * @param tableNames List of table names
3271    * @return HTD[] the tableDescriptor
3272    * @throws IOException if a remote or network exception occurs
3273    */
3274   @Override
3275   public HTableDescriptor[] getTableDescriptorsByTableName(final List<TableName> tableNames)
3276   throws IOException {
3277     return executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
3278       @Override
3279       public HTableDescriptor[] call(int callTimeout) throws Exception {
3280         HBaseRpcController controller = rpcControllerFactory.newController();
3281         controller.setCallTimeout(callTimeout);
3282         GetTableDescriptorsRequest req =
3283             RequestConverter.buildGetTableDescriptorsRequest(tableNames);
3284           return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(controller, req));
3285       }
3286     });
3287   }
3288 
3289   /**
3290    * Get tableDescriptor
3291    * @param tableName one table name
3292    * @return HTD the HTableDescriptor or null if the table not exists
3293    * @throws IOException if a remote or network exception occurs
3294    */
3295   private HTableDescriptor getTableDescriptorByTableName(TableName tableName)
3296       throws IOException {
3297     List<TableName> tableNames = new ArrayList<TableName>(1);
3298     tableNames.add(tableName);
3299 
3300     HTableDescriptor[] htdl = getTableDescriptorsByTableName(tableNames);
3301 
3302     if (htdl == null || htdl.length == 0) {
3303       return null;
3304     }
3305     else {
3306       return htdl[0];
3307     }
3308   }
3309 
3310   /**
3311    * Get tableDescriptors
3312    * @param names List of table names
3313    * @return HTD[] the tableDescriptor
3314    * @throws IOException if a remote or network exception occurs
3315    */
3316   @Override
3317   public HTableDescriptor[] getTableDescriptors(List<String> names)
3318   throws IOException {
3319     List<TableName> tableNames = new ArrayList<TableName>(names.size());
3320     for(String name : names) {
3321       tableNames.add(TableName.valueOf(name));
3322     }
3323     return getTableDescriptorsByTableName(tableNames);
3324   }
3325 
3326   private RollWALWriterResponse rollWALWriterImpl(final ServerName sn) throws IOException,
3327       FailedLogCloseException {
3328     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
3329     RollWALWriterRequest request = RequestConverter.buildRollWALWriterRequest();
3330     HBaseRpcController controller = rpcControllerFactory.newController();
3331 
3332     try {
3333       // TODO: this does not do retries, it should. Set priority and timeout in controller
3334       return admin.rollWALWriter(controller, request);
3335     } catch (ServiceException se) {
3336       throw ProtobufUtil.getRemoteException(se);
3337     }
3338   }
3339 
3340   /**
3341    * Roll the log writer. I.e. when using a file system based write ahead log,
3342    * start writing log messages to a new file.
3343    *
3344    * Note that when talking to a version 1.0+ HBase deployment, the rolling is asynchronous.
3345    * This method will return as soon as the roll is requested and the return value will
3346    * always be null. Additionally, the named region server may schedule store flushes at the
3347    * request of the wal handling the roll request.
3348    *
3349    * When talking to a 0.98 or older HBase deployment, the rolling is synchronous and the
3350    * return value may be either null or a list of encoded region names.
3351    *
3352    * @param serverName
3353    *          The servername of the regionserver. A server name is made of host,
3354    *          port and startcode. This is mandatory. Here is an example:
3355    *          <code> host187.example.com,60020,1289493121758</code>
3356    * @return a set of {@link HRegionInfo#getEncodedName()} that would allow the wal to
3357    *         clean up some underlying files. null if there's nothing to flush.
3358    * @throws IOException if a remote or network exception occurs
3359    * @throws FailedLogCloseException
3360    * @deprecated use {@link #rollWALWriter(ServerName)}
3361    */
3362   @Deprecated
3363   public synchronized byte[][] rollHLogWriter(String serverName)
3364       throws IOException, FailedLogCloseException {
3365     ServerName sn = ServerName.valueOf(serverName);
3366     final RollWALWriterResponse response = rollWALWriterImpl(sn);
3367     int regionCount = response.getRegionToFlushCount();
3368     if (0 == regionCount) {
3369       return null;
3370     }
3371     byte[][] regionsToFlush = new byte[regionCount][];
3372     for (int i = 0; i < regionCount; i++) {
3373       ByteString region = response.getRegionToFlush(i);
3374       regionsToFlush[i] = region.toByteArray();
3375     }
3376     return regionsToFlush;
3377   }
3378 
3379   @Override
3380   public synchronized void rollWALWriter(ServerName serverName)
3381       throws IOException, FailedLogCloseException {
3382     rollWALWriterImpl(serverName);
3383   }
3384 
3385   @Override
3386   public String[] getMasterCoprocessors() {
3387     try {
3388       return getClusterStatus().getMasterCoprocessors();
3389     } catch (IOException e) {
3390       LOG.error("Could not getClusterStatus()",e);
3391       return null;
3392     }
3393   }
3394 
3395   /**
3396    * {@inheritDoc}
3397    */
3398   @Override
3399   public CompactionState getCompactionState(final TableName tableName)
3400   throws IOException {
3401     CompactionState state = CompactionState.NONE;
3402     ZooKeeperWatcher zookeeper =
3403       new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
3404         new ThrowableAbortable());
3405     try {
3406       checkTableExists(tableName);
3407       List<Pair<HRegionInfo, ServerName>> pairs =
3408         MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName);
3409       for (Pair<HRegionInfo, ServerName> pair: pairs) {
3410         if (pair.getFirst().isOffline()) continue;
3411         if (pair.getSecond() == null) continue;
3412         try {
3413           ServerName sn = pair.getSecond();
3414           AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
3415           GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest(
3416             pair.getFirst().getRegionName(), true);
3417           GetRegionInfoResponse response = admin.getRegionInfo(null, request);
3418           switch (response.getCompactionState()) {
3419           case MAJOR_AND_MINOR:
3420             return CompactionState.MAJOR_AND_MINOR;
3421           case MAJOR:
3422             if (state == CompactionState.MINOR) {
3423               return CompactionState.MAJOR_AND_MINOR;
3424             }
3425             state = CompactionState.MAJOR;
3426             break;
3427           case MINOR:
3428             if (state == CompactionState.MAJOR) {
3429               return CompactionState.MAJOR_AND_MINOR;
3430             }
3431             state = CompactionState.MINOR;
3432             break;
3433           case NONE:
3434           default: // nothing, continue
3435           }
3436         } catch (NotServingRegionException e) {
3437           if (LOG.isDebugEnabled()) {
3438             LOG.debug("Trying to get compaction state of " +
3439               pair.getFirst() + ": " +
3440               StringUtils.stringifyException(e));
3441           }
3442         } catch (RemoteException e) {
3443           if (e.getMessage().indexOf(NotServingRegionException.class.getName()) >= 0) {
3444             if (LOG.isDebugEnabled()) {
3445               LOG.debug("Trying to get compaction state of " + pair.getFirst() + ": "
3446                 + StringUtils.stringifyException(e));
3447             }
3448           } else {
3449             throw e;
3450           }
3451         }
3452       }
3453     } catch (ServiceException se) {
3454       throw ProtobufUtil.getRemoteException(se);
3455     } finally {
3456       zookeeper.close();
3457     }
3458     return state;
3459   }
3460 
3461   /**
3462    * {@inheritDoc}
3463    */
3464   @Override
3465   public CompactionState getCompactionStateForRegion(final byte[] regionName)
3466   throws IOException {
3467     try {
3468       Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionName);
3469       if (regionServerPair == null) {
3470         throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName));
3471       }
3472       if (regionServerPair.getSecond() == null) {
3473         throw new NoServerForRegionException(Bytes.toStringBinary(regionName));
3474       }
3475       ServerName sn = regionServerPair.getSecond();
3476       AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
3477       GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest(
3478         regionServerPair.getFirst().getRegionName(), true);
3479       HBaseRpcController controller = rpcControllerFactory.newController();
3480       // TODO: this does not do retries, it should. Set priority and timeout in controller
3481       GetRegionInfoResponse response = admin.getRegionInfo(controller, request);
3482       return response.getCompactionState();
3483     } catch (ServiceException se) {
3484       throw ProtobufUtil.getRemoteException(se);
3485     }
3486   }
3487 
3488   /**
3489    * @deprecated Use {@link #getCompactionState(org.apache.hadoop.hbase.TableName)} or {@link
3490    * #getCompactionStateForRegion(byte[])} instead.
3491    */
3492   @Deprecated
3493   public CompactionState getCompactionState(final String tableNameOrRegionName)
3494   throws IOException, InterruptedException {
3495     return getCompactionState(Bytes.toBytes(tableNameOrRegionName));
3496   }
3497 
3498   /**
3499    * @deprecated Use {@link #getCompactionState(org.apache.hadoop.hbase.TableName)} or {@link
3500    * #getCompactionStateForRegion(byte[])} instead.
3501    */
3502   @Deprecated
3503   public CompactionState getCompactionState(final byte[] tableNameOrRegionName)
3504   throws IOException, InterruptedException {
3505     try {
3506       return getCompactionStateForRegion(tableNameOrRegionName);
3507     } catch (IllegalArgumentException e) {
3508       // Invalid region, try table
3509       return getCompactionState(TableName.valueOf(tableNameOrRegionName));
3510     }
3511   }
3512 
3513   /**
3514    * Take a snapshot for the given table. If the table is enabled, a FLUSH-type snapshot will be
3515    * taken. If the table is disabled, an offline snapshot is taken.
3516    * <p>
3517    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3518    * snapshot with the same name (even a different type or with different parameters) will fail with
3519    * a {@link SnapshotCreationException} indicating the duplicate naming.
3520    * <p>
3521    * Snapshot names follow the same naming constraints as tables in HBase. See
3522    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
3523    * @param snapshotName name of the snapshot to be created
3524    * @param tableName name of the table for which snapshot is created
3525    * @throws IOException if a remote or network exception occurs
3526    * @throws SnapshotCreationException if snapshot creation failed
3527    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3528    */
3529   @Override
3530   public void snapshot(final String snapshotName,
3531                        final TableName tableName) throws IOException,
3532       SnapshotCreationException, IllegalArgumentException {
3533     snapshot(snapshotName, tableName, SnapshotDescription.Type.FLUSH);
3534   }
3535 
3536   public void snapshot(final String snapshotName,
3537                        final String tableName) throws IOException,
3538       SnapshotCreationException, IllegalArgumentException {
3539     snapshot(snapshotName, TableName.valueOf(tableName),
3540         SnapshotDescription.Type.FLUSH);
3541   }
3542 
3543   /**
3544    * Create snapshot for the given table of given flush type.
3545    * <p>
3546    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3547    * snapshot with the same name (even a different type or with different parameters) will fail with
3548    * a {@link SnapshotCreationException} indicating the duplicate naming.
3549    * <p>
3550    * Snapshot names follow the same naming constraints as tables in HBase.
3551    * @param snapshotName name of the snapshot to be created
3552    * @param tableName name of the table for which snapshot is created
3553    * @param flushType if the snapshot should be taken without flush memstore first
3554    * @throws IOException if a remote or network exception occurs
3555    * @throws SnapshotCreationException if snapshot creation failed
3556    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3557    */
3558    public void snapshot(final byte[] snapshotName, final byte[] tableName,
3559                        final SnapshotDescription.Type flushType) throws
3560       IOException, SnapshotCreationException, IllegalArgumentException {
3561       snapshot(Bytes.toString(snapshotName), Bytes.toString(tableName), flushType);
3562   }
3563   /**
3564    public void snapshot(final String snapshotName,
3565     * Create a timestamp consistent snapshot for the given table.
3566                         final byte[] tableName) throws IOException,
3567     * <p>
3568     * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3569     * snapshot with the same name (even a different type or with different parameters) will fail with
3570     * a {@link SnapshotCreationException} indicating the duplicate naming.
3571     * <p>
3572     * Snapshot names follow the same naming constraints as tables in HBase.
3573     * @param snapshotName name of the snapshot to be created
3574     * @param tableName name of the table for which snapshot is created
3575     * @throws IOException if a remote or network exception occurs
3576     * @throws SnapshotCreationException if snapshot creation failed
3577     * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3578     */
3579   @Override
3580   public void snapshot(final byte[] snapshotName,
3581                        final TableName tableName) throws IOException,
3582       SnapshotCreationException, IllegalArgumentException {
3583     snapshot(Bytes.toString(snapshotName), tableName, SnapshotDescription.Type.FLUSH);
3584   }
3585 
3586   public void snapshot(final byte[] snapshotName,
3587                        final byte[] tableName) throws IOException,
3588       SnapshotCreationException, IllegalArgumentException {
3589     snapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName),
3590       SnapshotDescription.Type.FLUSH);
3591   }
3592 
3593   /**
3594    * Create typed snapshot of the table.
3595    * <p>
3596    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3597    * snapshot with the same name (even a different type or with different parameters) will fail with
3598    * a {@link SnapshotCreationException} indicating the duplicate naming.
3599    * <p>
3600    * Snapshot names follow the same naming constraints as tables in HBase. See
3601    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
3602    * <p>
3603    * @param snapshotName name to give the snapshot on the filesystem. Must be unique from all other
3604    *          snapshots stored on the cluster
3605    * @param tableName name of the table to snapshot
3606    * @param type type of snapshot to take
3607    * @throws IOException we fail to reach the master
3608    * @throws SnapshotCreationException if snapshot creation failed
3609    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3610    */
3611   @Override
3612   public void snapshot(final String snapshotName,
3613                        final TableName tableName,
3614                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
3615       IllegalArgumentException {
3616     SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
3617     builder.setTable(tableName.getNameAsString());
3618     builder.setName(snapshotName);
3619     builder.setType(type);
3620     snapshot(builder.build());
3621   }
3622 
3623   public void snapshot(final String snapshotName,
3624                        final String tableName,
3625                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
3626       IllegalArgumentException {
3627     snapshot(snapshotName, TableName.valueOf(tableName), type);
3628   }
3629 
3630   public void snapshot(final String snapshotName,
3631                        final byte[] tableName,
3632                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
3633       IllegalArgumentException {
3634     snapshot(snapshotName, TableName.valueOf(tableName), type);
3635   }
3636 
3637   /**
3638    * Take a snapshot and wait for the server to complete that snapshot (blocking).
3639    * <p>
3640    * Only a single snapshot should be taken at a time for an instance of HBase, or results may be
3641    * undefined (you can tell multiple HBase clusters to snapshot at the same time, but only one at a
3642    * time for a single cluster).
3643    * <p>
3644    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3645    * snapshot with the same name (even a different type or with different parameters) will fail with
3646    * a {@link SnapshotCreationException} indicating the duplicate naming.
3647    * <p>
3648    * Snapshot names follow the same naming constraints as tables in HBase. See
3649    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
3650    * <p>
3651    * You should probably use {@link #snapshot(String, String)} or {@link #snapshot(byte[], byte[])}
3652    * unless you are sure about the type of snapshot that you want to take.
3653    * @param snapshot snapshot to take
3654    * @throws IOException or we lose contact with the master.
3655    * @throws SnapshotCreationException if snapshot failed to be taken
3656    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3657    */
3658   @Override
3659   public void snapshot(SnapshotDescription snapshot) throws IOException, SnapshotCreationException,
3660       IllegalArgumentException {
3661     // actually take the snapshot
3662     SnapshotResponse response = takeSnapshotAsync(snapshot);
3663     final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot)
3664         .build();
3665     IsSnapshotDoneResponse done = null;
3666     long start = EnvironmentEdgeManager.currentTime();
3667     long max = response.getExpectedTimeout();
3668     long maxPauseTime = max / this.numRetries;
3669     int tries = 0;
3670     LOG.debug("Waiting a max of " + max + " ms for snapshot '" +
3671         ClientSnapshotDescriptionUtils.toString(snapshot) + "'' to complete. (max " +
3672         maxPauseTime + " ms per retry)");
3673     while (tries == 0
3674         || ((EnvironmentEdgeManager.currentTime() - start) < max && !done.getDone())) {
3675       try {
3676         // sleep a backoff <= pauseTime amount
3677         long sleep = getPauseTime(tries++);
3678         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
3679         LOG.debug("(#" + tries + ") Sleeping: " + sleep +
3680           "ms while waiting for snapshot completion.");
3681         Thread.sleep(sleep);
3682       } catch (InterruptedException e) {
3683         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
3684       }
3685       LOG.debug("Getting current status of snapshot from master...");
3686       done = executeCallable(new MasterCallable<IsSnapshotDoneResponse>(getConnection()) {
3687         @Override
3688         public IsSnapshotDoneResponse call(int callTimeout) throws ServiceException {
3689           HBaseRpcController controller = rpcControllerFactory.newController();
3690           controller.setCallTimeout(callTimeout);
3691           return master.isSnapshotDone(controller, request);
3692         }
3693       });
3694     }
3695     if (!done.getDone()) {
3696       throw new SnapshotCreationException("Snapshot '" + snapshot.getName()
3697           + "' wasn't completed in expectedTime:" + max + " ms", snapshot);
3698     }
3699   }
3700 
3701   /**
3702    * Take a snapshot without waiting for the server to complete that snapshot (asynchronous)
3703    * <p>
3704    * Only a single snapshot should be taken at a time, or results may be undefined.
3705    * @param snapshot snapshot to take
3706    * @return response from the server indicating the max time to wait for the snapshot
3707    * @throws IOException if the snapshot did not succeed or we lose contact with the master.
3708    * @throws SnapshotCreationException if snapshot creation failed
3709    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3710    */
3711   @Override
3712   public SnapshotResponse takeSnapshotAsync(SnapshotDescription snapshot) throws IOException,
3713       SnapshotCreationException {
3714     ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
3715     final SnapshotRequest request = SnapshotRequest.newBuilder().setSnapshot(snapshot)
3716         .build();
3717     // run the snapshot on the master
3718     return executeCallable(new MasterCallable<SnapshotResponse>(getConnection()) {
3719       @Override
3720       public SnapshotResponse call(int callTimeout) throws ServiceException {
3721         HBaseRpcController controller = rpcControllerFactory.newController();
3722         controller.setCallTimeout(callTimeout);
3723         return master.snapshot(controller, request);
3724       }
3725     });
3726   }
3727 
3728   /**
3729    * Check the current state of the passed snapshot.
3730    * <p>
3731    * There are three possible states:
3732    * <ol>
3733    * <li>running - returns <tt>false</tt></li>
3734    * <li>finished - returns <tt>true</tt></li>
3735    * <li>finished with error - throws the exception that caused the snapshot to fail</li>
3736    * </ol>
3737    * <p>
3738    * The cluster only knows about the most recent snapshot. Therefore, if another snapshot has been
3739    * run/started since the snapshot your are checking, you will recieve an
3740    * {@link UnknownSnapshotException}.
3741    * @param snapshot description of the snapshot to check
3742    * @return <tt>true</tt> if the snapshot is completed, <tt>false</tt> if the snapshot is still
3743    *         running
3744    * @throws IOException if we have a network issue
3745    * @throws HBaseSnapshotException if the snapshot failed
3746    * @throws UnknownSnapshotException if the requested snapshot is unknown
3747    */
3748   @Override
3749   public boolean isSnapshotFinished(final SnapshotDescription snapshot)
3750       throws IOException, HBaseSnapshotException, UnknownSnapshotException {
3751 
3752     return executeCallable(new MasterCallable<IsSnapshotDoneResponse>(getConnection()) {
3753       @Override
3754       public IsSnapshotDoneResponse call(int callTimeout) throws ServiceException {
3755         HBaseRpcController controller = rpcControllerFactory.newController();
3756         controller.setCallTimeout(callTimeout);
3757         return master.isSnapshotDone(controller,
3758           IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot).build());
3759       }
3760     }).getDone();
3761   }
3762 
3763   /**
3764    * Restore the specified snapshot on the original table. (The table must be disabled)
3765    * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property
3766    * is set to true, a snapshot of the current table is taken
3767    * before executing the restore operation.
3768    * In case of restore failure, the failsafe snapshot will be restored.
3769    * If the restore completes without problem the failsafe snapshot is deleted.
3770    *
3771    * @param snapshotName name of the snapshot to restore
3772    * @throws IOException if a remote or network exception occurs
3773    * @throws RestoreSnapshotException if snapshot failed to be restored
3774    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3775    */
3776   @Override
3777   public void restoreSnapshot(final byte[] snapshotName)
3778       throws IOException, RestoreSnapshotException {
3779     restoreSnapshot(Bytes.toString(snapshotName));
3780   }
3781 
3782   /**
3783    * Restore the specified snapshot on the original table. (The table must be disabled)
3784    * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property
3785    * is set to true, a snapshot of the current table is taken
3786    * before executing the restore operation.
3787    * In case of restore failure, the failsafe snapshot will be restored.
3788    * If the restore completes without problem the failsafe snapshot is deleted.
3789    *
3790    * @param snapshotName name of the snapshot to restore
3791    * @throws IOException if a remote or network exception occurs
3792    * @throws RestoreSnapshotException if snapshot failed to be restored
3793    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3794    */
3795   @Override
3796   public void restoreSnapshot(final String snapshotName)
3797       throws IOException, RestoreSnapshotException {
3798     boolean takeFailSafeSnapshot =
3799       conf.getBoolean("hbase.snapshot.restore.take.failsafe.snapshot", false);
3800     restoreSnapshot(snapshotName, takeFailSafeSnapshot);
3801   }
3802 
3803   /**
3804    * Restore the specified snapshot on the original table. (The table must be disabled)
3805    * If 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken
3806    * before executing the restore operation.
3807    * In case of restore failure, the failsafe snapshot will be restored.
3808    * If the restore completes without problem the failsafe snapshot is deleted.
3809    *
3810    * The failsafe snapshot name is configurable by using the property
3811    * "hbase.snapshot.restore.failsafe.name".
3812    *
3813    * @param snapshotName name of the snapshot to restore
3814    * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken
3815    * @throws IOException if a remote or network exception occurs
3816    * @throws RestoreSnapshotException if snapshot failed to be restored
3817    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3818    */
3819   @Override
3820   public void restoreSnapshot(final byte[] snapshotName, final boolean takeFailSafeSnapshot)
3821       throws IOException, RestoreSnapshotException {
3822     restoreSnapshot(Bytes.toString(snapshotName), takeFailSafeSnapshot);
3823   }
3824 
3825   /**
3826    * Restore the specified snapshot on the original table. (The table must be disabled) If
3827    * 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken before
3828    * executing the restore operation. In case of restore failure, the failsafe snapshot will be
3829    * restored. If the restore completes without problem the failsafe snapshot is deleted. The
3830    * failsafe snapshot name is configurable by using the property
3831    * "hbase.snapshot.restore.failsafe.name".
3832    * @param snapshotName name of the snapshot to restore
3833    * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken
3834    * @param restoreAcl true to restore acl of snapshot into table.
3835    * @throws IOException if a remote or network exception occurs
3836    * @throws RestoreSnapshotException if snapshot failed to be restored
3837    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3838    */
3839   @Override
3840   public void restoreSnapshot(final String snapshotName, boolean takeFailSafeSnapshot, boolean restoreAcl)
3841       throws IOException, RestoreSnapshotException {
3842     TableName tableName = null;
3843     for (SnapshotDescription snapshotInfo: listSnapshots()) {
3844       if (snapshotInfo.getName().equals(snapshotName)) {
3845         tableName = TableName.valueOf(snapshotInfo.getTable());
3846         break;
3847       }
3848     }
3849 
3850     if (tableName == null) {
3851       throw new RestoreSnapshotException(
3852         "Unable to find the table name for snapshot=" + snapshotName);
3853     }
3854 
3855     // The table does not exists, switch to clone.
3856     if (!tableExists(tableName)) {
3857       cloneSnapshot(snapshotName, tableName, restoreAcl);
3858       return;
3859     }
3860 
3861     // Check if the table is disabled
3862     if (!isTableDisabled(tableName)) {
3863       throw new TableNotDisabledException(tableName);
3864     }
3865 
3866     // Take a snapshot of the current state
3867     String failSafeSnapshotSnapshotName = null;
3868     if (takeFailSafeSnapshot) {
3869       failSafeSnapshotSnapshotName = conf.get("hbase.snapshot.restore.failsafe.name",
3870         "hbase-failsafe-{snapshot.name}-{restore.timestamp}");
3871       failSafeSnapshotSnapshotName = failSafeSnapshotSnapshotName
3872         .replace("{snapshot.name}", snapshotName)
3873         .replace("{table.name}", tableName.toString().replace(TableName.NAMESPACE_DELIM, '.'))
3874         .replace("{restore.timestamp}", String.valueOf(EnvironmentEdgeManager.currentTime()));
3875       LOG.info("Taking restore-failsafe snapshot: " + failSafeSnapshotSnapshotName);
3876       snapshot(failSafeSnapshotSnapshotName, tableName);
3877     }
3878 
3879     try {
3880       // Restore snapshot
3881       internalRestoreSnapshot(snapshotName, tableName, restoreAcl);
3882     } catch (IOException e) {
3883       // Somthing went wrong during the restore...
3884       // if the pre-restore snapshot is available try to rollback
3885       if (takeFailSafeSnapshot) {
3886         try {
3887           internalRestoreSnapshot(failSafeSnapshotSnapshotName, tableName, restoreAcl);
3888           String msg = "Restore snapshot=" + snapshotName +
3889             " failed. Rollback to snapshot=" + failSafeSnapshotSnapshotName + " succeeded.";
3890           LOG.error(msg, e);
3891           throw new RestoreSnapshotException(msg, e);
3892         } catch (IOException ex) {
3893           String msg = "Failed to restore and rollback to snapshot=" + failSafeSnapshotSnapshotName;
3894           LOG.error(msg, ex);
3895           throw new RestoreSnapshotException(msg, e);
3896         }
3897       } else {
3898         throw new RestoreSnapshotException("Failed to restore snapshot=" + snapshotName, e);
3899       }
3900     }
3901 
3902     // If the restore is succeeded, delete the pre-restore snapshot
3903     if (takeFailSafeSnapshot) {
3904       try {
3905         LOG.info("Deleting restore-failsafe snapshot: " + failSafeSnapshotSnapshotName);
3906         deleteSnapshot(failSafeSnapshotSnapshotName);
3907       } catch (IOException e) {
3908         LOG.error("Unable to remove the failsafe snapshot: " + failSafeSnapshotSnapshotName, e);
3909       }
3910     }
3911   }
3912 
3913   @Override
3914   public void restoreSnapshot(String snapshotName, boolean takeFailSafeSnapshot)
3915       throws IOException, RestoreSnapshotException {
3916     restoreSnapshot(snapshotName, takeFailSafeSnapshot, false);
3917   }
3918 
3919   /**
3920    * Create a new table by cloning the snapshot content.
3921    *
3922    * @param snapshotName name of the snapshot to be cloned
3923    * @param tableName name of the table where the snapshot will be restored
3924    * @throws IOException if a remote or network exception occurs
3925    * @throws TableExistsException if table to be created already exists
3926    * @throws RestoreSnapshotException if snapshot failed to be cloned
3927    * @throws IllegalArgumentException if the specified table has not a valid name
3928    */
3929   public void cloneSnapshot(final byte[] snapshotName, final byte[] tableName)
3930       throws IOException, TableExistsException, RestoreSnapshotException {
3931     cloneSnapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName));
3932   }
3933 
3934   /**
3935    * Create a new table by cloning the snapshot content.
3936    *
3937    * @param snapshotName name of the snapshot to be cloned
3938    * @param tableName name of the table where the snapshot will be restored
3939    * @throws IOException if a remote or network exception occurs
3940    * @throws TableExistsException if table to be created already exists
3941    * @throws RestoreSnapshotException if snapshot failed to be cloned
3942    * @throws IllegalArgumentException if the specified table has not a valid name
3943    */
3944   @Override
3945   public void cloneSnapshot(final byte[] snapshotName, final TableName tableName)
3946       throws IOException, TableExistsException, RestoreSnapshotException {
3947     cloneSnapshot(Bytes.toString(snapshotName), tableName);
3948   }
3949 
3950 
3951 
3952   /**
3953    * Create a new table by cloning the snapshot content.
3954    *
3955    * @param snapshotName name of the snapshot to be cloned
3956    * @param tableName name of the table where the snapshot will be restored
3957    * @throws IOException if a remote or network exception occurs
3958    * @throws TableExistsException if table to be created already exists
3959    * @throws RestoreSnapshotException if snapshot failed to be cloned
3960    * @throws IllegalArgumentException if the specified table has not a valid name
3961    */
3962   public void cloneSnapshot(final String snapshotName, final String tableName)
3963       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
3964     cloneSnapshot(snapshotName, TableName.valueOf(tableName));
3965   }
3966 
3967   /**
3968    * Create a new table by cloning the snapshot content.
3969    *
3970    * @param snapshotName name of the snapshot to be cloned
3971    * @param tableName name of the table where the snapshot will be restored
3972    * @throws IOException if a remote or network exception occurs
3973    * @throws TableExistsException if table to be created already exists
3974    * @throws RestoreSnapshotException if snapshot failed to be cloned
3975    * @throws IllegalArgumentException if the specified table has not a valid name
3976    */
3977   @Override
3978   public void cloneSnapshot(final String snapshotName, final TableName tableName,
3979       final boolean restoreAcl) throws IOException, TableExistsException, RestoreSnapshotException {
3980     if (tableExists(tableName)) {
3981       throw new TableExistsException(tableName);
3982     }
3983     internalRestoreSnapshot(snapshotName, tableName, restoreAcl);
3984     waitUntilTableIsEnabled(tableName);
3985   }
3986 
3987   @Override
3988   public void cloneSnapshot(String snapshotName, TableName tableName)
3989       throws IOException, TableExistsException, RestoreSnapshotException {
3990     cloneSnapshot(snapshotName, tableName, false);
3991   }
3992 
3993   /**
3994    * Execute a distributed procedure on a cluster synchronously with return data
3995    *
3996    * @param signature A distributed procedure is uniquely identified
3997    * by its signature (default the root ZK node name of the procedure).
3998    * @param instance The instance name of the procedure. For some procedures, this parameter is
3999    * optional.
4000    * @param props Property/Value pairs of properties passing to the procedure
4001    * @return data returned after procedure execution. null if no return data.
4002    * @throws IOException
4003    */
4004   @Override
4005   public byte[] execProcedureWithRet(String signature, String instance,
4006       Map<String, String> props) throws IOException {
4007     ProcedureDescription.Builder builder = ProcedureDescription.newBuilder();
4008     builder.setSignature(signature).setInstance(instance);
4009     for (Entry<String, String> entry : props.entrySet()) {
4010       NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey())
4011           .setValue(entry.getValue()).build();
4012       builder.addConfiguration(pair);
4013     }
4014 
4015     final ExecProcedureRequest request = ExecProcedureRequest.newBuilder()
4016         .setProcedure(builder.build()).build();
4017     // run the procedure on the master
4018     ExecProcedureResponse response = executeCallable(new MasterCallable<ExecProcedureResponse>(
4019         getConnection()) {
4020       @Override
4021       public ExecProcedureResponse call(int callTimeout) throws ServiceException {
4022         HBaseRpcController controller = rpcControllerFactory.newController();
4023         controller.setCallTimeout(callTimeout);
4024         return master.execProcedureWithRet(controller, request);
4025       }
4026     });
4027 
4028     return response.hasReturnData() ? response.getReturnData().toByteArray() : null;
4029   }
4030   /**
4031    * Execute a distributed procedure on a cluster.
4032    *
4033    * @param signature A distributed procedure is uniquely identified
4034    * by its signature (default the root ZK node name of the procedure).
4035    * @param instance The instance name of the procedure. For some procedures, this parameter is
4036    * optional.
4037    * @param props Property/Value pairs of properties passing to the procedure
4038    * @throws IOException
4039    */
4040   @Override
4041   public void execProcedure(String signature, String instance,
4042       Map<String, String> props) throws IOException {
4043     ProcedureDescription.Builder builder = ProcedureDescription.newBuilder();
4044     builder.setSignature(signature).setInstance(instance);
4045     for (Entry<String, String> entry : props.entrySet()) {
4046       NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey())
4047           .setValue(entry.getValue()).build();
4048       builder.addConfiguration(pair);
4049     }
4050 
4051     final ExecProcedureRequest request = ExecProcedureRequest.newBuilder()
4052         .setProcedure(builder.build()).build();
4053     // run the procedure on the master
4054     ExecProcedureResponse response = executeCallable(new MasterCallable<ExecProcedureResponse>(
4055         getConnection()) {
4056       @Override
4057       public ExecProcedureResponse call(int callTimeout) throws ServiceException {
4058         HBaseRpcController controller = rpcControllerFactory.newController();
4059         controller.setCallTimeout(callTimeout);
4060         return master.execProcedure(controller, request);
4061       }
4062     });
4063 
4064     long start = EnvironmentEdgeManager.currentTime();
4065     long max = response.getExpectedTimeout();
4066     long maxPauseTime = max / this.numRetries;
4067     int tries = 0;
4068     LOG.debug("Waiting a max of " + max + " ms for procedure '" +
4069         signature + " : " + instance + "'' to complete. (max " + maxPauseTime + " ms per retry)");
4070     boolean done = false;
4071     while (tries == 0
4072         || ((EnvironmentEdgeManager.currentTime() - start) < max && !done)) {
4073       try {
4074         // sleep a backoff <= pauseTime amount
4075         long sleep = getPauseTime(tries++);
4076         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
4077         LOG.debug("(#" + tries + ") Sleeping: " + sleep +
4078           "ms while waiting for procedure completion.");
4079         Thread.sleep(sleep);
4080       } catch (InterruptedException e) {
4081         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
4082       }
4083       LOG.debug("Getting current status of procedure from master...");
4084       done = isProcedureFinished(signature, instance, props);
4085     }
4086     if (!done) {
4087       throw new IOException("Procedure '" + signature + " : " + instance
4088           + "' wasn't completed in expectedTime:" + max + " ms");
4089     }
4090   }
4091 
4092   /**
4093    * Check the current state of the specified procedure.
4094    * <p>
4095    * There are three possible states:
4096    * <ol>
4097    * <li>running - returns <tt>false</tt></li>
4098    * <li>finished - returns <tt>true</tt></li>
4099    * <li>finished with error - throws the exception that caused the procedure to fail</li>
4100    * </ol>
4101    * <p>
4102    *
4103    * @param signature The signature that uniquely identifies a procedure
4104    * @param instance The instance name of the procedure
4105    * @param props Property/Value pairs of properties passing to the procedure
4106    * @return true if the specified procedure is finished successfully, false if it is still running
4107    * @throws IOException if the specified procedure finished with error
4108    */
4109   @Override
4110   public boolean isProcedureFinished(String signature, String instance, Map<String, String> props)
4111       throws IOException {
4112     final ProcedureDescription.Builder builder = ProcedureDescription.newBuilder();
4113     builder.setSignature(signature).setInstance(instance);
4114     for (Entry<String, String> entry : props.entrySet()) {
4115       NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey())
4116           .setValue(entry.getValue()).build();
4117       builder.addConfiguration(pair);
4118     }
4119     final ProcedureDescription desc = builder.build();
4120     return executeCallable(
4121         new MasterCallable<IsProcedureDoneResponse>(getConnection()) {
4122           @Override
4123           public IsProcedureDoneResponse call(int callTimeout) throws ServiceException {
4124             HBaseRpcController controller = rpcControllerFactory.newController();
4125             controller.setCallTimeout(callTimeout);
4126             return master.isProcedureDone(controller, IsProcedureDoneRequest
4127                 .newBuilder().setProcedure(desc).build());
4128           }
4129         }).getDone();
4130   }
4131 
4132   /**
4133    * Execute Restore/Clone snapshot and wait for the server to complete (blocking). To check if the
4134    * cloned table exists, use {@link #isTableAvailable} -- it is not safe to create an HTable
4135    * instance to this table before it is available.
4136    * @param snapshotName snapshot to restore
4137    * @param tableName table name to restore the snapshot on
4138    * @throws IOException if a remote or network exception occurs
4139    * @throws RestoreSnapshotException if snapshot failed to be restored
4140    * @throws IllegalArgumentException if the restore request is formatted incorrectly
4141    */
4142   private void internalRestoreSnapshot(final String snapshotName, final TableName tableName,
4143       final boolean restoreAcl)
4144       throws IOException, RestoreSnapshotException {
4145     SnapshotDescription snapshot = SnapshotDescription.newBuilder()
4146         .setName(snapshotName).setTable(tableName.getNameAsString()).build();
4147 
4148     // actually restore the snapshot
4149     internalRestoreSnapshotAsync(snapshot, restoreAcl);
4150 
4151     final IsRestoreSnapshotDoneRequest request = IsRestoreSnapshotDoneRequest.newBuilder()
4152         .setSnapshot(snapshot).build();
4153     IsRestoreSnapshotDoneResponse done = IsRestoreSnapshotDoneResponse.newBuilder()
4154         .setDone(false).buildPartial();
4155     final long maxPauseTime = 5000;
4156     int tries = 0;
4157     while (!done.getDone()) {
4158       try {
4159         // sleep a backoff <= pauseTime amount
4160         long sleep = getPauseTime(tries++);
4161         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
4162         LOG.debug(tries + ") Sleeping: " + sleep + " ms while we wait for snapshot restore to complete.");
4163         Thread.sleep(sleep);
4164       } catch (InterruptedException e) {
4165         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
4166       }
4167       LOG.debug("Getting current status of snapshot restore from master...");
4168       done = executeCallable(new MasterCallable<IsRestoreSnapshotDoneResponse>(
4169           getConnection()) {
4170         @Override
4171         public IsRestoreSnapshotDoneResponse call(int callTimeout) throws ServiceException {
4172           HBaseRpcController controller = rpcControllerFactory.newController();
4173           controller.setCallTimeout(callTimeout);
4174           return master.isRestoreSnapshotDone(controller, request);
4175         }
4176       });
4177     }
4178     if (!done.getDone()) {
4179       throw new RestoreSnapshotException("Snapshot '" + snapshot.getName() + "' wasn't restored.");
4180     }
4181   }
4182 
4183   /**
4184    * Execute Restore/Clone snapshot and wait for the server to complete (asynchronous)
4185    * <p>
4186    * Only a single snapshot should be restored at a time, or results may be undefined.
4187    * @param snapshot snapshot to restore
4188    * @return response from the server indicating the max time to wait for the snapshot
4189    * @throws IOException if a remote or network exception occurs
4190    * @throws RestoreSnapshotException if snapshot failed to be restored
4191    * @throws IllegalArgumentException if the restore request is formatted incorrectly
4192    */
4193   private RestoreSnapshotResponse internalRestoreSnapshotAsync(final SnapshotDescription snapshot,
4194       final boolean restoreAcl) throws IOException, RestoreSnapshotException {
4195     ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
4196 
4197     final RestoreSnapshotRequest request =
4198         RestoreSnapshotRequest.newBuilder().setSnapshot(snapshot).setRestoreACL(restoreAcl).build();
4199 
4200     // run the snapshot restore on the master
4201     return executeCallable(new MasterCallable<RestoreSnapshotResponse>(getConnection()) {
4202       @Override
4203       public RestoreSnapshotResponse call(int callTimeout) throws ServiceException {
4204         HBaseRpcController controller = rpcControllerFactory.newController();
4205         controller.setCallTimeout(callTimeout);
4206         return master.restoreSnapshot(controller, request);
4207       }
4208     });
4209   }
4210 
4211   /**
4212    * List completed snapshots.
4213    * @return a list of snapshot descriptors for completed snapshots
4214    * @throws IOException if a network error occurs
4215    */
4216   @Override
4217   public List<SnapshotDescription> listSnapshots() throws IOException {
4218     return executeCallable(new MasterCallable<List<SnapshotDescription>>(getConnection()) {
4219       @Override
4220       public List<SnapshotDescription> call(int callTimeout) throws ServiceException {
4221         HBaseRpcController controller = rpcControllerFactory.newController();
4222         controller.setCallTimeout(callTimeout);
4223         return master.getCompletedSnapshots(controller,
4224           GetCompletedSnapshotsRequest.newBuilder().build()).getSnapshotsList();
4225       }
4226     });
4227   }
4228 
4229   /**
4230    * List all the completed snapshots matching the given regular expression.
4231    *
4232    * @param regex The regular expression to match against
4233    * @return - returns a List of SnapshotDescription
4234    * @throws IOException if a remote or network exception occurs
4235    */
4236   @Override
4237   public List<SnapshotDescription> listSnapshots(String regex) throws IOException {
4238     return listSnapshots(Pattern.compile(regex));
4239   }
4240 
4241   /**
4242    * List all the completed snapshots matching the given pattern.
4243    *
4244    * @param pattern The compiled regular expression to match against
4245    * @return - returns a List of SnapshotDescription
4246    * @throws IOException if a remote or network exception occurs
4247    */
4248   @Override
4249   public List<SnapshotDescription> listSnapshots(Pattern pattern) throws IOException {
4250     List<SnapshotDescription> matched = new LinkedList<SnapshotDescription>();
4251     List<SnapshotDescription> snapshots = listSnapshots();
4252     for (SnapshotDescription snapshot : snapshots) {
4253       if (pattern.matcher(snapshot.getName()).matches()) {
4254         matched.add(snapshot);
4255       }
4256     }
4257     return matched;
4258   }
4259 
4260   /**
4261    * List all the completed snapshots matching the given table name regular expression and snapshot
4262    * name regular expression.
4263    * @param tableNameRegex The table name regular expression to match against
4264    * @param snapshotNameRegex The snapshot name regular expression to match against
4265    * @return returns a List of completed SnapshotDescription
4266    * @throws IOException if a remote or network exception occurs
4267    */
4268   @Override
4269   public List<SnapshotDescription> listTableSnapshots(String tableNameRegex,
4270       String snapshotNameRegex) throws IOException {
4271     return listTableSnapshots(Pattern.compile(tableNameRegex), Pattern.compile(snapshotNameRegex));
4272   }
4273 
4274   /**
4275    * List all the completed snapshots matching the given table name regular expression and snapshot
4276    * name regular expression.
4277    * @param tableNamePattern The compiled table name regular expression to match against
4278    * @param snapshotNamePattern The compiled snapshot name regular expression to match against
4279    * @return returns a List of completed SnapshotDescription
4280    * @throws IOException if a remote or network exception occurs
4281    */
4282   @Override
4283   public List<SnapshotDescription> listTableSnapshots(Pattern tableNamePattern,
4284       Pattern snapshotNamePattern) throws IOException {
4285     TableName[] tableNames = listTableNames(tableNamePattern);
4286 
4287     List<SnapshotDescription> tableSnapshots = new LinkedList<SnapshotDescription>();
4288     List<SnapshotDescription> snapshots = listSnapshots(snapshotNamePattern);
4289 
4290     List<TableName> listOfTableNames = Arrays.asList(tableNames);
4291     for (SnapshotDescription snapshot : snapshots) {
4292       if (listOfTableNames.contains(TableName.valueOf(snapshot.getTable()))) {
4293         tableSnapshots.add(snapshot);
4294       }
4295     }
4296     return tableSnapshots;
4297   }
4298 
4299   /**
4300    * Delete an existing snapshot.
4301    * @param snapshotName name of the snapshot
4302    * @throws IOException if a remote or network exception occurs
4303    */
4304   @Override
4305   public void deleteSnapshot(final byte[] snapshotName) throws IOException {
4306     deleteSnapshot(Bytes.toString(snapshotName));
4307   }
4308 
4309   /**
4310    * Delete an existing snapshot.
4311    * @param snapshotName name of the snapshot
4312    * @throws IOException if a remote or network exception occurs
4313    */
4314   @Override
4315   public void deleteSnapshot(final String snapshotName) throws IOException {
4316     // make sure the snapshot is possibly valid
4317     TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(snapshotName));
4318     // do the delete
4319     executeCallable(new MasterCallable<Void>(getConnection()) {
4320       @Override
4321       public Void call(int callTimeout) throws ServiceException {
4322         HBaseRpcController controller = rpcControllerFactory.newController();
4323         controller.setCallTimeout(callTimeout);
4324         master.deleteSnapshot(controller,
4325           DeleteSnapshotRequest.newBuilder().
4326             setSnapshot(SnapshotDescription.newBuilder().setName(snapshotName).build()).build()
4327         );
4328         return null;
4329       }
4330     });
4331   }
4332 
4333   /**
4334    * Delete existing snapshots whose names match the pattern passed.
4335    * @param regex The regular expression to match against
4336    * @throws IOException if a remote or network exception occurs
4337    */
4338   @Override
4339   public void deleteSnapshots(final String regex) throws IOException {
4340     deleteSnapshots(Pattern.compile(regex));
4341   }
4342 
4343   /**
4344    * Delete existing snapshots whose names match the pattern passed.
4345    * @param pattern pattern for names of the snapshot to match
4346    * @throws IOException if a remote or network exception occurs
4347    */
4348   @Override
4349   public void deleteSnapshots(final Pattern pattern) throws IOException {
4350     List<SnapshotDescription> snapshots = listSnapshots(pattern);
4351     for (final SnapshotDescription snapshot : snapshots) {
4352       try {
4353         internalDeleteSnapshot(snapshot);
4354       } catch (IOException ex) {
4355         LOG.info(
4356           "Failed to delete snapshot " + snapshot.getName() + " for table " + snapshot.getTable(),
4357           ex);
4358       }
4359     }
4360   }
4361 
4362   private void internalDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
4363     executeCallable(new MasterCallable<Void>(getConnection()) {
4364       @Override
4365       public Void call(int callTimeout) throws ServiceException {
4366         HBaseRpcController controller = rpcControllerFactory.newController();
4367         controller.setCallTimeout(callTimeout);
4368         this.master.deleteSnapshot(controller, DeleteSnapshotRequest.newBuilder()
4369           .setSnapshot(snapshot).build());
4370         return null;
4371       }
4372     });
4373   }
4374 
4375   /**
4376    * Delete all existing snapshots matching the given table name regular expression and snapshot
4377    * name regular expression.
4378    * @param tableNameRegex The table name regular expression to match against
4379    * @param snapshotNameRegex The snapshot name regular expression to match against
4380    * @throws IOException if a remote or network exception occurs
4381    */
4382   @Override
4383   public void deleteTableSnapshots(String tableNameRegex, String snapshotNameRegex)
4384       throws IOException {
4385     deleteTableSnapshots(Pattern.compile(tableNameRegex), Pattern.compile(snapshotNameRegex));
4386   }
4387 
4388   /**
4389    * Delete all existing snapshots matching the given table name regular expression and snapshot
4390    * name regular expression.
4391    * @param tableNamePattern The compiled table name regular expression to match against
4392    * @param snapshotNamePattern The compiled snapshot name regular expression to match against
4393    * @throws IOException if a remote or network exception occurs
4394    */
4395   @Override
4396   public void deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern)
4397       throws IOException {
4398     List<SnapshotDescription> snapshots = listTableSnapshots(tableNamePattern, snapshotNamePattern);
4399     for (SnapshotDescription snapshot : snapshots) {
4400       try {
4401         internalDeleteSnapshot(snapshot);
4402         LOG.debug("Successfully deleted snapshot: " + snapshot.getName());
4403       } catch (IOException e) {
4404         LOG.error("Failed to delete snapshot: " + snapshot.getName(), e);
4405       }
4406     }
4407   }
4408 
4409   /**
4410    * Apply the new quota settings.
4411    * @param quota the quota settings
4412    * @throws IOException if a remote or network exception occurs
4413    */
4414   @Override
4415   public void setQuota(final QuotaSettings quota) throws IOException {
4416     executeCallable(new MasterCallable<Void>(getConnection()) {
4417       @Override
4418       public Void call(int callTimeout) throws ServiceException {
4419         HBaseRpcController controller = rpcControllerFactory.newController();
4420         controller.setCallTimeout(callTimeout);
4421         this.master.setQuota(controller, QuotaSettings.buildSetQuotaRequestProto(quota));
4422         return null;
4423       }
4424     });
4425   }
4426 
4427   /**
4428    * Return a Quota Scanner to list the quotas based on the filter.
4429    * @param filter the quota settings filter
4430    * @return the quota scanner
4431    * @throws IOException if a remote or network exception occurs
4432    */
4433   @Override
4434   public QuotaRetriever getQuotaRetriever(final QuotaFilter filter) throws IOException {
4435     return QuotaRetriever.open(conf, filter);
4436   }
4437 
4438   private <V> V executeCallable(MasterCallable<V> callable) throws IOException {
4439     return executeCallable(callable, rpcCallerFactory, operationTimeout, rpcTimeout);
4440   }
4441 
4442   private static <V> V executeCallable(MasterCallable<V> callable,
4443              RpcRetryingCallerFactory rpcCallerFactory, int operationTimeout, int rpcTimeout)
4444       throws IOException {
4445     RpcRetryingCaller<V> caller = rpcCallerFactory.newCaller(rpcTimeout);
4446     try {
4447       return caller.callWithRetries(callable, operationTimeout);
4448     } finally {
4449       callable.close();
4450     }
4451   }
4452 
4453   /**
4454    * Creates and returns a {@link com.google.protobuf.RpcChannel} instance
4455    * connected to the active master.
4456    *
4457    * <p>
4458    * The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published
4459    * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations:
4460    * </p>
4461    *
4462    * <div style="background-color: #cccccc; padding: 2px">
4463    * <blockquote><pre>
4464    * CoprocessorRpcChannel channel = myAdmin.coprocessorService();
4465    * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
4466    * MyCallRequest request = MyCallRequest.newBuilder()
4467    *     ...
4468    *     .build();
4469    * MyCallResponse response = service.myCall(null, request);
4470    * </pre></blockquote></div>
4471    *
4472    * @return A MasterCoprocessorRpcChannel instance
4473    */
4474   @Override
4475   public CoprocessorRpcChannel coprocessorService() {
4476     return new MasterCoprocessorRpcChannel(connection);
4477   }
4478 
4479   /**
4480    * Simple {@link Abortable}, throwing RuntimeException on abort.
4481    */
4482   private static class ThrowableAbortable implements Abortable {
4483 
4484     @Override
4485     public void abort(String why, Throwable e) {
4486       throw new RuntimeException(why, e);
4487     }
4488 
4489     @Override
4490     public boolean isAborted() {
4491       return true;
4492     }
4493   }
4494 
4495   /**
4496    * Creates and returns a {@link com.google.protobuf.RpcChannel} instance
4497    * connected to the passed region server.
4498    *
4499    * <p>
4500    * The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published
4501    * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations:
4502    * </p>
4503    *
4504    * <div style="background-color: #cccccc; padding: 2px">
4505    * <blockquote><pre>
4506    * CoprocessorRpcChannel channel = myAdmin.coprocessorService(serverName);
4507    * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
4508    * MyCallRequest request = MyCallRequest.newBuilder()
4509    *     ...
4510    *     .build();
4511    * MyCallResponse response = service.myCall(null, request);
4512    * </pre></blockquote></div>
4513    *
4514    * @param sn the server name to which the endpoint call is made
4515    * @return A RegionServerCoprocessorRpcChannel instance
4516    */
4517   @Override
4518   public CoprocessorRpcChannel coprocessorService(ServerName sn) {
4519     return new RegionServerCoprocessorRpcChannel(connection, sn);
4520   }
4521 
4522   @Override
4523   public void updateConfiguration(ServerName server) throws IOException {
4524     try {
4525       this.connection.getAdmin(server).updateConfiguration(null,
4526         UpdateConfigurationRequest.getDefaultInstance());
4527     } catch (ServiceException e) {
4528       throw ProtobufUtil.getRemoteException(e);
4529     }
4530   }
4531 
4532   @Override
4533   public void updateConfiguration() throws IOException {
4534     for (ServerName server : this.getClusterStatus().getServers()) {
4535       updateConfiguration(server);
4536     }
4537 
4538     updateConfiguration(this.getClusterStatus().getMaster());
4539 
4540     for (ServerName server : this.getClusterStatus().getBackupMasters()) {
4541       updateConfiguration(server);
4542     }
4543   }
4544 
4545   @Override
4546   public int getMasterInfoPort() throws IOException {
4547     // TODO: Fix!  Reaching into internal implementation!!!!
4548     ConnectionManager.HConnectionImplementation connection =
4549         (ConnectionManager.HConnectionImplementation)this.connection;
4550     ZooKeeperKeepAliveConnection zkw = connection.getKeepAliveZooKeeperWatcher();
4551     try {
4552       return MasterAddressTracker.getMasterInfoPort(zkw);
4553     } catch (KeeperException e) {
4554       throw new IOException("Failed to get master info port from MasterAddressTracker", e);
4555     }
4556   }
4557 
4558   @Override
4559   public long getLastMajorCompactionTimestamp(final TableName tableName) throws IOException {
4560     return executeCallable(new MasterCallable<Long>(getConnection()) {
4561       @Override
4562       public Long call(int callTimeout) throws ServiceException {
4563         HBaseRpcController controller = rpcControllerFactory.newController();
4564         controller.setCallTimeout(callTimeout);
4565         MajorCompactionTimestampRequest req =
4566             MajorCompactionTimestampRequest.newBuilder()
4567                 .setTableName(ProtobufUtil.toProtoTableName(tableName)).build();
4568         return master.getLastMajorCompactionTimestamp(controller, req).getCompactionTimestamp();
4569       }
4570     });
4571   }
4572 
4573   @Override
4574   public long getLastMajorCompactionTimestampForRegion(final byte[] regionName) throws IOException {
4575     return executeCallable(new MasterCallable<Long>(getConnection()) {
4576       @Override
4577       public Long call(int callTimeout) throws ServiceException {
4578         HBaseRpcController controller = rpcControllerFactory.newController();
4579         controller.setCallTimeout(callTimeout);
4580         MajorCompactionTimestampForRegionRequest req =
4581             MajorCompactionTimestampForRegionRequest
4582                 .newBuilder()
4583                 .setRegion(
4584                   RequestConverter
4585                       .buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName)).build();
4586         return master.getLastMajorCompactionTimestampForRegion(controller, req)
4587             .getCompactionTimestamp();
4588       }
4589     });
4590   }
4591 
4592   /**
4593    * Future that waits on a procedure result.
4594    * Returned by the async version of the Admin calls,
4595    * and used internally by the sync calls to wait on the result of the procedure.
4596    */
4597   @InterfaceAudience.Private
4598   @InterfaceStability.Evolving
4599   protected static class ProcedureFuture<V> implements Future<V> {
4600     private ExecutionException exception = null;
4601     private boolean procResultFound = false;
4602     private boolean done = false;
4603     private boolean cancelled = false;
4604     private boolean waitForOpResult = false;
4605     private V result = null;
4606 
4607     private final HBaseAdmin admin;
4608     private final Long procId;
4609 
4610     public ProcedureFuture(final HBaseAdmin admin, final Long procId) {
4611       this.admin = admin;
4612       this.procId = procId;
4613     }
4614 
4615     public ProcedureFuture(final HBaseAdmin admin, final Long procId,
4616         final boolean waitForOpResult) {
4617       this.admin = admin;
4618       this.procId = procId;
4619       this.waitForOpResult = waitForOpResult;
4620     }
4621 
4622     @Override
4623     public boolean cancel(boolean mayInterruptIfRunning) {
4624       AbortProcedureRequest abortProcRequest = AbortProcedureRequest.newBuilder()
4625           .setProcId(procId).setMayInterruptIfRunning(mayInterruptIfRunning).build();
4626       try {
4627         cancelled = abortProcedureResult(abortProcRequest).getIsProcedureAborted();
4628         if (cancelled) {
4629           done = true;
4630         }
4631       } catch (IOException e) {
4632         // Cancell thrown exception for some reason. At this time, we are not sure whether
4633         // the cancell succeeds or fails. We assume that it is failed, but print out a warning
4634         // for debugging purpose.
4635         LOG.warn(
4636           "Cancelling the procedure with procId=" + procId + " throws exception " + e.getMessage(),
4637           e);
4638         cancelled = false;
4639       }
4640       return cancelled;
4641     }
4642 
4643     @Override
4644     public boolean isCancelled() {
4645       return cancelled;
4646     }
4647 
4648     protected AbortProcedureResponse abortProcedureResult(
4649         final AbortProcedureRequest request) throws IOException {
4650       return admin.executeCallable(new MasterCallable<AbortProcedureResponse>(
4651           admin.getConnection()) {
4652         @Override
4653         public AbortProcedureResponse call(int callTimeout) throws ServiceException {
4654           HBaseRpcController controller = admin.getRpcControllerFactory().newController();
4655           controller.setCallTimeout(callTimeout);
4656           return master.abortProcedure(controller, request);
4657         }
4658       });
4659     }
4660 
4661     @Override
4662     public V get() throws InterruptedException, ExecutionException {
4663       // TODO: should we ever spin forever?
4664       throw new UnsupportedOperationException();
4665     }
4666 
4667     @Override
4668     public V get(long timeout, TimeUnit unit)
4669         throws InterruptedException, ExecutionException, TimeoutException {
4670       if (!done) {
4671         long deadlineTs = EnvironmentEdgeManager.currentTime() + unit.toMillis(timeout);
4672         try {
4673           try {
4674             // if the master support procedures, try to wait the result
4675             if (procId != null) {
4676               result = waitProcedureResult(procId, deadlineTs);
4677             }
4678             // if we don't have a proc result, try the compatibility wait
4679             if (!procResultFound || waitForOpResult) {
4680               result = waitOperationResult(deadlineTs);
4681             }
4682             result = postOperationResult(result, deadlineTs);
4683             done = true;
4684           } catch (IOException e) {
4685             result = postOpeartionFailure(e, deadlineTs);
4686             done = true;
4687           }
4688         } catch (IOException e) {
4689           exception = new ExecutionException(e);
4690           done = true;
4691         }
4692       }
4693       if (exception != null) {
4694         throw exception;
4695       }
4696       return result;
4697     }
4698 
4699     @Override
4700     public boolean isDone() {
4701       return done;
4702     }
4703 
4704     protected HBaseAdmin getAdmin() {
4705       return admin;
4706     }
4707 
4708     private V waitProcedureResult(long procId, long deadlineTs)
4709         throws IOException, TimeoutException, InterruptedException {
4710       GetProcedureResultRequest request = GetProcedureResultRequest.newBuilder()
4711           .setProcId(procId)
4712           .build();
4713 
4714       int tries = 0;
4715       IOException serviceEx = null;
4716       while (EnvironmentEdgeManager.currentTime() < deadlineTs) {
4717         GetProcedureResultResponse response = null;
4718         try {
4719           // Try to fetch the result
4720           response = getProcedureResult(request);
4721         } catch (IOException e) {
4722           serviceEx = unwrapException(e);
4723 
4724           // the master may be down
4725           LOG.warn("failed to get the procedure result procId=" + procId, serviceEx);
4726 
4727           // Not much to do, if we have a DoNotRetryIOException
4728           if (serviceEx instanceof DoNotRetryIOException ||
4729               serviceEx instanceof NeedUnmanagedConnectionException) {
4730             // TODO: looks like there is no way to unwrap this exception and get the proper
4731             // UnsupportedOperationException aside from looking at the message.
4732             // anyway, if we fail here we just failover to the compatibility side
4733             // and that is always a valid solution.
4734             LOG.warn("Proc-v2 is unsupported on this master: " + serviceEx.getMessage(), serviceEx);
4735             procResultFound = false;
4736             waitForOpResult = false;
4737             return null;
4738           }
4739         }
4740 
4741         // If the procedure is no longer running, we should have a result
4742         if (response != null && response.getState() != GetProcedureResultResponse.State.RUNNING) {
4743           procResultFound = response.getState() != GetProcedureResultResponse.State.NOT_FOUND;
4744           return convertResult(response);
4745         }
4746 
4747         try {
4748           Thread.sleep(getAdmin().getPauseTime(tries++));
4749         } catch (InterruptedException e) {
4750           throw new InterruptedException(
4751             "Interrupted while waiting for the result of proc " + procId);
4752         }
4753       }
4754       if (serviceEx != null) {
4755         throw serviceEx;
4756       } else {
4757         throw new TimeoutException("The procedure " + procId + " is still running");
4758       }
4759     }
4760 
4761     private static IOException unwrapException(IOException e) {
4762       if (e instanceof RemoteException) {
4763         return ((RemoteException)e).unwrapRemoteException();
4764       }
4765       return e;
4766     }
4767 
4768     protected GetProcedureResultResponse getProcedureResult(final GetProcedureResultRequest request)
4769         throws IOException {
4770       return admin.executeCallable(new MasterCallable<GetProcedureResultResponse>(
4771           admin.getConnection()) {
4772         @Override
4773         public GetProcedureResultResponse call(int callTimeout) throws ServiceException {
4774           return master.getProcedureResult(null, request);
4775         }
4776       });
4777     }
4778 
4779     /**
4780      * Convert the procedure result response to a specified type.
4781      * @param response the procedure result object to parse
4782      * @return the result data of the procedure.
4783      */
4784     protected V convertResult(final GetProcedureResultResponse response) throws IOException {
4785       if (response.hasException()) {
4786         throw ForeignExceptionUtil.toIOException(response.getException());
4787       }
4788       return null;
4789     }
4790 
4791     /**
4792      * Fallback implementation in case the procedure is not supported by the server.
4793      * It should try to wait until the operation is completed.
4794      * @param deadlineTs the timestamp after which this method should throw a TimeoutException
4795      * @return the result data of the operation
4796      */
4797     protected V waitOperationResult(final long deadlineTs)
4798         throws IOException, TimeoutException {
4799       return null;
4800     }
4801 
4802     /**
4803      * Called after the operation is completed and the result fetched.
4804      * this allows to perform extra steps after the procedure is completed.
4805      * it allows to apply transformations to the result that will be returned by get().
4806      * @param result the result of the procedure
4807      * @param deadlineTs the timestamp after which this method should throw a TimeoutException
4808      * @return the result of the procedure, which may be the same as the passed one
4809      */
4810     protected V postOperationResult(final V result, final long deadlineTs)
4811         throws IOException, TimeoutException {
4812       return result;
4813     }
4814 
4815     /**
4816      * Called after the operation is terminated with a failure.
4817      * this allows to perform extra steps after the procedure is terminated.
4818      * it allows to apply transformations to the result that will be returned by get().
4819      * The default implementation will rethrow the exception
4820      * @param exception the exception got from fetching the result
4821      * @param deadlineTs the timestamp after which this method should throw a TimeoutException
4822      * @return the result of the procedure, which may be the same as the passed one
4823      */
4824     protected V postOpeartionFailure(final IOException exception, final long deadlineTs)
4825         throws IOException, TimeoutException {
4826       throw exception;
4827     }
4828 
4829     protected interface WaitForStateCallable {
4830       boolean checkState(int tries) throws IOException;
4831       void throwInterruptedException() throws InterruptedIOException;
4832       void throwTimeoutException(long elapsed) throws TimeoutException;
4833     }
4834 
4835     protected void waitForState(final long deadlineTs, final WaitForStateCallable callable)
4836         throws IOException, TimeoutException {
4837       int tries = 0;
4838       IOException serverEx = null;
4839       long startTime = EnvironmentEdgeManager.currentTime();
4840       while (EnvironmentEdgeManager.currentTime() < deadlineTs) {
4841         serverEx = null;
4842         try {
4843           if (callable.checkState(tries)) {
4844             return;
4845           }
4846         } catch (IOException e) {
4847           serverEx = e;
4848         }
4849         try {
4850           Thread.sleep(getAdmin().getPauseTime(tries++));
4851         } catch (InterruptedException e) {
4852           callable.throwInterruptedException();
4853         }
4854       }
4855       if (serverEx != null) {
4856         throw unwrapException(serverEx);
4857       } else {
4858         callable.throwTimeoutException(EnvironmentEdgeManager.currentTime() - startTime);
4859       }
4860     }
4861   }
4862 
4863   @Override
4864   public List<SecurityCapability> getSecurityCapabilities() throws IOException {
4865     try {
4866       return executeCallable(new MasterCallable<List<SecurityCapability>>(getConnection()) {
4867         @Override
4868         public List<SecurityCapability> call(int callTimeout) throws ServiceException {
4869           HBaseRpcController controller = rpcControllerFactory.newController();
4870           controller.setCallTimeout(callTimeout);
4871           SecurityCapabilitiesRequest req = SecurityCapabilitiesRequest.newBuilder().build();
4872           return ProtobufUtil.toSecurityCapabilityList(
4873             master.getSecurityCapabilities(controller, req).getCapabilitiesList());
4874         }
4875       });
4876     } catch (IOException e) {
4877       if (e instanceof RemoteException) {
4878         e = ((RemoteException)e).unwrapRemoteException();
4879       }
4880       throw e;
4881     }
4882   }
4883 
4884   @Override
4885   public boolean[] setSplitOrMergeEnabled(final boolean enabled, final boolean synchronous,
4886                                           final MasterSwitchType... switchTypes)
4887     throws IOException {
4888     return executeCallable(new MasterCallable<boolean[]>(getConnection()) {
4889       @Override
4890       public boolean[] call(int callTimeout) throws ServiceException {
4891         MasterProtos.SetSplitOrMergeEnabledResponse response = master.setSplitOrMergeEnabled(null,
4892           RequestConverter.buildSetSplitOrMergeEnabledRequest(enabled, synchronous, switchTypes));
4893         boolean[] result = new boolean[switchTypes.length];
4894         int i = 0;
4895         for (Boolean prevValue : response.getPrevValueList()) {
4896           result[i++] = prevValue;
4897         }
4898         return result;
4899       }
4900     });
4901   }
4902 
4903   @Override
4904   public boolean isSplitOrMergeEnabled(final MasterSwitchType switchType) throws IOException {
4905     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
4906       @Override
4907       public Boolean call(int callTimeout) throws ServiceException {
4908         return master.isSplitOrMergeEnabled(null,
4909           RequestConverter.buildIsSplitOrMergeEnabledRequest(switchType)).getEnabled();
4910       }
4911     });
4912   }
4913 
4914   @Override
4915   public List<ServerName> listDeadServers() throws IOException {
4916     return new ArrayList<>(getClusterStatus().getDeadServerNames());
4917   }
4918 
4919   @Override
4920   public List<ServerName> clearDeadServers(final List<ServerName> servers) throws IOException {
4921     if (servers == null || servers.size() == 0) {
4922       throw new IllegalArgumentException("servers cannot be null or empty");
4923     }
4924     return executeCallable(new MasterCallable<List<ServerName>>(getConnection()) {
4925       @Override
4926       public List<ServerName> call(int callTimeout) throws Exception {
4927         ClearDeadServersRequest req = RequestConverter.buildClearDeadServersRequest(servers);
4928         return ProtobufUtil.toServerNameList(
4929                 master.clearDeadServers(null, req).getServerNameList());
4930       }
4931     });
4932   }
4933 
4934 
4935   private RpcControllerFactory getRpcControllerFactory() {
4936     return rpcControllerFactory;
4937   }
4938 }