View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import java.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.net.SocketTimeoutException;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.HashMap;
27  import java.util.LinkedList;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Map.Entry;
31  import java.util.concurrent.atomic.AtomicInteger;
32  import java.util.concurrent.atomic.AtomicReference;
33  import java.util.concurrent.ExecutionException;
34  import java.util.concurrent.Future;
35  import java.util.concurrent.TimeUnit;
36  import java.util.concurrent.TimeoutException;
37  import java.util.regex.Pattern;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.conf.Configuration;
42  import org.apache.hadoop.hbase.Abortable;
43  import org.apache.hadoop.hbase.ClusterStatus;
44  import org.apache.hadoop.hbase.DoNotRetryIOException;
45  import org.apache.hadoop.hbase.HBaseConfiguration;
46  import org.apache.hadoop.hbase.HColumnDescriptor;
47  import org.apache.hadoop.hbase.HConstants;
48  import org.apache.hadoop.hbase.HRegionInfo;
49  import org.apache.hadoop.hbase.HRegionLocation;
50  import org.apache.hadoop.hbase.HTableDescriptor;
51  import org.apache.hadoop.hbase.MasterNotRunningException;
52  import org.apache.hadoop.hbase.MetaTableAccessor;
53  import org.apache.hadoop.hbase.NamespaceDescriptor;
54  import org.apache.hadoop.hbase.NamespaceNotFoundException;
55  import org.apache.hadoop.hbase.NotServingRegionException;
56  import org.apache.hadoop.hbase.ProcedureInfo;
57  import org.apache.hadoop.hbase.RegionException;
58  import org.apache.hadoop.hbase.RegionLocations;
59  import org.apache.hadoop.hbase.ServerName;
60  import org.apache.hadoop.hbase.TableExistsException;
61  import org.apache.hadoop.hbase.TableName;
62  import org.apache.hadoop.hbase.TableNotDisabledException;
63  import org.apache.hadoop.hbase.TableNotEnabledException;
64  import org.apache.hadoop.hbase.TableNotFoundException;
65  import org.apache.hadoop.hbase.UnknownRegionException;
66  import org.apache.hadoop.hbase.ZooKeeperConnectionException;
67  import org.apache.hadoop.hbase.classification.InterfaceAudience;
68  import org.apache.hadoop.hbase.classification.InterfaceStability;
69  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitor;
70  import org.apache.hadoop.hbase.client.MetaScanner.MetaScannerVisitorBase;
71  import org.apache.hadoop.hbase.client.security.SecurityCapability;
72  import org.apache.hadoop.hbase.exceptions.DeserializationException;
73  import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
74  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
75  import org.apache.hadoop.hbase.ipc.MasterCoprocessorRpcChannel;
76  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
77  import org.apache.hadoop.hbase.ipc.RegionServerCoprocessorRpcChannel;
78  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
79  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
80  import org.apache.hadoop.hbase.protobuf.RequestConverter;
81  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.AdminService;
82  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionRequest;
83  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CloseRegionResponse;
84  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.CompactRegionRequest;
85  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.FlushRegionRequest;
86  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoRequest;
87  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse;
88  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.GetRegionInfoResponse.CompactionState;
89  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterRequest;
90  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.RollWALWriterResponse;
91  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.StopServerRequest;
92  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.UpdateConfigurationRequest;
93  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos;
94  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.NameStringPair;
95  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.ProcedureDescription;
96  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
97  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
98  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.TableSchema;
99  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos;
100 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AbortProcedureRequest;
101 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AbortProcedureResponse;
102 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AddColumnRequest;
103 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.AssignRegionRequest;
104 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateNamespaceRequest;
105 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableRequest;
106 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.CreateTableResponse;
107 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteColumnRequest;
108 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteNamespaceRequest;
109 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteSnapshotRequest;
110 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableRequest;
111 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DeleteTableResponse;
112 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableRequest;
113 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DisableTableResponse;
114 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.DispatchMergingRegionsRequest;
115 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableRequest;
116 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.EnableTableResponse;
117 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ExecProcedureRequest;
118 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ExecProcedureResponse;
119 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetClusterStatusRequest;
120 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetCompletedSnapshotsRequest;
121 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetNamespaceDescriptorRequest;
122 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultRequest;
123 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetProcedureResultResponse;
124 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusRequest;
125 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetSchemaAlterStatusResponse;
126 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsRequest;
127 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableDescriptorsResponse;
128 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.GetTableNamesRequest;
129 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsProcedureDoneRequest;
130 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsProcedureDoneResponse;
131 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneRequest;
132 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsRestoreSnapshotDoneResponse;
133 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
134 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
135 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListNamespaceDescriptorsRequest;
136 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListProceduresRequest;
137 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableDescriptorsByNamespaceRequest;
138 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ListTableNamesByNamespaceRequest;
139 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MajorCompactionTimestampForRegionRequest;
140 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MajorCompactionTimestampRequest;
141 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyColumnRequest;
142 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyNamespaceRequest;
143 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ModifyTableRequest;
144 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.MoveRegionRequest;
145 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotRequest;
146 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.RestoreSnapshotResponse;
147 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SecurityCapabilitiesRequest;
148 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetBalancerRunningRequest;
149 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SetNormalizerRunningRequest;
150 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.ShutdownRequest;
151 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotRequest;
152 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.SnapshotResponse;
153 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.StopMasterRequest;
154 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.TruncateTableRequest;
155 import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.UnassignRegionRequest;
156 import org.apache.hadoop.hbase.protobuf.generated.ProcedureProtos;
157 import org.apache.hadoop.hbase.quotas.QuotaFilter;
158 import org.apache.hadoop.hbase.quotas.QuotaRetriever;
159 import org.apache.hadoop.hbase.quotas.QuotaSettings;
160 import org.apache.hadoop.hbase.regionserver.wal.FailedLogCloseException;
161 import org.apache.hadoop.hbase.snapshot.ClientSnapshotDescriptionUtils;
162 import org.apache.hadoop.hbase.snapshot.HBaseSnapshotException;
163 import org.apache.hadoop.hbase.snapshot.RestoreSnapshotException;
164 import org.apache.hadoop.hbase.snapshot.SnapshotCreationException;
165 import org.apache.hadoop.hbase.snapshot.UnknownSnapshotException;
166 import org.apache.hadoop.hbase.util.Addressing;
167 import org.apache.hadoop.hbase.util.Bytes;
168 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
169 import org.apache.hadoop.hbase.util.ForeignExceptionUtil;
170 import org.apache.hadoop.hbase.util.Pair;
171 import org.apache.hadoop.hbase.zookeeper.MasterAddressTracker;
172 import org.apache.hadoop.hbase.zookeeper.ZooKeeperWatcher;
173 import org.apache.hadoop.ipc.RemoteException;
174 import org.apache.hadoop.util.StringUtils;
175 import org.apache.zookeeper.KeeperException;
176 
177 import com.google.common.annotations.VisibleForTesting;
178 import com.google.protobuf.ByteString;
179 import com.google.protobuf.ServiceException;
180 
181 /**
182  * HBaseAdmin is no longer a client API. It is marked InterfaceAudience.Private indicating that
183  * this is an HBase-internal class as defined in
184  * https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/InterfaceClassification.html
185  * There are no guarantees for backwards source / binary compatibility and methods or class can
186  * change or go away without deprecation.
187  * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead of constructing
188  * an HBaseAdmin directly.
189  *
190  * <p>Connection should be an <i>unmanaged</i> connection obtained via
191  * {@link ConnectionFactory#createConnection(Configuration)}
192  *
193  * @see ConnectionFactory
194  * @see Connection
195  * @see Admin
196  */
197 @InterfaceAudience.Private
198 @InterfaceStability.Evolving
199 public class HBaseAdmin implements Admin {
200   private static final Log LOG = LogFactory.getLog(HBaseAdmin.class);
201 
202   private static final String ZK_IDENTIFIER_PREFIX =  "hbase-admin-on-";
203 
204   private ClusterConnection connection;
205 
206   private volatile Configuration conf;
207   private final long pause;
208   private final int numRetries;
209   // Some operations can take a long time such as disable of big table.
210   // numRetries is for 'normal' stuff... Multiply by this factor when
211   // want to wait a long time.
212   private final int retryLongerMultiplier;
213   private final int syncWaitTimeout;
214   private boolean aborted;
215   private boolean cleanupConnectionOnClose = false; // close the connection in close()
216   private boolean closed = false;
217   private int operationTimeout;
218   private int rpcTimeout;
219 
220   private RpcRetryingCallerFactory rpcCallerFactory;
221   private RpcControllerFactory rpcControllerFactory;
222 
223   private NonceGenerator ng;
224 
225   /**
226    * Constructor.
227    * See {@link #HBaseAdmin(Connection connection)}
228    *
229    * @param c Configuration object. Copied internally.
230    * @deprecated Constructing HBaseAdmin objects manually has been deprecated.
231    * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead.
232    */
233   @Deprecated
234   public HBaseAdmin(Configuration c)
235   throws MasterNotRunningException, ZooKeeperConnectionException, IOException {
236     // Will not leak connections, as the new implementation of the constructor
237     // does not throw exceptions anymore.
238     this(ConnectionManager.getConnectionInternal(new Configuration(c)));
239     this.cleanupConnectionOnClose = true;
240   }
241 
242   @Override
243   public int getOperationTimeout() {
244     return operationTimeout;
245   }
246 
247 
248   /**
249    * Constructor for externally managed Connections.
250    * The connection to master will be created when required by admin functions.
251    *
252    * @param connection The Connection instance to use
253    * @throws MasterNotRunningException
254    * @throws ZooKeeperConnectionException are not
255    *  thrown anymore but kept into the interface for backward api compatibility
256    * @deprecated Constructing HBaseAdmin objects manually has been deprecated.
257    * Use {@link Connection#getAdmin()} to obtain an instance of {@link Admin} instead.
258    */
259   @Deprecated
260   public HBaseAdmin(Connection connection)
261       throws MasterNotRunningException, ZooKeeperConnectionException {
262     this((ClusterConnection)connection);
263   }
264 
265   HBaseAdmin(ClusterConnection connection) {
266     this.conf = connection.getConfiguration();
267     this.connection = connection;
268 
269     // TODO: receive ConnectionConfiguration here rather than re-parsing these configs every time.
270     this.pause = this.conf.getLong(HConstants.HBASE_CLIENT_PAUSE,
271         HConstants.DEFAULT_HBASE_CLIENT_PAUSE);
272     this.numRetries = this.conf.getInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER,
273         HConstants.DEFAULT_HBASE_CLIENT_RETRIES_NUMBER);
274     this.retryLongerMultiplier = this.conf.getInt(
275         "hbase.client.retries.longer.multiplier", 10);
276     this.operationTimeout = this.conf.getInt(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT,
277         HConstants.DEFAULT_HBASE_CLIENT_OPERATION_TIMEOUT);
278     this.rpcTimeout = this.conf.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
279         HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
280     this.syncWaitTimeout = this.conf.getInt(
281       "hbase.client.sync.wait.timeout.msec", 10 * 60000); // 10min
282 
283     this.rpcCallerFactory = connection.getRpcRetryingCallerFactory();
284     this.rpcControllerFactory = connection.getRpcControllerFactory();
285 
286     this.ng = this.connection.getNonceGenerator();
287   }
288 
289   @Override
290   public void abort(String why, Throwable e) {
291     // Currently does nothing but throw the passed message and exception
292     this.aborted = true;
293     throw new RuntimeException(why, e);
294   }
295 
296   @Override
297   public boolean isAborted(){
298     return this.aborted;
299   }
300 
301   /**
302    * Abort a procedure
303    * @param procId ID of the procedure to abort
304    * @param mayInterruptIfRunning if the proc completed at least one step, should it be aborted?
305    * @return true if aborted, false if procedure already completed or does not exist
306    * @throws IOException
307    */
308   @Override
309   public boolean abortProcedure(
310       final long procId,
311       final boolean mayInterruptIfRunning) throws IOException {
312     Future<Boolean> future = abortProcedureAsync(procId, mayInterruptIfRunning);
313     try {
314       return future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
315     } catch (InterruptedException e) {
316       throw new InterruptedIOException("Interrupted when waiting for procedure to be cancelled");
317     } catch (TimeoutException e) {
318       throw new TimeoutIOException(e);
319     } catch (ExecutionException e) {
320       if (e.getCause() instanceof IOException) {
321         throw (IOException)e.getCause();
322       } else {
323         throw new IOException(e.getCause());
324       }
325     }
326   }
327 
328   /**
329    * Abort a procedure but does not block and wait for it be completely removed.
330    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
331    * It may throw ExecutionException if there was an error while executing the operation
332    * or TimeoutException in case the wait timeout was not long enough to allow the
333    * operation to complete.
334    *
335    * @param procId ID of the procedure to abort
336    * @param mayInterruptIfRunning if the proc completed at least one step, should it be aborted?
337    * @return true if aborted, false if procedure already completed or does not exist
338    * @throws IOException
339    */
340   @Override
341   public Future<Boolean> abortProcedureAsync(
342       final long procId,
343       final boolean mayInterruptIfRunning) throws IOException {
344     Boolean abortProcResponse = executeCallable(
345       new MasterCallable<AbortProcedureResponse>(getConnection()) {
346         @Override
347         public AbortProcedureResponse call(int callTimeout) throws ServiceException {
348           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
349           controller.setCallTimeout(callTimeout);
350           AbortProcedureRequest abortProcRequest =
351               AbortProcedureRequest.newBuilder().setProcId(procId).build();
352           return master.abortProcedure(controller, abortProcRequest);
353         }
354       }).getIsProcedureAborted();
355 
356     AbortProcedureFuture abortProcFuture =
357         new AbortProcedureFuture(this, procId, abortProcResponse);
358     return abortProcFuture;
359   }
360 
361   private static class AbortProcedureFuture extends ProcedureFuture<Boolean> {
362     private boolean isAbortInProgress;
363 
364     public AbortProcedureFuture(
365         final HBaseAdmin admin,
366         final Long procId,
367         final Boolean abortProcResponse) {
368       super(admin, procId);
369       this.isAbortInProgress = abortProcResponse;
370     }
371 
372     @Override
373     public Boolean get(long timeout, TimeUnit unit)
374         throws InterruptedException, ExecutionException, TimeoutException {
375       if (!this.isAbortInProgress) {
376         return false;
377       }
378       super.get(timeout, unit);
379       return true;
380     }
381   }
382 
383   /** @return HConnection used by this object. */
384   @Override
385   public HConnection getConnection() {
386     return connection;
387   }
388 
389   /** @return - true if the master server is running. Throws an exception
390    *  otherwise.
391    * @throws ZooKeeperConnectionException
392    * @throws MasterNotRunningException
393    * @deprecated this has been deprecated without a replacement
394    */
395   @Deprecated
396   public boolean isMasterRunning()
397   throws MasterNotRunningException, ZooKeeperConnectionException {
398     return connection.isMasterRunning();
399   }
400 
401   /**
402    * @param tableName Table to check.
403    * @return True if table exists already.
404    * @throws IOException
405    */
406   @Override
407   public boolean tableExists(final TableName tableName) throws IOException {
408     return MetaTableAccessor.tableExists(connection, tableName);
409   }
410 
411   public boolean tableExists(final byte[] tableName)
412   throws IOException {
413     return tableExists(TableName.valueOf(tableName));
414   }
415 
416   public boolean tableExists(final String tableName)
417   throws IOException {
418     return tableExists(TableName.valueOf(tableName));
419   }
420 
421   @Override
422   public HTableDescriptor[] listTables() throws IOException {
423     return listTables((Pattern)null, false);
424   }
425 
426   @Override
427   public HTableDescriptor[] listTables(Pattern pattern) throws IOException {
428     return listTables(pattern, false);
429   }
430 
431   @Override
432   public HTableDescriptor[] listTables(String regex) throws IOException {
433     return listTables(Pattern.compile(regex), false);
434   }
435 
436   @Override
437   public HTableDescriptor[] listTables(final Pattern pattern, final boolean includeSysTables)
438       throws IOException {
439     return executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
440       @Override
441       public HTableDescriptor[] call(int callTimeout) throws ServiceException {
442         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
443         controller.setCallTimeout(callTimeout);
444         GetTableDescriptorsRequest req =
445             RequestConverter.buildGetTableDescriptorsRequest(pattern, includeSysTables);
446         return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(controller, req));
447       }
448     });
449   }
450 
451   @Override
452   public HTableDescriptor[] listTables(String regex, boolean includeSysTables)
453       throws IOException {
454     return listTables(Pattern.compile(regex), includeSysTables);
455   }
456 
457   /**
458    * List all of the names of userspace tables.
459    * @return String[] table names
460    * @throws IOException if a remote or network exception occurs
461    * @deprecated Use {@link Admin#listTableNames()} instead
462    */
463   @Deprecated
464   public String[] getTableNames() throws IOException {
465     TableName[] tableNames = listTableNames();
466     String result[] = new String[tableNames.length];
467     for (int i = 0; i < tableNames.length; i++) {
468       result[i] = tableNames[i].getNameAsString();
469     }
470     return result;
471   }
472 
473   /**
474    * List all of the names of userspace tables matching the given regular expression.
475    * @param pattern The regular expression to match against
476    * @return String[] table names
477    * @throws IOException if a remote or network exception occurs
478    * @deprecated Use {@link Admin#listTableNames(Pattern)} instead.
479    */
480   @Deprecated
481   public String[] getTableNames(Pattern pattern) throws IOException {
482     TableName[] tableNames = listTableNames(pattern);
483     String result[] = new String[tableNames.length];
484     for (int i = 0; i < tableNames.length; i++) {
485       result[i] = tableNames[i].getNameAsString();
486     }
487     return result;
488   }
489 
490   /**
491    * List all of the names of userspace tables matching the given regular expression.
492    * @param regex The regular expression to match against
493    * @return String[] table names
494    * @throws IOException if a remote or network exception occurs
495    * @deprecated Use {@link Admin#listTableNames(Pattern)} instead.
496    */
497   @Deprecated
498   public String[] getTableNames(String regex) throws IOException {
499     return getTableNames(Pattern.compile(regex));
500   }
501 
502   @Override
503   public TableName[] listTableNames() throws IOException {
504     return listTableNames((Pattern)null, false);
505   }
506 
507   @Override
508   public TableName[] listTableNames(Pattern pattern) throws IOException {
509     return listTableNames(pattern, false);
510   }
511 
512   @Override
513   public TableName[] listTableNames(String regex) throws IOException {
514     return listTableNames(Pattern.compile(regex), false);
515   }
516 
517   @Override
518   public TableName[] listTableNames(final Pattern pattern, final boolean includeSysTables)
519       throws IOException {
520     return executeCallable(new MasterCallable<TableName[]>(getConnection()) {
521       @Override
522       public TableName[] call(int callTimeout) throws ServiceException {
523         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
524         controller.setCallTimeout(callTimeout);
525         GetTableNamesRequest req =
526             RequestConverter.buildGetTableNamesRequest(pattern, includeSysTables);
527         return ProtobufUtil.getTableNameArray(master.getTableNames(controller, req)
528             .getTableNamesList());
529       }
530     });
531   }
532 
533   @Override
534   public TableName[] listTableNames(final String regex, final boolean includeSysTables)
535       throws IOException {
536     return listTableNames(Pattern.compile(regex), includeSysTables);
537   }
538 
539   /**
540    * Method for getting the tableDescriptor
541    * @param tableName as a byte []
542    * @return the tableDescriptor
543    * @throws TableNotFoundException
544    * @throws IOException if a remote or network exception occurs
545    */
546   @Override
547   public HTableDescriptor getTableDescriptor(final TableName tableName)
548   throws TableNotFoundException, IOException {
549      return getTableDescriptor(tableName, getConnection(), rpcCallerFactory, rpcControllerFactory,
550        operationTimeout, rpcTimeout);
551   }
552 
553   static HTableDescriptor getTableDescriptor(final TableName tableName, HConnection connection,
554       RpcRetryingCallerFactory rpcCallerFactory, final RpcControllerFactory rpcControllerFactory,
555          int operationTimeout, int rpcTimeout) throws TableNotFoundException, IOException {
556 
557       if (tableName == null) return null;
558       HTableDescriptor htd = executeCallable(new MasterCallable<HTableDescriptor>(connection) {
559         @Override
560         public HTableDescriptor call(int callTimeout) throws ServiceException {
561           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
562           controller.setCallTimeout(callTimeout);
563           GetTableDescriptorsResponse htds;
564           GetTableDescriptorsRequest req =
565                   RequestConverter.buildGetTableDescriptorsRequest(tableName);
566           htds = master.getTableDescriptors(controller, req);
567 
568           if (!htds.getTableSchemaList().isEmpty()) {
569             return HTableDescriptor.convert(htds.getTableSchemaList().get(0));
570           }
571           return null;
572         }
573       }, rpcCallerFactory, operationTimeout, rpcTimeout);
574       if (htd != null) {
575         return htd;
576       }
577       throw new TableNotFoundException(tableName.getNameAsString());
578   }
579 
580   public HTableDescriptor getTableDescriptor(final byte[] tableName)
581   throws TableNotFoundException, IOException {
582     return getTableDescriptor(TableName.valueOf(tableName));
583   }
584 
585   private long getPauseTime(int tries) {
586     int triesCount = tries;
587     if (triesCount >= HConstants.RETRY_BACKOFF.length) {
588       triesCount = HConstants.RETRY_BACKOFF.length - 1;
589     }
590     return this.pause * HConstants.RETRY_BACKOFF[triesCount];
591   }
592 
593   /**
594    * Creates a new table.
595    * Synchronous operation.
596    *
597    * @param desc table descriptor for table
598    *
599    * @throws IllegalArgumentException if the table name is reserved
600    * @throws MasterNotRunningException if master is not running
601    * @throws TableExistsException if table already exists (If concurrent
602    * threads, the table may have been created between test-for-existence
603    * and attempt-at-creation).
604    * @throws IOException if a remote or network exception occurs
605    */
606   @Override
607   public void createTable(HTableDescriptor desc)
608   throws IOException {
609     createTable(desc, null);
610   }
611 
612   /**
613    * Creates a new table with the specified number of regions.  The start key
614    * specified will become the end key of the first region of the table, and
615    * the end key specified will become the start key of the last region of the
616    * table (the first region has a null start key and the last region has a
617    * null end key).
618    *
619    * BigInteger math will be used to divide the key range specified into
620    * enough segments to make the required number of total regions.
621    *
622    * Synchronous operation.
623    *
624    * @param desc table descriptor for table
625    * @param startKey beginning of key range
626    * @param endKey end of key range
627    * @param numRegions the total number of regions to create
628    *
629    * @throws IllegalArgumentException if the table name is reserved
630    * @throws MasterNotRunningException if master is not running
631    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
632    * threads, the table may have been created between test-for-existence
633    * and attempt-at-creation).
634    * @throws IOException
635    */
636   @Override
637   public void createTable(HTableDescriptor desc, byte [] startKey,
638       byte [] endKey, int numRegions)
639   throws IOException {
640     if(numRegions < 3) {
641       throw new IllegalArgumentException("Must create at least three regions");
642     } else if(Bytes.compareTo(startKey, endKey) >= 0) {
643       throw new IllegalArgumentException("Start key must be smaller than end key");
644     }
645     if (numRegions == 3) {
646       createTable(desc, new byte[][]{startKey, endKey});
647       return;
648     }
649     byte [][] splitKeys = Bytes.split(startKey, endKey, numRegions - 3);
650     if(splitKeys == null || splitKeys.length != numRegions - 1) {
651       throw new IllegalArgumentException("Unable to split key range into enough regions");
652     }
653     createTable(desc, splitKeys);
654   }
655 
656   /**
657    * Creates a new table with an initial set of empty regions defined by the
658    * specified split keys.  The total number of regions created will be the
659    * number of split keys plus one. Synchronous operation.
660    * Note : Avoid passing empty split key.
661    *
662    * @param desc table descriptor for table
663    * @param splitKeys array of split keys for the initial regions of the table
664    *
665    * @throws IllegalArgumentException if the table name is reserved, if the split keys
666    * are repeated and if the split key has empty byte array.
667    * @throws MasterNotRunningException if master is not running
668    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
669    * threads, the table may have been created between test-for-existence
670    * and attempt-at-creation).
671    * @throws IOException
672    */
673   @Override
674   public void createTable(final HTableDescriptor desc, byte [][] splitKeys)
675       throws IOException {
676     Future<Void> future = createTableAsyncV2(desc, splitKeys);
677     try {
678       // TODO: how long should we wait? spin forever?
679       future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
680     } catch (InterruptedException e) {
681       throw new InterruptedIOException("Interrupted when waiting" +
682           " for table to be enabled; meta scan was done");
683     } catch (TimeoutException e) {
684       throw new TimeoutIOException(e);
685     } catch (ExecutionException e) {
686       if (e.getCause() instanceof IOException) {
687         throw (IOException)e.getCause();
688       } else {
689         throw new IOException(e.getCause());
690       }
691     }
692   }
693 
694   /**
695    * Creates a new table but does not block and wait for it to come online.
696    * Asynchronous operation.  To check if the table exists, use
697    * {@link #isTableAvailable} -- it is not safe to create an HTable
698    * instance to this table before it is available.
699    * Note : Avoid passing empty split key.
700    * @param desc table descriptor for table
701    *
702    * @throws IllegalArgumentException Bad table name, if the split keys
703    * are repeated and if the split key has empty byte array.
704    * @throws MasterNotRunningException if master is not running
705    * @throws org.apache.hadoop.hbase.TableExistsException if table already exists (If concurrent
706    * threads, the table may have been created between test-for-existence
707    * and attempt-at-creation).
708    * @throws IOException
709    */
710   @Override
711   public void createTableAsync(final HTableDescriptor desc, final byte [][] splitKeys)
712       throws IOException {
713     createTableAsyncV2(desc, splitKeys);
714   }
715 
716   /**
717    * Creates a new table but does not block and wait for it to come online.
718    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
719    * It may throw ExecutionException if there was an error while executing the operation
720    * or TimeoutException in case the wait timeout was not long enough to allow the
721    * operation to complete.
722    *
723    * @param desc table descriptor for table
724    * @param splitKeys keys to check if the table has been created with all split keys
725    * @throws IllegalArgumentException Bad table name, if the split keys
726    *    are repeated and if the split key has empty byte array.
727    * @throws IOException if a remote or network exception occurs
728    * @return the result of the async creation. You can use Future.get(long, TimeUnit)
729    *    to wait on the operation to complete.
730    */
731   // TODO: This should be called Async but it will break binary compatibility
732   private Future<Void> createTableAsyncV2(final HTableDescriptor desc, final byte[][] splitKeys)
733       throws IOException {
734     if (desc.getTableName() == null) {
735       throw new IllegalArgumentException("TableName cannot be null");
736     }
737     if (splitKeys != null && splitKeys.length > 0) {
738       Arrays.sort(splitKeys, Bytes.BYTES_COMPARATOR);
739       // Verify there are no duplicate split keys
740       byte[] lastKey = null;
741       for (byte[] splitKey : splitKeys) {
742         if (Bytes.compareTo(splitKey, HConstants.EMPTY_BYTE_ARRAY) == 0) {
743           throw new IllegalArgumentException(
744               "Empty split key must not be passed in the split keys.");
745         }
746         if (lastKey != null && Bytes.equals(splitKey, lastKey)) {
747           throw new IllegalArgumentException("All split keys must be unique, " +
748             "found duplicate: " + Bytes.toStringBinary(splitKey) +
749             ", " + Bytes.toStringBinary(lastKey));
750         }
751         lastKey = splitKey;
752       }
753     }
754 
755     CreateTableResponse response = executeCallable(
756       new MasterCallable<CreateTableResponse>(getConnection()) {
757         @Override
758         public CreateTableResponse call(int callTimeout) throws ServiceException {
759           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
760           controller.setCallTimeout(callTimeout);
761           controller.setPriority(desc.getTableName());
762           CreateTableRequest request = RequestConverter.buildCreateTableRequest(
763             desc, splitKeys, ng.getNonceGroup(), ng.newNonce());
764           return master.createTable(controller, request);
765         }
766       });
767     return new CreateTableFuture(this, desc, splitKeys, response);
768   }
769 
770   private static class CreateTableFuture extends ProcedureFuture<Void> {
771     private final HTableDescriptor desc;
772     private final byte[][] splitKeys;
773 
774     public CreateTableFuture(final HBaseAdmin admin, final HTableDescriptor desc,
775         final byte[][] splitKeys, final CreateTableResponse response) {
776       super(admin, (response != null && response.hasProcId()) ? response.getProcId() : null, true);
777       this.splitKeys = splitKeys;
778       this.desc = desc;
779     }
780 
781     @Override
782     protected Void waitOperationResult(final long deadlineTs)
783         throws IOException, TimeoutException {
784       waitForTableEnabled(deadlineTs);
785       waitForAllRegionsOnline(deadlineTs);
786       return null;
787     }
788 
789     @Override
790     protected Void postOperationResult(final Void result, final long deadlineTs)
791         throws IOException, TimeoutException {
792       LOG.info("Created " + desc.getTableName());
793       return result;
794     }
795 
796     private void waitForTableEnabled(final long deadlineTs)
797         throws IOException, TimeoutException {
798       waitForState(deadlineTs, new WaitForStateCallable() {
799         @Override
800         public boolean checkState(int tries) throws IOException {
801           try {
802             if (getAdmin().isTableAvailable(desc.getTableName())) {
803               return true;
804             }
805           } catch (TableNotFoundException tnfe) {
806             LOG.debug("Table "+ desc.getTableName() +" was not enabled, sleeping. tries="+  tries);
807           }
808           return false;
809         }
810 
811         @Override
812         public void throwInterruptedException() throws InterruptedIOException {
813           throw new InterruptedIOException("Interrupted when waiting for table " +
814               desc.getTableName() + " to be enabled");
815         }
816 
817         @Override
818         public void throwTimeoutException(long elapsedTime) throws TimeoutException {
819           throw new TimeoutException("Table " + desc.getTableName() +
820             " not enabled after " + elapsedTime + "msec");
821         }
822       });
823     }
824 
825     private void waitForAllRegionsOnline(final long deadlineTs)
826         throws IOException, TimeoutException {
827       final AtomicInteger actualRegCount = new AtomicInteger(0);
828       final MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
829         @Override
830         public boolean processRow(Result rowResult) throws IOException {
831           RegionLocations list = MetaTableAccessor.getRegionLocations(rowResult);
832           if (list == null) {
833             LOG.warn("No serialized HRegionInfo in " + rowResult);
834             return true;
835           }
836           HRegionLocation l = list.getRegionLocation();
837           if (l == null) {
838             return true;
839           }
840           if (!l.getRegionInfo().getTable().equals(desc.getTableName())) {
841             return false;
842           }
843           if (l.getRegionInfo().isOffline() || l.getRegionInfo().isSplit()) return true;
844           HRegionLocation[] locations = list.getRegionLocations();
845           for (HRegionLocation location : locations) {
846             if (location == null) continue;
847             ServerName serverName = location.getServerName();
848             // Make sure that regions are assigned to server
849             if (serverName != null && serverName.getHostAndPort() != null) {
850               actualRegCount.incrementAndGet();
851             }
852           }
853           return true;
854         }
855       };
856 
857       int tries = 0;
858       IOException serverEx = null;
859       int numRegs = (splitKeys == null ? 1 : splitKeys.length + 1) * desc.getRegionReplication();
860       while (EnvironmentEdgeManager.currentTime() < deadlineTs) {
861         actualRegCount.set(0);
862         MetaScanner.metaScan(getAdmin().getConnection(), visitor, desc.getTableName());
863         if (actualRegCount.get() == numRegs) {
864           // all the regions are online
865           return;
866         }
867 
868         try {
869           Thread.sleep(getAdmin().getPauseTime(tries++));
870         } catch (InterruptedException e) {
871           throw new InterruptedIOException("Interrupted when opening" +
872             " regions; " + actualRegCount.get() + " of " + numRegs +
873             " regions processed so far");
874         }
875       }
876       throw new TimeoutException("Only " + actualRegCount.get() +
877               " of " + numRegs + " regions are online; retries exhausted.");
878     }
879   }
880 
881   public void deleteTable(final String tableName) throws IOException {
882     deleteTable(TableName.valueOf(tableName));
883   }
884 
885   public void deleteTable(final byte[] tableName) throws IOException {
886     deleteTable(TableName.valueOf(tableName));
887   }
888 
889   /**
890    * Deletes a table.
891    * Synchronous operation.
892    *
893    * @param tableName name of table to delete
894    * @throws IOException if a remote or network exception occurs
895    */
896   @Override
897   public void deleteTable(final TableName tableName) throws IOException {
898     Future<Void> future = deleteTableAsyncV2(tableName);
899     try {
900       future.get(syncWaitTimeout, TimeUnit.MILLISECONDS);
901     } catch (InterruptedException e) {
902       throw new InterruptedIOException("Interrupted when waiting for table to be deleted");
903     } catch (TimeoutException e) {
904       throw new TimeoutIOException(e);
905     } catch (ExecutionException e) {
906       if (e.getCause() instanceof IOException) {
907         throw (IOException)e.getCause();
908       } else {
909         throw new IOException(e.getCause());
910       }
911     }
912   }
913 
914   /**
915    * Deletes the table but does not block and wait for it be completely removed.
916    * You can use Future.get(long, TimeUnit) to wait on the operation to complete.
917    * It may throw ExecutionException if there was an error while executing the operation
918    * or TimeoutException in case the wait timeout was not long enough to allow the
919    * operation to complete.
920    *
921    * @param desc table descriptor for table
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           PayloadCarryingRpcController 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           PayloadCarryingRpcController 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           PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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       PayloadCarryingRpcController 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     PayloadCarryingRpcController 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     PayloadCarryingRpcController 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     PayloadCarryingRpcController 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     PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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         PayloadCarryingRpcController 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   public boolean isNormalizerEnabled() throws IOException {
2464     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2465       @Override
2466       public Boolean call(int callTimeout) throws ServiceException {
2467         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2468         controller.setCallTimeout(callTimeout);
2469 
2470         return master.isNormalizerEnabled(controller,
2471           RequestConverter.buildIsNormalizerEnabledRequest()).getEnabled();
2472       }
2473     });
2474   }
2475 
2476   /**
2477    * Turn region normalizer on or off.
2478    *
2479    * @return Previous normalizer value
2480    */
2481   public boolean setNormalizerRunning(final boolean on) throws IOException {
2482     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2483       @Override
2484       public Boolean call(int callTimeout) throws ServiceException {
2485         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2486         controller.setCallTimeout(callTimeout);
2487 
2488         SetNormalizerRunningRequest req =
2489           RequestConverter.buildSetNormalizerRunningRequest(on);
2490         return master.setNormalizerRunning(controller, req).getPrevNormalizerValue();
2491       }
2492     });
2493   }
2494 
2495   /**
2496    * Enable/Disable the catalog janitor
2497    * @param enable if true enables the catalog janitor
2498    * @return the previous state
2499    * @throws MasterNotRunningException
2500    */
2501   @Override
2502   public boolean enableCatalogJanitor(final boolean enable)
2503       throws IOException {
2504     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2505       @Override
2506       public Boolean call(int callTimeout) throws ServiceException {
2507         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2508         controller.setCallTimeout(callTimeout);
2509 
2510         return master.enableCatalogJanitor(controller,
2511           RequestConverter.buildEnableCatalogJanitorRequest(enable)).getPrevValue();
2512       }
2513     });
2514   }
2515 
2516   /**
2517    * Ask for a scan of the catalog table
2518    * @return the number of entries cleaned
2519    * @throws MasterNotRunningException
2520    */
2521   @Override
2522   public int runCatalogScan() throws IOException {
2523     return executeCallable(new MasterCallable<Integer>(getConnection()) {
2524       @Override
2525       public Integer call(int callTimeout) throws ServiceException {
2526         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2527         controller.setCallTimeout(callTimeout);
2528 
2529         return master.runCatalogScan(controller,
2530           RequestConverter.buildCatalogScanRequest()).getScanResult();
2531       }
2532     });
2533   }
2534 
2535   /**
2536    * Query on the catalog janitor state (Enabled/Disabled?)
2537    * @throws org.apache.hadoop.hbase.MasterNotRunningException
2538    */
2539   @Override
2540   public boolean isCatalogJanitorEnabled() throws IOException {
2541     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
2542       @Override
2543       public Boolean call(int callTimeout) throws ServiceException {
2544         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2545         controller.setCallTimeout(callTimeout);
2546 
2547         return master.isCatalogJanitorEnabled(controller,
2548           RequestConverter.buildIsCatalogJanitorEnabledRequest()).getValue();
2549       }
2550     });
2551   }
2552 
2553   private boolean isEncodedRegionName(byte[] regionName) throws IOException {
2554     try {
2555       HRegionInfo.parseRegionName(regionName);
2556       return false;
2557     } catch (IOException e) {
2558       if (StringUtils.stringifyException(e)
2559         .contains(HRegionInfo.INVALID_REGION_NAME_FORMAT_MESSAGE)) {
2560         return true;
2561       }
2562       throw e;
2563     }
2564   }
2565 
2566   /**
2567    * Merge two regions. Asynchronous operation.
2568    * @param nameOfRegionA encoded or full name of region a
2569    * @param nameOfRegionB encoded or full name of region b
2570    * @param forcible true if do a compulsory merge, otherwise we will only merge
2571    *          two adjacent regions
2572    * @throws IOException
2573    */
2574   @Override
2575   public void mergeRegions(final byte[] nameOfRegionA,
2576       final byte[] nameOfRegionB, final boolean forcible)
2577       throws IOException {
2578     final byte[] encodedNameOfRegionA = isEncodedRegionName(nameOfRegionA) ?
2579       nameOfRegionA : HRegionInfo.encodeRegionName(nameOfRegionA).getBytes();
2580     final byte[] encodedNameOfRegionB = isEncodedRegionName(nameOfRegionB) ?
2581       nameOfRegionB : HRegionInfo.encodeRegionName(nameOfRegionB).getBytes();
2582 
2583     Pair<HRegionInfo, ServerName> pair = getRegion(nameOfRegionA);
2584     if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID)
2585       throw new IllegalArgumentException("Can't invoke merge on non-default regions directly");
2586     pair = getRegion(nameOfRegionB);
2587     if (pair != null && pair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID)
2588       throw new IllegalArgumentException("Can't invoke merge on non-default regions directly");
2589     executeCallable(new MasterCallable<Void>(getConnection()) {
2590       @Override
2591       public Void call(int callTimeout) throws ServiceException {
2592         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2593         controller.setCallTimeout(callTimeout);
2594 
2595         try {
2596           DispatchMergingRegionsRequest request = RequestConverter
2597               .buildDispatchMergingRegionsRequest(encodedNameOfRegionA,
2598                 encodedNameOfRegionB, forcible);
2599           master.dispatchMergingRegions(controller, request);
2600         } catch (DeserializationException de) {
2601           LOG.error("Could not parse destination server name: " + de);
2602         }
2603         return null;
2604       }
2605     });
2606   }
2607 
2608   /**
2609    * {@inheritDoc}
2610    */
2611   @Override
2612   public void split(final TableName tableName)
2613     throws IOException {
2614     split(tableName, null);
2615   }
2616 
2617   /**
2618    * {@inheritDoc}
2619    */
2620   @Override
2621   public void splitRegion(final byte[] regionName)
2622     throws IOException {
2623     splitRegion(regionName, null);
2624   }
2625 
2626   /**
2627    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName)} or {@link #splitRegion
2628    * (byte[])} instead.
2629    */
2630   @Deprecated
2631   public void split(final String tableNameOrRegionName)
2632   throws IOException, InterruptedException {
2633     split(Bytes.toBytes(tableNameOrRegionName));
2634   }
2635 
2636   /**
2637    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName)} or {@link #splitRegion
2638    * (byte[])} instead.
2639    */
2640   @Deprecated
2641   public void split(final byte[] tableNameOrRegionName)
2642   throws IOException, InterruptedException {
2643     split(tableNameOrRegionName, null);
2644   }
2645 
2646   /**
2647    * {@inheritDoc}
2648    */
2649   @Override
2650   public void split(final TableName tableName, final byte [] splitPoint)
2651   throws IOException {
2652     ZooKeeperWatcher zookeeper = null;
2653     try {
2654       checkTableExists(tableName);
2655       zookeeper = new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
2656         new ThrowableAbortable());
2657       List<Pair<HRegionInfo, ServerName>> pairs =
2658         MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName);
2659       for (Pair<HRegionInfo, ServerName> pair: pairs) {
2660         // May not be a server for a particular row
2661         if (pair.getSecond() == null) continue;
2662         HRegionInfo r = pair.getFirst();
2663         // check for parents
2664         if (r.isSplitParent()) continue;
2665         // if a split point given, only split that particular region
2666         if (r.getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID ||
2667            (splitPoint != null && !r.containsRow(splitPoint))) continue;
2668         // call out to region server to do split now
2669         split(pair.getSecond(), pair.getFirst(), splitPoint);
2670       }
2671     } finally {
2672       if (zookeeper != null) {
2673         zookeeper.close();
2674       }
2675     }
2676   }
2677 
2678   /**
2679    * {@inheritDoc}
2680    */
2681   @Override
2682   public void splitRegion(final byte[] regionName, final byte [] splitPoint)
2683   throws IOException {
2684     Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionName);
2685     if (regionServerPair == null) {
2686       throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName));
2687     }
2688     if (regionServerPair.getFirst() != null &&
2689         regionServerPair.getFirst().getReplicaId() != HRegionInfo.DEFAULT_REPLICA_ID) {
2690       throw new IllegalArgumentException("Can't split replicas directly. "
2691           + "Replicas are auto-split when their primary is split.");
2692     }
2693     if (regionServerPair.getSecond() == null) {
2694       throw new NoServerForRegionException(Bytes.toStringBinary(regionName));
2695     }
2696     split(regionServerPair.getSecond(), regionServerPair.getFirst(), splitPoint);
2697   }
2698 
2699   /**
2700    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName,
2701    * byte[])} or {@link #splitRegion(byte[], byte[])} instead.
2702    */
2703   @Deprecated
2704   public void split(final String tableNameOrRegionName,
2705     final String splitPoint) throws IOException {
2706     split(Bytes.toBytes(tableNameOrRegionName), Bytes.toBytes(splitPoint));
2707   }
2708 
2709   /**
2710    * @deprecated Use {@link #split(org.apache.hadoop.hbase.TableName,
2711    * byte[])} or {@link #splitRegion(byte[], byte[])} instead.
2712    */
2713   @Deprecated
2714   public void split(final byte[] tableNameOrRegionName,
2715       final byte [] splitPoint) throws IOException {
2716     try {
2717       splitRegion(tableNameOrRegionName, splitPoint);
2718     } catch (IllegalArgumentException e) {
2719       // Bad region, try table
2720       split(TableName.valueOf(tableNameOrRegionName), splitPoint);
2721     }
2722   }
2723 
2724   @VisibleForTesting
2725   public void split(final ServerName sn, final HRegionInfo hri,
2726       byte[] splitPoint) throws IOException {
2727     if (hri.getStartKey() != null && splitPoint != null &&
2728          Bytes.compareTo(hri.getStartKey(), splitPoint) == 0) {
2729        throw new IOException("should not give a splitkey which equals to startkey!");
2730     }
2731     PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2732     controller.setPriority(hri.getTable());
2733 
2734     // TODO: this does not do retries, it should. Set priority and timeout in controller
2735     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
2736     ProtobufUtil.split(controller, admin, hri, splitPoint);
2737   }
2738 
2739   /**
2740    * Modify an existing table, more IRB friendly version.
2741    * Asynchronous operation.  This means that it may be a while before your
2742    * schema change is updated across all of the table.
2743    *
2744    * @param tableName name of table.
2745    * @param htd modified description of the table
2746    * @throws IOException if a remote or network exception occurs
2747    */
2748   @Override
2749   public void modifyTable(final TableName tableName, final HTableDescriptor htd)
2750   throws IOException {
2751     if (!tableName.equals(htd.getTableName())) {
2752       throw new IllegalArgumentException("the specified table name '" + tableName +
2753         "' doesn't match with the HTD one: " + htd.getTableName());
2754     }
2755 
2756     executeCallable(new MasterCallable<Void>(getConnection()) {
2757       @Override
2758       public Void call(int callTimeout) throws ServiceException {
2759         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2760         controller.setCallTimeout(callTimeout);
2761         controller.setPriority(tableName);
2762         ModifyTableRequest request = RequestConverter.buildModifyTableRequest(
2763           tableName, htd, ng.getNonceGroup(), ng.newNonce());
2764         master.modifyTable(controller, request);
2765         return null;
2766       }
2767     });
2768   }
2769 
2770   public void modifyTable(final byte[] tableName, final HTableDescriptor htd)
2771   throws IOException {
2772     modifyTable(TableName.valueOf(tableName), htd);
2773   }
2774 
2775   public void modifyTable(final String tableName, final HTableDescriptor htd)
2776   throws IOException {
2777     modifyTable(TableName.valueOf(tableName), htd);
2778   }
2779 
2780   /**
2781    * @param regionName Name of a region.
2782    * @return a pair of HRegionInfo and ServerName if <code>regionName</code> is
2783    *  a verified region name (we call {@link
2784    *  MetaTableAccessor#getRegion(HConnection, byte[])}
2785    *  else null.
2786    * Throw IllegalArgumentException if <code>regionName</code> is null.
2787    * @throws IOException
2788    */
2789   Pair<HRegionInfo, ServerName> getRegion(final byte[] regionName) throws IOException {
2790     if (regionName == null) {
2791       throw new IllegalArgumentException("Pass a table name or region name");
2792     }
2793     Pair<HRegionInfo, ServerName> pair =
2794       MetaTableAccessor.getRegion(connection, regionName);
2795     if (pair == null) {
2796       final AtomicReference<Pair<HRegionInfo, ServerName>> result =
2797         new AtomicReference<Pair<HRegionInfo, ServerName>>(null);
2798       final String encodedName = Bytes.toString(regionName);
2799       MetaScannerVisitor visitor = new MetaScannerVisitorBase() {
2800         @Override
2801         public boolean processRow(Result data) throws IOException {
2802           HRegionInfo info = HRegionInfo.getHRegionInfo(data);
2803           if (info == null) {
2804             LOG.warn("No serialized HRegionInfo in " + data);
2805             return true;
2806           }
2807           RegionLocations rl = MetaTableAccessor.getRegionLocations(data);
2808           boolean matched = false;
2809           ServerName sn = null;
2810           for (HRegionLocation h : rl.getRegionLocations()) {
2811             if (h != null && encodedName.equals(h.getRegionInfo().getEncodedName())) {
2812               sn = h.getServerName();
2813               info = h.getRegionInfo();
2814               matched = true;
2815             }
2816           }
2817           if (!matched) return true;
2818           result.set(new Pair<HRegionInfo, ServerName>(info, sn));
2819           return false; // found the region, stop
2820         }
2821       };
2822 
2823       MetaScanner.metaScan(connection, visitor, null);
2824       pair = result.get();
2825     }
2826     return pair;
2827   }
2828 
2829   /**
2830    * If the input is a region name, it is returned as is. If it's an
2831    * encoded region name, the corresponding region is found from meta
2832    * and its region name is returned. If we can't find any region in
2833    * meta matching the input as either region name or encoded region
2834    * name, the input is returned as is. We don't throw unknown
2835    * region exception.
2836    */
2837   private byte[] getRegionName(
2838       final byte[] regionNameOrEncodedRegionName) throws IOException {
2839     if (Bytes.equals(regionNameOrEncodedRegionName,
2840         HRegionInfo.FIRST_META_REGIONINFO.getRegionName())
2841           || Bytes.equals(regionNameOrEncodedRegionName,
2842             HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes())) {
2843       return HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
2844     }
2845     byte[] tmp = regionNameOrEncodedRegionName;
2846     Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionNameOrEncodedRegionName);
2847     if (regionServerPair != null && regionServerPair.getFirst() != null) {
2848       tmp = regionServerPair.getFirst().getRegionName();
2849     }
2850     return tmp;
2851   }
2852 
2853   /**
2854    * Check if table exists or not
2855    * @param tableName Name of a table.
2856    * @return tableName instance
2857    * @throws IOException if a remote or network exception occurs.
2858    * @throws TableNotFoundException if table does not exist.
2859    */
2860   private TableName checkTableExists(final TableName tableName)
2861       throws IOException {
2862     if (!MetaTableAccessor.tableExists(connection, tableName)) {
2863       throw new TableNotFoundException(tableName);
2864     }
2865     return tableName;
2866   }
2867 
2868   /**
2869    * Shuts down the HBase cluster
2870    * @throws IOException if a remote or network exception occurs
2871    */
2872   @Override
2873   public synchronized void shutdown() throws IOException {
2874     executeCallable(new MasterCallable<Void>(getConnection()) {
2875       @Override
2876       public Void call(int callTimeout) throws ServiceException {
2877         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2878         controller.setCallTimeout(callTimeout);
2879         controller.setPriority(HConstants.HIGH_QOS);
2880         master.shutdown(controller, ShutdownRequest.newBuilder().build());
2881         return null;
2882       }
2883     });
2884   }
2885 
2886   /**
2887    * Shuts down the current HBase master only.
2888    * Does not shutdown the cluster.
2889    * @see #shutdown()
2890    * @throws IOException if a remote or network exception occurs
2891    */
2892   @Override
2893   public synchronized void stopMaster() throws IOException {
2894     executeCallable(new MasterCallable<Void>(getConnection()) {
2895       @Override
2896       public Void call(int callTimeout) throws ServiceException {
2897         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2898         controller.setCallTimeout(callTimeout);
2899         controller.setPriority(HConstants.HIGH_QOS);
2900         master.stopMaster(controller, StopMasterRequest.newBuilder().build());
2901         return null;
2902       }
2903     });
2904   }
2905 
2906   /**
2907    * Stop the designated regionserver
2908    * @param hostnamePort Hostname and port delimited by a <code>:</code> as in
2909    * <code>example.org:1234</code>
2910    * @throws IOException if a remote or network exception occurs
2911    */
2912   @Override
2913   public synchronized void stopRegionServer(final String hostnamePort)
2914   throws IOException {
2915     String hostname = Addressing.parseHostname(hostnamePort);
2916     int port = Addressing.parsePort(hostnamePort);
2917     AdminService.BlockingInterface admin =
2918       this.connection.getAdmin(ServerName.valueOf(hostname, port, 0));
2919     StopServerRequest request = RequestConverter.buildStopServerRequest(
2920       "Called by admin client " + this.connection.toString());
2921     PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2922 
2923     controller.setPriority(HConstants.HIGH_QOS);
2924     try {
2925       // TODO: this does not do retries, it should. Set priority and timeout in controller
2926       admin.stopServer(controller, request);
2927     } catch (ServiceException se) {
2928       throw ProtobufUtil.getRemoteException(se);
2929     }
2930   }
2931 
2932 
2933   /**
2934    * @return cluster status
2935    * @throws IOException if a remote or network exception occurs
2936    */
2937   @Override
2938   public ClusterStatus getClusterStatus() throws IOException {
2939     return executeCallable(new MasterCallable<ClusterStatus>(getConnection()) {
2940       @Override
2941       public ClusterStatus call(int callTimeout) throws ServiceException {
2942         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2943         controller.setCallTimeout(callTimeout);
2944         GetClusterStatusRequest req = RequestConverter.buildGetClusterStatusRequest();
2945         return ClusterStatus.convert(master.getClusterStatus(controller, req).getClusterStatus());
2946       }
2947     });
2948   }
2949 
2950   /**
2951    * @return Configuration used by the instance.
2952    */
2953   @Override
2954   public Configuration getConfiguration() {
2955     return this.conf;
2956   }
2957 
2958   /**
2959    * Create a new namespace
2960    * @param descriptor descriptor which describes the new namespace
2961    * @throws IOException
2962    */
2963   @Override
2964   public void createNamespace(final NamespaceDescriptor descriptor) throws IOException {
2965     executeCallable(new MasterCallable<Void>(getConnection()) {
2966       @Override
2967       public Void call(int callTimeout) throws Exception {
2968         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2969         controller.setCallTimeout(callTimeout);
2970         // TODO: set priority based on NS?
2971         master.createNamespace(controller,
2972           CreateNamespaceRequest.newBuilder()
2973             .setNamespaceDescriptor(ProtobufUtil
2974               .toProtoNamespaceDescriptor(descriptor)).build()
2975         );
2976         return null;
2977       }
2978     });
2979   }
2980 
2981   /**
2982    * Modify an existing namespace
2983    * @param descriptor descriptor which describes the new namespace
2984    * @throws IOException
2985    */
2986   @Override
2987   public void modifyNamespace(final NamespaceDescriptor descriptor) throws IOException {
2988     executeCallable(new MasterCallable<Void>(getConnection()) {
2989       @Override
2990       public Void call(int callTimeout) throws Exception {
2991         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
2992         controller.setCallTimeout(callTimeout);
2993         master.modifyNamespace(controller, ModifyNamespaceRequest.newBuilder().
2994           setNamespaceDescriptor(ProtobufUtil.toProtoNamespaceDescriptor(descriptor)).build());
2995         return null;
2996       }
2997     });
2998   }
2999 
3000   /**
3001    * Delete an existing namespace. Only empty namespaces (no tables) can be removed.
3002    * @param name namespace name
3003    * @throws IOException
3004    */
3005   @Override
3006   public void deleteNamespace(final String name) throws IOException {
3007     executeCallable(new MasterCallable<Void>(getConnection()) {
3008       @Override
3009       public Void call(int callTimeout) throws Exception {
3010         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3011         controller.setCallTimeout(callTimeout);
3012         master.deleteNamespace(controller, DeleteNamespaceRequest.newBuilder().
3013           setNamespaceName(name).build());
3014         return null;
3015       }
3016     });
3017   }
3018 
3019   /**
3020    * Get a namespace descriptor by name
3021    * @param name name of namespace descriptor
3022    * @return A descriptor
3023    * @throws IOException
3024    */
3025   @Override
3026   public NamespaceDescriptor getNamespaceDescriptor(final String name)
3027       throws NamespaceNotFoundException, IOException {
3028     return
3029         executeCallable(new MasterCallable<NamespaceDescriptor>(getConnection()) {
3030           @Override
3031           public NamespaceDescriptor call(int callTimeout) throws Exception {
3032             PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3033             controller.setCallTimeout(callTimeout);
3034             return ProtobufUtil.toNamespaceDescriptor(
3035               master.getNamespaceDescriptor(controller, GetNamespaceDescriptorRequest.newBuilder().
3036                 setNamespaceName(name).build()).getNamespaceDescriptor());
3037           }
3038         });
3039   }
3040 
3041   /**
3042    * List available namespace descriptors
3043    * @return List of descriptors
3044    * @throws IOException
3045    */
3046   @Override
3047   public NamespaceDescriptor[] listNamespaceDescriptors() throws IOException {
3048     return
3049         executeCallable(new MasterCallable<NamespaceDescriptor[]>(getConnection()) {
3050           @Override
3051           public NamespaceDescriptor[] call(int callTimeout) throws Exception {
3052             PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3053             controller.setCallTimeout(callTimeout);
3054             List<HBaseProtos.NamespaceDescriptor> list =
3055                 master.listNamespaceDescriptors(controller,
3056                   ListNamespaceDescriptorsRequest.newBuilder().build())
3057                 .getNamespaceDescriptorList();
3058             NamespaceDescriptor[] res = new NamespaceDescriptor[list.size()];
3059             for(int i = 0; i < list.size(); i++) {
3060               res[i] = ProtobufUtil.toNamespaceDescriptor(list.get(i));
3061             }
3062             return res;
3063           }
3064         });
3065   }
3066 
3067   /**
3068    * List procedures
3069    * @return procedure list
3070    * @throws IOException
3071    */
3072   @Override
3073   public ProcedureInfo[] listProcedures() throws IOException {
3074     return
3075         executeCallable(new MasterCallable<ProcedureInfo[]>(getConnection()) {
3076           @Override
3077           public ProcedureInfo[] call(int callTimeout) throws Exception {
3078             PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3079             controller.setCallTimeout(callTimeout);
3080             List<ProcedureProtos.Procedure> procList = master.listProcedures(
3081               controller, ListProceduresRequest.newBuilder().build()).getProcedureList();
3082             ProcedureInfo[] procInfoList = new ProcedureInfo[procList.size()];
3083             for (int i = 0; i < procList.size(); i++) {
3084               procInfoList[i] = ProcedureInfo.convert(procList.get(i));
3085             }
3086             return procInfoList;
3087           }
3088         });
3089   }
3090 
3091   /**
3092    * Get list of table descriptors by namespace
3093    * @param name namespace name
3094    * @return A descriptor
3095    * @throws IOException
3096    */
3097   @Override
3098   public HTableDescriptor[] listTableDescriptorsByNamespace(final String name) throws IOException {
3099     return
3100         executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
3101           @Override
3102           public HTableDescriptor[] call(int callTimeout) throws Exception {
3103             PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3104             controller.setCallTimeout(callTimeout);
3105             List<TableSchema> list =
3106                 master.listTableDescriptorsByNamespace(controller,
3107                   ListTableDescriptorsByNamespaceRequest.newBuilder().setNamespaceName(name)
3108                   .build()).getTableSchemaList();
3109             HTableDescriptor[] res = new HTableDescriptor[list.size()];
3110             for(int i=0; i < list.size(); i++) {
3111 
3112               res[i] = HTableDescriptor.convert(list.get(i));
3113             }
3114             return res;
3115           }
3116         });
3117   }
3118 
3119   /**
3120    * Get list of table names by namespace
3121    * @param name namespace name
3122    * @return The list of table names in the namespace
3123    * @throws IOException
3124    */
3125   @Override
3126   public TableName[] listTableNamesByNamespace(final String name) throws IOException {
3127     return
3128         executeCallable(new MasterCallable<TableName[]>(getConnection()) {
3129           @Override
3130           public TableName[] call(int callTimeout) throws Exception {
3131             PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3132             controller.setCallTimeout(callTimeout);
3133             List<HBaseProtos.TableName> tableNames =
3134               master.listTableNamesByNamespace(controller, ListTableNamesByNamespaceRequest.
3135                 newBuilder().setNamespaceName(name).build())
3136                 .getTableNameList();
3137             TableName[] result = new TableName[tableNames.size()];
3138             for (int i = 0; i < tableNames.size(); i++) {
3139               result[i] = ProtobufUtil.toTableName(tableNames.get(i));
3140             }
3141             return result;
3142           }
3143         });
3144   }
3145 
3146   /**
3147    * Check to see if HBase is running. Throw an exception if not.
3148    * @param conf system configuration
3149    * @throws MasterNotRunningException if the master is not running
3150    * @throws ZooKeeperConnectionException if unable to connect to zookeeper
3151    */
3152   // Used by tests and by the Merge tool. Merge tool uses it to figure if HBase is up or not.
3153   public static void checkHBaseAvailable(Configuration conf)
3154   throws MasterNotRunningException, ZooKeeperConnectionException, ServiceException, IOException {
3155     Configuration copyOfConf = HBaseConfiguration.create(conf);
3156     // We set it to make it fail as soon as possible if HBase is not available
3157     copyOfConf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
3158     copyOfConf.setInt("zookeeper.recovery.retry", 0);
3159     try (ClusterConnection connection =
3160         (ClusterConnection)ConnectionFactory.createConnection(copyOfConf)) {
3161         // Check ZK first.
3162         // If the connection exists, we may have a connection to ZK that does not work anymore
3163         ZooKeeperKeepAliveConnection zkw = null;
3164         try {
3165           // This is NASTY. FIX!!!! Dependent on internal implementation! TODO
3166           zkw = ((ConnectionManager.HConnectionImplementation)connection).
3167             getKeepAliveZooKeeperWatcher();
3168           zkw.getRecoverableZooKeeper().getZooKeeper().exists(zkw.baseZNode, false);
3169         } catch (IOException e) {
3170           throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
3171         } catch (InterruptedException e) {
3172           throw (InterruptedIOException)
3173             new InterruptedIOException("Can't connect to ZooKeeper").initCause(e);
3174         } catch (KeeperException e) {
3175           throw new ZooKeeperConnectionException("Can't connect to ZooKeeper", e);
3176         } finally {
3177           if (zkw != null) {
3178             zkw.close();
3179           }
3180         }
3181       connection.isMasterRunning();
3182     }
3183   }
3184 
3185   /**
3186    * get the regions of a given table.
3187    *
3188    * @param tableName the name of the table
3189    * @return Ordered list of {@link HRegionInfo}.
3190    * @throws IOException
3191    */
3192   @Override
3193   public List<HRegionInfo> getTableRegions(final TableName tableName)
3194   throws IOException {
3195     ZooKeeperWatcher zookeeper =
3196       new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
3197         new ThrowableAbortable());
3198     List<HRegionInfo> Regions = null;
3199     try {
3200       Regions = MetaTableAccessor.getTableRegions(zookeeper, connection, tableName, true);
3201     } finally {
3202       zookeeper.close();
3203     }
3204     return Regions;
3205   }
3206 
3207   public List<HRegionInfo> getTableRegions(final byte[] tableName)
3208   throws IOException {
3209     return getTableRegions(TableName.valueOf(tableName));
3210   }
3211 
3212   @Override
3213   public synchronized void close() throws IOException {
3214     if (cleanupConnectionOnClose && this.connection != null && !this.closed) {
3215       this.connection.close();
3216       this.closed = true;
3217     }
3218   }
3219 
3220   /**
3221    * Get tableDescriptors
3222    * @param tableNames List of table names
3223    * @return HTD[] the tableDescriptor
3224    * @throws IOException if a remote or network exception occurs
3225    */
3226   @Override
3227   public HTableDescriptor[] getTableDescriptorsByTableName(final List<TableName> tableNames)
3228   throws IOException {
3229     return executeCallable(new MasterCallable<HTableDescriptor[]>(getConnection()) {
3230       @Override
3231       public HTableDescriptor[] call(int callTimeout) throws Exception {
3232         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3233         controller.setCallTimeout(callTimeout);
3234         GetTableDescriptorsRequest req =
3235             RequestConverter.buildGetTableDescriptorsRequest(tableNames);
3236           return ProtobufUtil.getHTableDescriptorArray(master.getTableDescriptors(controller, req));
3237       }
3238     });
3239   }
3240 
3241   /**
3242    * Get tableDescriptor
3243    * @param tableName one table name
3244    * @return HTD the HTableDescriptor or null if the table not exists
3245    * @throws IOException if a remote or network exception occurs
3246    */
3247   private HTableDescriptor getTableDescriptorByTableName(TableName tableName)
3248       throws IOException {
3249     List<TableName> tableNames = new ArrayList<TableName>(1);
3250     tableNames.add(tableName);
3251 
3252     HTableDescriptor[] htdl = getTableDescriptorsByTableName(tableNames);
3253 
3254     if (htdl == null || htdl.length == 0) {
3255       return null;
3256     }
3257     else {
3258       return htdl[0];
3259     }
3260   }
3261 
3262   /**
3263    * Get tableDescriptors
3264    * @param names List of table names
3265    * @return HTD[] the tableDescriptor
3266    * @throws IOException if a remote or network exception occurs
3267    */
3268   @Override
3269   public HTableDescriptor[] getTableDescriptors(List<String> names)
3270   throws IOException {
3271     List<TableName> tableNames = new ArrayList<TableName>(names.size());
3272     for(String name : names) {
3273       tableNames.add(TableName.valueOf(name));
3274     }
3275     return getTableDescriptorsByTableName(tableNames);
3276   }
3277 
3278   private RollWALWriterResponse rollWALWriterImpl(final ServerName sn) throws IOException,
3279       FailedLogCloseException {
3280     AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
3281     RollWALWriterRequest request = RequestConverter.buildRollWALWriterRequest();
3282     PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3283 
3284     try {
3285       // TODO: this does not do retries, it should. Set priority and timeout in controller
3286       return admin.rollWALWriter(controller, request);
3287     } catch (ServiceException se) {
3288       throw ProtobufUtil.getRemoteException(se);
3289     }
3290   }
3291 
3292   /**
3293    * Roll the log writer. I.e. when using a file system based write ahead log,
3294    * start writing log messages to a new file.
3295    *
3296    * Note that when talking to a version 1.0+ HBase deployment, the rolling is asynchronous.
3297    * This method will return as soon as the roll is requested and the return value will
3298    * always be null. Additionally, the named region server may schedule store flushes at the
3299    * request of the wal handling the roll request.
3300    *
3301    * When talking to a 0.98 or older HBase deployment, the rolling is synchronous and the
3302    * return value may be either null or a list of encoded region names.
3303    *
3304    * @param serverName
3305    *          The servername of the regionserver. A server name is made of host,
3306    *          port and startcode. This is mandatory. Here is an example:
3307    *          <code> host187.example.com,60020,1289493121758</code>
3308    * @return a set of {@link HRegionInfo#getEncodedName()} that would allow the wal to
3309    *         clean up some underlying files. null if there's nothing to flush.
3310    * @throws IOException if a remote or network exception occurs
3311    * @throws FailedLogCloseException
3312    * @deprecated use {@link #rollWALWriter(ServerName)}
3313    */
3314   @Deprecated
3315   public synchronized byte[][] rollHLogWriter(String serverName)
3316       throws IOException, FailedLogCloseException {
3317     ServerName sn = ServerName.valueOf(serverName);
3318     final RollWALWriterResponse response = rollWALWriterImpl(sn);
3319     int regionCount = response.getRegionToFlushCount();
3320     if (0 == regionCount) {
3321       return null;
3322     }
3323     byte[][] regionsToFlush = new byte[regionCount][];
3324     for (int i = 0; i < regionCount; i++) {
3325       ByteString region = response.getRegionToFlush(i);
3326       regionsToFlush[i] = region.toByteArray();
3327     }
3328     return regionsToFlush;
3329   }
3330 
3331   @Override
3332   public synchronized void rollWALWriter(ServerName serverName)
3333       throws IOException, FailedLogCloseException {
3334     rollWALWriterImpl(serverName);
3335   }
3336 
3337   @Override
3338   public String[] getMasterCoprocessors() {
3339     try {
3340       return getClusterStatus().getMasterCoprocessors();
3341     } catch (IOException e) {
3342       LOG.error("Could not getClusterStatus()",e);
3343       return null;
3344     }
3345   }
3346 
3347   /**
3348    * {@inheritDoc}
3349    */
3350   @Override
3351   public CompactionState getCompactionState(final TableName tableName)
3352   throws IOException {
3353     CompactionState state = CompactionState.NONE;
3354     ZooKeeperWatcher zookeeper =
3355       new ZooKeeperWatcher(conf, ZK_IDENTIFIER_PREFIX + connection.toString(),
3356         new ThrowableAbortable());
3357     try {
3358       checkTableExists(tableName);
3359       List<Pair<HRegionInfo, ServerName>> pairs =
3360         MetaTableAccessor.getTableRegionsAndLocations(zookeeper, connection, tableName);
3361       for (Pair<HRegionInfo, ServerName> pair: pairs) {
3362         if (pair.getFirst().isOffline()) continue;
3363         if (pair.getSecond() == null) continue;
3364         try {
3365           ServerName sn = pair.getSecond();
3366           AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
3367           GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest(
3368             pair.getFirst().getRegionName(), true);
3369           GetRegionInfoResponse response = admin.getRegionInfo(null, request);
3370           switch (response.getCompactionState()) {
3371           case MAJOR_AND_MINOR:
3372             return CompactionState.MAJOR_AND_MINOR;
3373           case MAJOR:
3374             if (state == CompactionState.MINOR) {
3375               return CompactionState.MAJOR_AND_MINOR;
3376             }
3377             state = CompactionState.MAJOR;
3378             break;
3379           case MINOR:
3380             if (state == CompactionState.MAJOR) {
3381               return CompactionState.MAJOR_AND_MINOR;
3382             }
3383             state = CompactionState.MINOR;
3384             break;
3385           case NONE:
3386           default: // nothing, continue
3387           }
3388         } catch (NotServingRegionException e) {
3389           if (LOG.isDebugEnabled()) {
3390             LOG.debug("Trying to get compaction state of " +
3391               pair.getFirst() + ": " +
3392               StringUtils.stringifyException(e));
3393           }
3394         } catch (RemoteException e) {
3395           if (e.getMessage().indexOf(NotServingRegionException.class.getName()) >= 0) {
3396             if (LOG.isDebugEnabled()) {
3397               LOG.debug("Trying to get compaction state of " + pair.getFirst() + ": "
3398                 + StringUtils.stringifyException(e));
3399             }
3400           } else {
3401             throw e;
3402           }
3403         }
3404       }
3405     } catch (ServiceException se) {
3406       throw ProtobufUtil.getRemoteException(se);
3407     } finally {
3408       zookeeper.close();
3409     }
3410     return state;
3411   }
3412 
3413   /**
3414    * {@inheritDoc}
3415    */
3416   @Override
3417   public CompactionState getCompactionStateForRegion(final byte[] regionName)
3418   throws IOException {
3419     try {
3420       Pair<HRegionInfo, ServerName> regionServerPair = getRegion(regionName);
3421       if (regionServerPair == null) {
3422         throw new IllegalArgumentException("Invalid region: " + Bytes.toStringBinary(regionName));
3423       }
3424       if (regionServerPair.getSecond() == null) {
3425         throw new NoServerForRegionException(Bytes.toStringBinary(regionName));
3426       }
3427       ServerName sn = regionServerPair.getSecond();
3428       AdminService.BlockingInterface admin = this.connection.getAdmin(sn);
3429       GetRegionInfoRequest request = RequestConverter.buildGetRegionInfoRequest(
3430         regionServerPair.getFirst().getRegionName(), true);
3431       PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3432       // TODO: this does not do retries, it should. Set priority and timeout in controller
3433       GetRegionInfoResponse response = admin.getRegionInfo(controller, request);
3434       return response.getCompactionState();
3435     } catch (ServiceException se) {
3436       throw ProtobufUtil.getRemoteException(se);
3437     }
3438   }
3439 
3440   /**
3441    * @deprecated Use {@link #getCompactionState(org.apache.hadoop.hbase.TableName)} or {@link
3442    * #getCompactionStateForRegion(byte[])} instead.
3443    */
3444   @Deprecated
3445   public CompactionState getCompactionState(final String tableNameOrRegionName)
3446   throws IOException, InterruptedException {
3447     return getCompactionState(Bytes.toBytes(tableNameOrRegionName));
3448   }
3449 
3450   /**
3451    * @deprecated Use {@link #getCompactionState(org.apache.hadoop.hbase.TableName)} or {@link
3452    * #getCompactionStateForRegion(byte[])} instead.
3453    */
3454   @Deprecated
3455   public CompactionState getCompactionState(final byte[] tableNameOrRegionName)
3456   throws IOException, InterruptedException {
3457     try {
3458       return getCompactionStateForRegion(tableNameOrRegionName);
3459     } catch (IllegalArgumentException e) {
3460       // Invalid region, try table
3461       return getCompactionState(TableName.valueOf(tableNameOrRegionName));
3462     }
3463   }
3464 
3465   /**
3466    * Take a snapshot for the given table. If the table is enabled, a FLUSH-type snapshot will be
3467    * taken. If the table is disabled, an offline snapshot is taken.
3468    * <p>
3469    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3470    * snapshot with the same name (even a different type or with different parameters) will fail with
3471    * a {@link SnapshotCreationException} indicating the duplicate naming.
3472    * <p>
3473    * Snapshot names follow the same naming constraints as tables in HBase. See
3474    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
3475    * @param snapshotName name of the snapshot to be created
3476    * @param tableName name of the table for which snapshot is created
3477    * @throws IOException if a remote or network exception occurs
3478    * @throws SnapshotCreationException if snapshot creation failed
3479    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3480    */
3481   @Override
3482   public void snapshot(final String snapshotName,
3483                        final TableName tableName) throws IOException,
3484       SnapshotCreationException, IllegalArgumentException {
3485     snapshot(snapshotName, tableName, SnapshotDescription.Type.FLUSH);
3486   }
3487 
3488   public void snapshot(final String snapshotName,
3489                        final String tableName) throws IOException,
3490       SnapshotCreationException, IllegalArgumentException {
3491     snapshot(snapshotName, TableName.valueOf(tableName),
3492         SnapshotDescription.Type.FLUSH);
3493   }
3494 
3495   /**
3496    * Create snapshot for the given table of given flush type.
3497    * <p>
3498    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3499    * snapshot with the same name (even a different type or with different parameters) will fail with
3500    * a {@link SnapshotCreationException} indicating the duplicate naming.
3501    * <p>
3502    * Snapshot names follow the same naming constraints as tables in HBase.
3503    * @param snapshotName name of the snapshot to be created
3504    * @param tableName name of the table for which snapshot is created
3505    * @param flushType if the snapshot should be taken without flush memstore first
3506    * @throws IOException if a remote or network exception occurs
3507    * @throws SnapshotCreationException if snapshot creation failed
3508    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3509    */
3510    public void snapshot(final byte[] snapshotName, final byte[] tableName,
3511                        final SnapshotDescription.Type flushType) throws
3512       IOException, SnapshotCreationException, IllegalArgumentException {
3513       snapshot(Bytes.toString(snapshotName), Bytes.toString(tableName), flushType);
3514   }
3515   /**
3516    public void snapshot(final String snapshotName,
3517     * Create a timestamp consistent snapshot for the given table.
3518                         final byte[] tableName) throws IOException,
3519     * <p>
3520     * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3521     * snapshot with the same name (even a different type or with different parameters) will fail with
3522     * a {@link SnapshotCreationException} indicating the duplicate naming.
3523     * <p>
3524     * Snapshot names follow the same naming constraints as tables in HBase.
3525     * @param snapshotName name of the snapshot to be created
3526     * @param tableName name of the table for which snapshot is created
3527     * @throws IOException if a remote or network exception occurs
3528     * @throws SnapshotCreationException if snapshot creation failed
3529     * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3530     */
3531   @Override
3532   public void snapshot(final byte[] snapshotName,
3533                        final TableName tableName) throws IOException,
3534       SnapshotCreationException, IllegalArgumentException {
3535     snapshot(Bytes.toString(snapshotName), tableName, SnapshotDescription.Type.FLUSH);
3536   }
3537 
3538   public void snapshot(final byte[] snapshotName,
3539                        final byte[] tableName) throws IOException,
3540       SnapshotCreationException, IllegalArgumentException {
3541     snapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName),
3542       SnapshotDescription.Type.FLUSH);
3543   }
3544 
3545   /**
3546    * Create typed snapshot of the table.
3547    * <p>
3548    * Snapshots are considered unique based on <b>the name of the snapshot</b>. Attempts to take a
3549    * snapshot with the same name (even a different type or with different parameters) will fail with
3550    * a {@link SnapshotCreationException} indicating the duplicate naming.
3551    * <p>
3552    * Snapshot names follow the same naming constraints as tables in HBase. See
3553    * {@link org.apache.hadoop.hbase.TableName#isLegalFullyQualifiedTableName(byte[])}.
3554    * <p>
3555    * @param snapshotName name to give the snapshot on the filesystem. Must be unique from all other
3556    *          snapshots stored on the cluster
3557    * @param tableName name of the table to snapshot
3558    * @param type type of snapshot to take
3559    * @throws IOException we fail to reach the master
3560    * @throws SnapshotCreationException if snapshot creation failed
3561    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3562    */
3563   @Override
3564   public void snapshot(final String snapshotName,
3565                        final TableName tableName,
3566                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
3567       IllegalArgumentException {
3568     SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
3569     builder.setTable(tableName.getNameAsString());
3570     builder.setName(snapshotName);
3571     builder.setType(type);
3572     snapshot(builder.build());
3573   }
3574 
3575   public void snapshot(final String snapshotName,
3576                        final String tableName,
3577                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
3578       IllegalArgumentException {
3579     snapshot(snapshotName, TableName.valueOf(tableName), type);
3580   }
3581 
3582   public void snapshot(final String snapshotName,
3583                        final byte[] tableName,
3584                       SnapshotDescription.Type type) throws IOException, SnapshotCreationException,
3585       IllegalArgumentException {
3586     snapshot(snapshotName, TableName.valueOf(tableName), type);
3587   }
3588 
3589   /**
3590    * Take a snapshot and wait for the server to complete that snapshot (blocking).
3591    * <p>
3592    * Only a single snapshot should be taken at a time for an instance of HBase, or results may be
3593    * undefined (you can tell multiple HBase clusters to snapshot at the same time, but only one at a
3594    * time for a single cluster).
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    * You should probably use {@link #snapshot(String, String)} or {@link #snapshot(byte[], byte[])}
3604    * unless you are sure about the type of snapshot that you want to take.
3605    * @param snapshot snapshot to take
3606    * @throws IOException or we lose contact with the master.
3607    * @throws SnapshotCreationException if snapshot failed to be taken
3608    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3609    */
3610   @Override
3611   public void snapshot(SnapshotDescription snapshot) throws IOException, SnapshotCreationException,
3612       IllegalArgumentException {
3613     // actually take the snapshot
3614     SnapshotResponse response = takeSnapshotAsync(snapshot);
3615     final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot)
3616         .build();
3617     IsSnapshotDoneResponse done = null;
3618     long start = EnvironmentEdgeManager.currentTime();
3619     long max = response.getExpectedTimeout();
3620     long maxPauseTime = max / this.numRetries;
3621     int tries = 0;
3622     LOG.debug("Waiting a max of " + max + " ms for snapshot '" +
3623         ClientSnapshotDescriptionUtils.toString(snapshot) + "'' to complete. (max " +
3624         maxPauseTime + " ms per retry)");
3625     while (tries == 0
3626         || ((EnvironmentEdgeManager.currentTime() - start) < max && !done.getDone())) {
3627       try {
3628         // sleep a backoff <= pauseTime amount
3629         long sleep = getPauseTime(tries++);
3630         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
3631         LOG.debug("(#" + tries + ") Sleeping: " + sleep +
3632           "ms while waiting for snapshot completion.");
3633         Thread.sleep(sleep);
3634       } catch (InterruptedException e) {
3635         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
3636       }
3637       LOG.debug("Getting current status of snapshot from master...");
3638       done = executeCallable(new MasterCallable<IsSnapshotDoneResponse>(getConnection()) {
3639         @Override
3640         public IsSnapshotDoneResponse call(int callTimeout) throws ServiceException {
3641           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3642           controller.setCallTimeout(callTimeout);
3643           return master.isSnapshotDone(controller, request);
3644         }
3645       });
3646     }
3647     if (!done.getDone()) {
3648       throw new SnapshotCreationException("Snapshot '" + snapshot.getName()
3649           + "' wasn't completed in expectedTime:" + max + " ms", snapshot);
3650     }
3651   }
3652 
3653   /**
3654    * Take a snapshot without waiting for the server to complete that snapshot (asynchronous)
3655    * <p>
3656    * Only a single snapshot should be taken at a time, or results may be undefined.
3657    * @param snapshot snapshot to take
3658    * @return response from the server indicating the max time to wait for the snapshot
3659    * @throws IOException if the snapshot did not succeed or we lose contact with the master.
3660    * @throws SnapshotCreationException if snapshot creation failed
3661    * @throws IllegalArgumentException if the snapshot request is formatted incorrectly
3662    */
3663   @Override
3664   public SnapshotResponse takeSnapshotAsync(SnapshotDescription snapshot) throws IOException,
3665       SnapshotCreationException {
3666     ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
3667     final SnapshotRequest request = SnapshotRequest.newBuilder().setSnapshot(snapshot)
3668         .build();
3669     // run the snapshot on the master
3670     return executeCallable(new MasterCallable<SnapshotResponse>(getConnection()) {
3671       @Override
3672       public SnapshotResponse call(int callTimeout) throws ServiceException {
3673         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3674         controller.setCallTimeout(callTimeout);
3675         return master.snapshot(controller, request);
3676       }
3677     });
3678   }
3679 
3680   /**
3681    * Check the current state of the passed snapshot.
3682    * <p>
3683    * There are three possible states:
3684    * <ol>
3685    * <li>running - returns <tt>false</tt></li>
3686    * <li>finished - returns <tt>true</tt></li>
3687    * <li>finished with error - throws the exception that caused the snapshot to fail</li>
3688    * </ol>
3689    * <p>
3690    * The cluster only knows about the most recent snapshot. Therefore, if another snapshot has been
3691    * run/started since the snapshot your are checking, you will recieve an
3692    * {@link UnknownSnapshotException}.
3693    * @param snapshot description of the snapshot to check
3694    * @return <tt>true</tt> if the snapshot is completed, <tt>false</tt> if the snapshot is still
3695    *         running
3696    * @throws IOException if we have a network issue
3697    * @throws HBaseSnapshotException if the snapshot failed
3698    * @throws UnknownSnapshotException if the requested snapshot is unknown
3699    */
3700   @Override
3701   public boolean isSnapshotFinished(final SnapshotDescription snapshot)
3702       throws IOException, HBaseSnapshotException, UnknownSnapshotException {
3703 
3704     return executeCallable(new MasterCallable<IsSnapshotDoneResponse>(getConnection()) {
3705       @Override
3706       public IsSnapshotDoneResponse call(int callTimeout) throws ServiceException {
3707         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3708         controller.setCallTimeout(callTimeout);
3709         return master.isSnapshotDone(controller,
3710           IsSnapshotDoneRequest.newBuilder().setSnapshot(snapshot).build());
3711       }
3712     }).getDone();
3713   }
3714 
3715   /**
3716    * Restore the specified snapshot on the original table. (The table must be disabled)
3717    * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property
3718    * is set to true, a snapshot of the current table is taken
3719    * before executing the restore operation.
3720    * In case of restore failure, the failsafe snapshot will be restored.
3721    * If the restore completes without problem the failsafe snapshot is deleted.
3722    *
3723    * @param snapshotName name of the snapshot to restore
3724    * @throws IOException if a remote or network exception occurs
3725    * @throws RestoreSnapshotException if snapshot failed to be restored
3726    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3727    */
3728   @Override
3729   public void restoreSnapshot(final byte[] snapshotName)
3730       throws IOException, RestoreSnapshotException {
3731     restoreSnapshot(Bytes.toString(snapshotName));
3732   }
3733 
3734   /**
3735    * Restore the specified snapshot on the original table. (The table must be disabled)
3736    * If the "hbase.snapshot.restore.take.failsafe.snapshot" configuration property
3737    * is set to true, a snapshot of the current table is taken
3738    * before executing the restore operation.
3739    * In case of restore failure, the failsafe snapshot will be restored.
3740    * If the restore completes without problem the failsafe snapshot is deleted.
3741    *
3742    * @param snapshotName name of the snapshot to restore
3743    * @throws IOException if a remote or network exception occurs
3744    * @throws RestoreSnapshotException if snapshot failed to be restored
3745    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3746    */
3747   @Override
3748   public void restoreSnapshot(final String snapshotName)
3749       throws IOException, RestoreSnapshotException {
3750     boolean takeFailSafeSnapshot =
3751       conf.getBoolean("hbase.snapshot.restore.take.failsafe.snapshot", false);
3752     restoreSnapshot(snapshotName, takeFailSafeSnapshot);
3753   }
3754 
3755   /**
3756    * Restore the specified snapshot on the original table. (The table must be disabled)
3757    * If 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken
3758    * before executing the restore operation.
3759    * In case of restore failure, the failsafe snapshot will be restored.
3760    * If the restore completes without problem the failsafe snapshot is deleted.
3761    *
3762    * The failsafe snapshot name is configurable by using the property
3763    * "hbase.snapshot.restore.failsafe.name".
3764    *
3765    * @param snapshotName name of the snapshot to restore
3766    * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken
3767    * @throws IOException if a remote or network exception occurs
3768    * @throws RestoreSnapshotException if snapshot failed to be restored
3769    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3770    */
3771   @Override
3772   public void restoreSnapshot(final byte[] snapshotName, final boolean takeFailSafeSnapshot)
3773       throws IOException, RestoreSnapshotException {
3774     restoreSnapshot(Bytes.toString(snapshotName), takeFailSafeSnapshot);
3775   }
3776 
3777   /**
3778    * Restore the specified snapshot on the original table. (The table must be disabled)
3779    * If 'takeFailSafeSnapshot' is set to true, a snapshot of the current table is taken
3780    * before executing the restore operation.
3781    * In case of restore failure, the failsafe snapshot will be restored.
3782    * If the restore completes without problem the failsafe snapshot is deleted.
3783    *
3784    * The failsafe snapshot name is configurable by using the property
3785    * "hbase.snapshot.restore.failsafe.name".
3786    *
3787    * @param snapshotName name of the snapshot to restore
3788    * @param takeFailSafeSnapshot true if the failsafe snapshot should be taken
3789    * @throws IOException if a remote or network exception occurs
3790    * @throws RestoreSnapshotException if snapshot failed to be restored
3791    * @throws IllegalArgumentException if the restore request is formatted incorrectly
3792    */
3793   @Override
3794   public void restoreSnapshot(final String snapshotName, boolean takeFailSafeSnapshot)
3795       throws IOException, RestoreSnapshotException {
3796     TableName tableName = null;
3797     for (SnapshotDescription snapshotInfo: listSnapshots()) {
3798       if (snapshotInfo.getName().equals(snapshotName)) {
3799         tableName = TableName.valueOf(snapshotInfo.getTable());
3800         break;
3801       }
3802     }
3803 
3804     if (tableName == null) {
3805       throw new RestoreSnapshotException(
3806         "Unable to find the table name for snapshot=" + snapshotName);
3807     }
3808 
3809     // The table does not exists, switch to clone.
3810     if (!tableExists(tableName)) {
3811       cloneSnapshot(snapshotName, tableName);
3812       return;
3813     }
3814 
3815     // Check if the table is disabled
3816     if (!isTableDisabled(tableName)) {
3817       throw new TableNotDisabledException(tableName);
3818     }
3819 
3820     // Take a snapshot of the current state
3821     String failSafeSnapshotSnapshotName = null;
3822     if (takeFailSafeSnapshot) {
3823       failSafeSnapshotSnapshotName = conf.get("hbase.snapshot.restore.failsafe.name",
3824         "hbase-failsafe-{snapshot.name}-{restore.timestamp}");
3825       failSafeSnapshotSnapshotName = failSafeSnapshotSnapshotName
3826         .replace("{snapshot.name}", snapshotName)
3827         .replace("{table.name}", tableName.toString().replace(TableName.NAMESPACE_DELIM, '.'))
3828         .replace("{restore.timestamp}", String.valueOf(EnvironmentEdgeManager.currentTime()));
3829       LOG.info("Taking restore-failsafe snapshot: " + failSafeSnapshotSnapshotName);
3830       snapshot(failSafeSnapshotSnapshotName, tableName);
3831     }
3832 
3833     try {
3834       // Restore snapshot
3835       internalRestoreSnapshot(snapshotName, tableName);
3836     } catch (IOException e) {
3837       // Somthing went wrong during the restore...
3838       // if the pre-restore snapshot is available try to rollback
3839       if (takeFailSafeSnapshot) {
3840         try {
3841           internalRestoreSnapshot(failSafeSnapshotSnapshotName, tableName);
3842           String msg = "Restore snapshot=" + snapshotName +
3843             " failed. Rollback to snapshot=" + failSafeSnapshotSnapshotName + " succeeded.";
3844           LOG.error(msg, e);
3845           throw new RestoreSnapshotException(msg, e);
3846         } catch (IOException ex) {
3847           String msg = "Failed to restore and rollback to snapshot=" + failSafeSnapshotSnapshotName;
3848           LOG.error(msg, ex);
3849           throw new RestoreSnapshotException(msg, e);
3850         }
3851       } else {
3852         throw new RestoreSnapshotException("Failed to restore snapshot=" + snapshotName, e);
3853       }
3854     }
3855 
3856     // If the restore is succeeded, delete the pre-restore snapshot
3857     if (takeFailSafeSnapshot) {
3858       try {
3859         LOG.info("Deleting restore-failsafe snapshot: " + failSafeSnapshotSnapshotName);
3860         deleteSnapshot(failSafeSnapshotSnapshotName);
3861       } catch (IOException e) {
3862         LOG.error("Unable to remove the failsafe snapshot: " + failSafeSnapshotSnapshotName, e);
3863       }
3864     }
3865   }
3866 
3867   /**
3868    * Create a new table by cloning the snapshot content.
3869    *
3870    * @param snapshotName name of the snapshot to be cloned
3871    * @param tableName name of the table where the snapshot will be restored
3872    * @throws IOException if a remote or network exception occurs
3873    * @throws TableExistsException if table to be created already exists
3874    * @throws RestoreSnapshotException if snapshot failed to be cloned
3875    * @throws IllegalArgumentException if the specified table has not a valid name
3876    */
3877   public void cloneSnapshot(final byte[] snapshotName, final byte[] tableName)
3878       throws IOException, TableExistsException, RestoreSnapshotException {
3879     cloneSnapshot(Bytes.toString(snapshotName), TableName.valueOf(tableName));
3880   }
3881 
3882   /**
3883    * Create a new table by cloning the snapshot content.
3884    *
3885    * @param snapshotName name of the snapshot to be cloned
3886    * @param tableName name of the table where the snapshot will be restored
3887    * @throws IOException if a remote or network exception occurs
3888    * @throws TableExistsException if table to be created already exists
3889    * @throws RestoreSnapshotException if snapshot failed to be cloned
3890    * @throws IllegalArgumentException if the specified table has not a valid name
3891    */
3892   @Override
3893   public void cloneSnapshot(final byte[] snapshotName, final TableName tableName)
3894       throws IOException, TableExistsException, RestoreSnapshotException {
3895     cloneSnapshot(Bytes.toString(snapshotName), tableName);
3896   }
3897 
3898 
3899 
3900   /**
3901    * Create a new table by cloning the snapshot content.
3902    *
3903    * @param snapshotName name of the snapshot to be cloned
3904    * @param tableName name of the table where the snapshot will be restored
3905    * @throws IOException if a remote or network exception occurs
3906    * @throws TableExistsException if table to be created already exists
3907    * @throws RestoreSnapshotException if snapshot failed to be cloned
3908    * @throws IllegalArgumentException if the specified table has not a valid name
3909    */
3910   public void cloneSnapshot(final String snapshotName, final String tableName)
3911       throws IOException, TableExistsException, RestoreSnapshotException, InterruptedException {
3912     cloneSnapshot(snapshotName, TableName.valueOf(tableName));
3913   }
3914 
3915   /**
3916    * Create a new table by cloning the snapshot content.
3917    *
3918    * @param snapshotName name of the snapshot to be cloned
3919    * @param tableName name of the table where the snapshot will be restored
3920    * @throws IOException if a remote or network exception occurs
3921    * @throws TableExistsException if table to be created already exists
3922    * @throws RestoreSnapshotException if snapshot failed to be cloned
3923    * @throws IllegalArgumentException if the specified table has not a valid name
3924    */
3925   @Override
3926   public void cloneSnapshot(final String snapshotName, final TableName tableName)
3927       throws IOException, TableExistsException, RestoreSnapshotException {
3928     if (tableExists(tableName)) {
3929       throw new TableExistsException(tableName);
3930     }
3931     internalRestoreSnapshot(snapshotName, tableName);
3932     waitUntilTableIsEnabled(tableName);
3933   }
3934 
3935   /**
3936    * Execute a distributed procedure on a cluster synchronously with return data
3937    *
3938    * @param signature A distributed procedure is uniquely identified
3939    * by its signature (default the root ZK node name of the procedure).
3940    * @param instance The instance name of the procedure. For some procedures, this parameter is
3941    * optional.
3942    * @param props Property/Value pairs of properties passing to the procedure
3943    * @return data returned after procedure execution. null if no return data.
3944    * @throws IOException
3945    */
3946   @Override
3947   public byte[] execProcedureWithRet(String signature, String instance,
3948       Map<String, String> props) throws IOException {
3949     ProcedureDescription.Builder builder = ProcedureDescription.newBuilder();
3950     builder.setSignature(signature).setInstance(instance);
3951     for (Entry<String, String> entry : props.entrySet()) {
3952       NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey())
3953           .setValue(entry.getValue()).build();
3954       builder.addConfiguration(pair);
3955     }
3956 
3957     final ExecProcedureRequest request = ExecProcedureRequest.newBuilder()
3958         .setProcedure(builder.build()).build();
3959     // run the procedure on the master
3960     ExecProcedureResponse response = executeCallable(new MasterCallable<ExecProcedureResponse>(
3961         getConnection()) {
3962       @Override
3963       public ExecProcedureResponse call(int callTimeout) throws ServiceException {
3964         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
3965         controller.setCallTimeout(callTimeout);
3966         return master.execProcedureWithRet(controller, request);
3967       }
3968     });
3969 
3970     return response.hasReturnData() ? response.getReturnData().toByteArray() : null;
3971   }
3972   /**
3973    * Execute a distributed procedure on a cluster.
3974    *
3975    * @param signature A distributed procedure is uniquely identified
3976    * by its signature (default the root ZK node name of the procedure).
3977    * @param instance The instance name of the procedure. For some procedures, this parameter is
3978    * optional.
3979    * @param props Property/Value pairs of properties passing to the procedure
3980    * @throws IOException
3981    */
3982   @Override
3983   public void execProcedure(String signature, String instance,
3984       Map<String, String> props) throws IOException {
3985     ProcedureDescription.Builder builder = ProcedureDescription.newBuilder();
3986     builder.setSignature(signature).setInstance(instance);
3987     for (Entry<String, String> entry : props.entrySet()) {
3988       NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey())
3989           .setValue(entry.getValue()).build();
3990       builder.addConfiguration(pair);
3991     }
3992 
3993     final ExecProcedureRequest request = ExecProcedureRequest.newBuilder()
3994         .setProcedure(builder.build()).build();
3995     // run the procedure on the master
3996     ExecProcedureResponse response = executeCallable(new MasterCallable<ExecProcedureResponse>(
3997         getConnection()) {
3998       @Override
3999       public ExecProcedureResponse call(int callTimeout) throws ServiceException {
4000         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4001         controller.setCallTimeout(callTimeout);
4002         return master.execProcedure(controller, request);
4003       }
4004     });
4005 
4006     long start = EnvironmentEdgeManager.currentTime();
4007     long max = response.getExpectedTimeout();
4008     long maxPauseTime = max / this.numRetries;
4009     int tries = 0;
4010     LOG.debug("Waiting a max of " + max + " ms for procedure '" +
4011         signature + " : " + instance + "'' to complete. (max " + maxPauseTime + " ms per retry)");
4012     boolean done = false;
4013     while (tries == 0
4014         || ((EnvironmentEdgeManager.currentTime() - start) < max && !done)) {
4015       try {
4016         // sleep a backoff <= pauseTime amount
4017         long sleep = getPauseTime(tries++);
4018         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
4019         LOG.debug("(#" + tries + ") Sleeping: " + sleep +
4020           "ms while waiting for procedure completion.");
4021         Thread.sleep(sleep);
4022       } catch (InterruptedException e) {
4023         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
4024       }
4025       LOG.debug("Getting current status of procedure from master...");
4026       done = isProcedureFinished(signature, instance, props);
4027     }
4028     if (!done) {
4029       throw new IOException("Procedure '" + signature + " : " + instance
4030           + "' wasn't completed in expectedTime:" + max + " ms");
4031     }
4032   }
4033 
4034   /**
4035    * Check the current state of the specified procedure.
4036    * <p>
4037    * There are three possible states:
4038    * <ol>
4039    * <li>running - returns <tt>false</tt></li>
4040    * <li>finished - returns <tt>true</tt></li>
4041    * <li>finished with error - throws the exception that caused the procedure to fail</li>
4042    * </ol>
4043    * <p>
4044    *
4045    * @param signature The signature that uniquely identifies a procedure
4046    * @param instance The instance name of the procedure
4047    * @param props Property/Value pairs of properties passing to the procedure
4048    * @return true if the specified procedure is finished successfully, false if it is still running
4049    * @throws IOException if the specified procedure finished with error
4050    */
4051   @Override
4052   public boolean isProcedureFinished(String signature, String instance, Map<String, String> props)
4053       throws IOException {
4054     final ProcedureDescription.Builder builder = ProcedureDescription.newBuilder();
4055     builder.setSignature(signature).setInstance(instance);
4056     for (Entry<String, String> entry : props.entrySet()) {
4057       NameStringPair pair = NameStringPair.newBuilder().setName(entry.getKey())
4058           .setValue(entry.getValue()).build();
4059       builder.addConfiguration(pair);
4060     }
4061     final ProcedureDescription desc = builder.build();
4062     return executeCallable(
4063         new MasterCallable<IsProcedureDoneResponse>(getConnection()) {
4064           @Override
4065           public IsProcedureDoneResponse call(int callTimeout) throws ServiceException {
4066             PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4067             controller.setCallTimeout(callTimeout);
4068             return master.isProcedureDone(controller, IsProcedureDoneRequest
4069                 .newBuilder().setProcedure(desc).build());
4070           }
4071         }).getDone();
4072   }
4073 
4074   /**
4075    * Execute Restore/Clone snapshot and wait for the server to complete (blocking).
4076    * To check if the cloned table exists, use {@link #isTableAvailable} -- it is not safe to
4077    * create an HTable instance to this table before it is available.
4078    * @param snapshotName snapshot to restore
4079    * @param tableName table name to restore the snapshot on
4080    * @throws IOException if a remote or network exception occurs
4081    * @throws RestoreSnapshotException if snapshot failed to be restored
4082    * @throws IllegalArgumentException if the restore request is formatted incorrectly
4083    */
4084   private void internalRestoreSnapshot(final String snapshotName, final TableName
4085       tableName)
4086       throws IOException, RestoreSnapshotException {
4087     SnapshotDescription snapshot = SnapshotDescription.newBuilder()
4088         .setName(snapshotName).setTable(tableName.getNameAsString()).build();
4089 
4090     // actually restore the snapshot
4091     internalRestoreSnapshotAsync(snapshot);
4092 
4093     final IsRestoreSnapshotDoneRequest request = IsRestoreSnapshotDoneRequest.newBuilder()
4094         .setSnapshot(snapshot).build();
4095     IsRestoreSnapshotDoneResponse done = IsRestoreSnapshotDoneResponse.newBuilder()
4096         .setDone(false).buildPartial();
4097     final long maxPauseTime = 5000;
4098     int tries = 0;
4099     while (!done.getDone()) {
4100       try {
4101         // sleep a backoff <= pauseTime amount
4102         long sleep = getPauseTime(tries++);
4103         sleep = sleep > maxPauseTime ? maxPauseTime : sleep;
4104         LOG.debug(tries + ") Sleeping: " + sleep + " ms while we wait for snapshot restore to complete.");
4105         Thread.sleep(sleep);
4106       } catch (InterruptedException e) {
4107         throw (InterruptedIOException)new InterruptedIOException("Interrupted").initCause(e);
4108       }
4109       LOG.debug("Getting current status of snapshot restore from master...");
4110       done = executeCallable(new MasterCallable<IsRestoreSnapshotDoneResponse>(
4111           getConnection()) {
4112         @Override
4113         public IsRestoreSnapshotDoneResponse call(int callTimeout) throws ServiceException {
4114           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4115           controller.setCallTimeout(callTimeout);
4116           return master.isRestoreSnapshotDone(controller, request);
4117         }
4118       });
4119     }
4120     if (!done.getDone()) {
4121       throw new RestoreSnapshotException("Snapshot '" + snapshot.getName() + "' wasn't restored.");
4122     }
4123   }
4124 
4125   /**
4126    * Execute Restore/Clone snapshot and wait for the server to complete (asynchronous)
4127    * <p>
4128    * Only a single snapshot should be restored at a time, or results may be undefined.
4129    * @param snapshot snapshot to restore
4130    * @return response from the server indicating the max time to wait for the snapshot
4131    * @throws IOException if a remote or network exception occurs
4132    * @throws RestoreSnapshotException if snapshot failed to be restored
4133    * @throws IllegalArgumentException if the restore request is formatted incorrectly
4134    */
4135   private RestoreSnapshotResponse internalRestoreSnapshotAsync(final SnapshotDescription snapshot)
4136       throws IOException, RestoreSnapshotException {
4137     ClientSnapshotDescriptionUtils.assertSnapshotRequestIsValid(snapshot);
4138 
4139     final RestoreSnapshotRequest request = RestoreSnapshotRequest.newBuilder().setSnapshot(snapshot)
4140         .build();
4141 
4142     // run the snapshot restore on the master
4143     return executeCallable(new MasterCallable<RestoreSnapshotResponse>(getConnection()) {
4144       @Override
4145       public RestoreSnapshotResponse call(int callTimeout) throws ServiceException {
4146         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4147         controller.setCallTimeout(callTimeout);
4148         return master.restoreSnapshot(controller, request);
4149       }
4150     });
4151   }
4152 
4153   /**
4154    * List completed snapshots.
4155    * @return a list of snapshot descriptors for completed snapshots
4156    * @throws IOException if a network error occurs
4157    */
4158   @Override
4159   public List<SnapshotDescription> listSnapshots() throws IOException {
4160     return executeCallable(new MasterCallable<List<SnapshotDescription>>(getConnection()) {
4161       @Override
4162       public List<SnapshotDescription> call(int callTimeout) throws ServiceException {
4163         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4164         controller.setCallTimeout(callTimeout);
4165         return master.getCompletedSnapshots(controller,
4166           GetCompletedSnapshotsRequest.newBuilder().build()).getSnapshotsList();
4167       }
4168     });
4169   }
4170 
4171   /**
4172    * List all the completed snapshots matching the given regular expression.
4173    *
4174    * @param regex The regular expression to match against
4175    * @return - returns a List of SnapshotDescription
4176    * @throws IOException if a remote or network exception occurs
4177    */
4178   @Override
4179   public List<SnapshotDescription> listSnapshots(String regex) throws IOException {
4180     return listSnapshots(Pattern.compile(regex));
4181   }
4182 
4183   /**
4184    * List all the completed snapshots matching the given pattern.
4185    *
4186    * @param pattern The compiled regular expression to match against
4187    * @return - returns a List of SnapshotDescription
4188    * @throws IOException if a remote or network exception occurs
4189    */
4190   @Override
4191   public List<SnapshotDescription> listSnapshots(Pattern pattern) throws IOException {
4192     List<SnapshotDescription> matched = new LinkedList<SnapshotDescription>();
4193     List<SnapshotDescription> snapshots = listSnapshots();
4194     for (SnapshotDescription snapshot : snapshots) {
4195       if (pattern.matcher(snapshot.getName()).matches()) {
4196         matched.add(snapshot);
4197       }
4198     }
4199     return matched;
4200   }
4201 
4202   /**
4203    * List all the completed snapshots matching the given table name regular expression and snapshot
4204    * name regular expression.
4205    * @param tableNameRegex The table name regular expression to match against
4206    * @param snapshotNameRegex The snapshot name regular expression to match against
4207    * @return returns a List of completed SnapshotDescription
4208    * @throws IOException if a remote or network exception occurs
4209    */
4210   @Override
4211   public List<SnapshotDescription> listTableSnapshots(String tableNameRegex,
4212       String snapshotNameRegex) throws IOException {
4213     return listTableSnapshots(Pattern.compile(tableNameRegex), Pattern.compile(snapshotNameRegex));
4214   }
4215 
4216   /**
4217    * List all the completed snapshots matching the given table name regular expression and snapshot
4218    * name regular expression.
4219    * @param tableNamePattern The compiled table name regular expression to match against
4220    * @param snapshotNamePattern The compiled snapshot name regular expression to match against
4221    * @return returns a List of completed SnapshotDescription
4222    * @throws IOException if a remote or network exception occurs
4223    */
4224   @Override
4225   public List<SnapshotDescription> listTableSnapshots(Pattern tableNamePattern,
4226       Pattern snapshotNamePattern) throws IOException {
4227     TableName[] tableNames = listTableNames(tableNamePattern);
4228 
4229     List<SnapshotDescription> tableSnapshots = new LinkedList<SnapshotDescription>();
4230     List<SnapshotDescription> snapshots = listSnapshots(snapshotNamePattern);
4231 
4232     List<TableName> listOfTableNames = Arrays.asList(tableNames);
4233     for (SnapshotDescription snapshot : snapshots) {
4234       if (listOfTableNames.contains(TableName.valueOf(snapshot.getTable()))) {
4235         tableSnapshots.add(snapshot);
4236       }
4237     }
4238     return tableSnapshots;
4239   }
4240 
4241   /**
4242    * Delete an existing snapshot.
4243    * @param snapshotName name of the snapshot
4244    * @throws IOException if a remote or network exception occurs
4245    */
4246   @Override
4247   public void deleteSnapshot(final byte[] snapshotName) throws IOException {
4248     deleteSnapshot(Bytes.toString(snapshotName));
4249   }
4250 
4251   /**
4252    * Delete an existing snapshot.
4253    * @param snapshotName name of the snapshot
4254    * @throws IOException if a remote or network exception occurs
4255    */
4256   @Override
4257   public void deleteSnapshot(final String snapshotName) throws IOException {
4258     // make sure the snapshot is possibly valid
4259     TableName.isLegalFullyQualifiedTableName(Bytes.toBytes(snapshotName));
4260     // do the delete
4261     executeCallable(new MasterCallable<Void>(getConnection()) {
4262       @Override
4263       public Void call(int callTimeout) throws ServiceException {
4264         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4265         controller.setCallTimeout(callTimeout);
4266         master.deleteSnapshot(controller,
4267           DeleteSnapshotRequest.newBuilder().
4268             setSnapshot(SnapshotDescription.newBuilder().setName(snapshotName).build()).build()
4269         );
4270         return null;
4271       }
4272     });
4273   }
4274 
4275   /**
4276    * Delete existing snapshots whose names match the pattern passed.
4277    * @param regex The regular expression to match against
4278    * @throws IOException if a remote or network exception occurs
4279    */
4280   @Override
4281   public void deleteSnapshots(final String regex) throws IOException {
4282     deleteSnapshots(Pattern.compile(regex));
4283   }
4284 
4285   /**
4286    * Delete existing snapshots whose names match the pattern passed.
4287    * @param pattern pattern for names of the snapshot to match
4288    * @throws IOException if a remote or network exception occurs
4289    */
4290   @Override
4291   public void deleteSnapshots(final Pattern pattern) throws IOException {
4292     List<SnapshotDescription> snapshots = listSnapshots(pattern);
4293     for (final SnapshotDescription snapshot : snapshots) {
4294       try {
4295         internalDeleteSnapshot(snapshot);
4296       } catch (IOException ex) {
4297         LOG.info(
4298           "Failed to delete snapshot " + snapshot.getName() + " for table " + snapshot.getTable(),
4299           ex);
4300       }
4301     }
4302   }
4303 
4304   private void internalDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
4305     executeCallable(new MasterCallable<Void>(getConnection()) {
4306       @Override
4307       public Void call(int callTimeout) throws ServiceException {
4308         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4309         controller.setCallTimeout(callTimeout);
4310         this.master.deleteSnapshot(controller, DeleteSnapshotRequest.newBuilder()
4311           .setSnapshot(snapshot).build());
4312         return null;
4313       }
4314     });
4315   }
4316 
4317   /**
4318    * Delete all existing snapshots matching the given table name regular expression and snapshot
4319    * name regular expression.
4320    * @param tableNameRegex The table name regular expression to match against
4321    * @param snapshotNameRegex The snapshot name regular expression to match against
4322    * @throws IOException if a remote or network exception occurs
4323    */
4324   @Override
4325   public void deleteTableSnapshots(String tableNameRegex, String snapshotNameRegex)
4326       throws IOException {
4327     deleteTableSnapshots(Pattern.compile(tableNameRegex), Pattern.compile(snapshotNameRegex));
4328   }
4329 
4330   /**
4331    * Delete all existing snapshots matching the given table name regular expression and snapshot
4332    * name regular expression.
4333    * @param tableNamePattern The compiled table name regular expression to match against
4334    * @param snapshotNamePattern The compiled snapshot name regular expression to match against
4335    * @throws IOException if a remote or network exception occurs
4336    */
4337   @Override
4338   public void deleteTableSnapshots(Pattern tableNamePattern, Pattern snapshotNamePattern)
4339       throws IOException {
4340     List<SnapshotDescription> snapshots = listTableSnapshots(tableNamePattern, snapshotNamePattern);
4341     for (SnapshotDescription snapshot : snapshots) {
4342       try {
4343         internalDeleteSnapshot(snapshot);
4344         LOG.debug("Successfully deleted snapshot: " + snapshot.getName());
4345       } catch (IOException e) {
4346         LOG.error("Failed to delete snapshot: " + snapshot.getName(), e);
4347       }
4348     }
4349   }
4350 
4351   /**
4352    * Apply the new quota settings.
4353    * @param quota the quota settings
4354    * @throws IOException if a remote or network exception occurs
4355    */
4356   @Override
4357   public void setQuota(final QuotaSettings quota) throws IOException {
4358     executeCallable(new MasterCallable<Void>(getConnection()) {
4359       @Override
4360       public Void call(int callTimeout) throws ServiceException {
4361         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4362         controller.setCallTimeout(callTimeout);
4363         this.master.setQuota(controller, QuotaSettings.buildSetQuotaRequestProto(quota));
4364         return null;
4365       }
4366     });
4367   }
4368 
4369   /**
4370    * Return a Quota Scanner to list the quotas based on the filter.
4371    * @param filter the quota settings filter
4372    * @return the quota scanner
4373    * @throws IOException if a remote or network exception occurs
4374    */
4375   @Override
4376   public QuotaRetriever getQuotaRetriever(final QuotaFilter filter) throws IOException {
4377     return QuotaRetriever.open(conf, filter);
4378   }
4379 
4380   private <V> V executeCallable(MasterCallable<V> callable) throws IOException {
4381     return executeCallable(callable, rpcCallerFactory, operationTimeout, rpcTimeout);
4382   }
4383 
4384   private static <V> V executeCallable(MasterCallable<V> callable,
4385              RpcRetryingCallerFactory rpcCallerFactory, int operationTimeout, int rpcTimeout)
4386       throws IOException {
4387     RpcRetryingCaller<V> caller = rpcCallerFactory.newCaller(rpcTimeout);
4388     try {
4389       return caller.callWithRetries(callable, operationTimeout);
4390     } finally {
4391       callable.close();
4392     }
4393   }
4394 
4395   /**
4396    * Creates and returns a {@link com.google.protobuf.RpcChannel} instance
4397    * connected to the active master.
4398    *
4399    * <p>
4400    * The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published
4401    * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations:
4402    * </p>
4403    *
4404    * <div style="background-color: #cccccc; padding: 2px">
4405    * <blockquote><pre>
4406    * CoprocessorRpcChannel channel = myAdmin.coprocessorService();
4407    * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
4408    * MyCallRequest request = MyCallRequest.newBuilder()
4409    *     ...
4410    *     .build();
4411    * MyCallResponse response = service.myCall(null, request);
4412    * </pre></blockquote></div>
4413    *
4414    * @return A MasterCoprocessorRpcChannel instance
4415    */
4416   @Override
4417   public CoprocessorRpcChannel coprocessorService() {
4418     return new MasterCoprocessorRpcChannel(connection);
4419   }
4420 
4421   /**
4422    * Simple {@link Abortable}, throwing RuntimeException on abort.
4423    */
4424   private static class ThrowableAbortable implements Abortable {
4425 
4426     @Override
4427     public void abort(String why, Throwable e) {
4428       throw new RuntimeException(why, e);
4429     }
4430 
4431     @Override
4432     public boolean isAborted() {
4433       return true;
4434     }
4435   }
4436 
4437   /**
4438    * Creates and returns a {@link com.google.protobuf.RpcChannel} instance
4439    * connected to the passed region server.
4440    *
4441    * <p>
4442    * The obtained {@link com.google.protobuf.RpcChannel} instance can be used to access a published
4443    * coprocessor {@link com.google.protobuf.Service} using standard protobuf service invocations:
4444    * </p>
4445    *
4446    * <div style="background-color: #cccccc; padding: 2px">
4447    * <blockquote><pre>
4448    * CoprocessorRpcChannel channel = myAdmin.coprocessorService(serverName);
4449    * MyService.BlockingInterface service = MyService.newBlockingStub(channel);
4450    * MyCallRequest request = MyCallRequest.newBuilder()
4451    *     ...
4452    *     .build();
4453    * MyCallResponse response = service.myCall(null, request);
4454    * </pre></blockquote></div>
4455    *
4456    * @param sn the server name to which the endpoint call is made
4457    * @return A RegionServerCoprocessorRpcChannel instance
4458    */
4459   @Override
4460   public CoprocessorRpcChannel coprocessorService(ServerName sn) {
4461     return new RegionServerCoprocessorRpcChannel(connection, sn);
4462   }
4463 
4464   @Override
4465   public void updateConfiguration(ServerName server) throws IOException {
4466     try {
4467       this.connection.getAdmin(server).updateConfiguration(null,
4468         UpdateConfigurationRequest.getDefaultInstance());
4469     } catch (ServiceException e) {
4470       throw ProtobufUtil.getRemoteException(e);
4471     }
4472   }
4473 
4474   @Override
4475   public void updateConfiguration() throws IOException {
4476     for (ServerName server : this.getClusterStatus().getServers()) {
4477       updateConfiguration(server);
4478     }
4479   }
4480 
4481   @Override
4482   public int getMasterInfoPort() throws IOException {
4483     // TODO: Fix!  Reaching into internal implementation!!!!
4484     ConnectionManager.HConnectionImplementation connection =
4485         (ConnectionManager.HConnectionImplementation)this.connection;
4486     ZooKeeperKeepAliveConnection zkw = connection.getKeepAliveZooKeeperWatcher();
4487     try {
4488       return MasterAddressTracker.getMasterInfoPort(zkw);
4489     } catch (KeeperException e) {
4490       throw new IOException("Failed to get master info port from MasterAddressTracker", e);
4491     }
4492   }
4493 
4494   @Override
4495   public long getLastMajorCompactionTimestamp(final TableName tableName) throws IOException {
4496     return executeCallable(new MasterCallable<Long>(getConnection()) {
4497       @Override
4498       public Long call(int callTimeout) throws ServiceException {
4499         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4500         controller.setCallTimeout(callTimeout);
4501         MajorCompactionTimestampRequest req =
4502             MajorCompactionTimestampRequest.newBuilder()
4503                 .setTableName(ProtobufUtil.toProtoTableName(tableName)).build();
4504         return master.getLastMajorCompactionTimestamp(controller, req).getCompactionTimestamp();
4505       }
4506     });
4507   }
4508 
4509   @Override
4510   public long getLastMajorCompactionTimestampForRegion(final byte[] regionName) throws IOException {
4511     return executeCallable(new MasterCallable<Long>(getConnection()) {
4512       @Override
4513       public Long call(int callTimeout) throws ServiceException {
4514         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4515         controller.setCallTimeout(callTimeout);
4516         MajorCompactionTimestampForRegionRequest req =
4517             MajorCompactionTimestampForRegionRequest
4518                 .newBuilder()
4519                 .setRegion(
4520                   RequestConverter
4521                       .buildRegionSpecifier(RegionSpecifierType.REGION_NAME, regionName)).build();
4522         return master.getLastMajorCompactionTimestampForRegion(controller, req)
4523             .getCompactionTimestamp();
4524       }
4525     });
4526   }
4527 
4528   /**
4529    * Future that waits on a procedure result.
4530    * Returned by the async version of the Admin calls,
4531    * and used internally by the sync calls to wait on the result of the procedure.
4532    */
4533   @InterfaceAudience.Private
4534   @InterfaceStability.Evolving
4535   protected static class ProcedureFuture<V> implements Future<V> {
4536     private ExecutionException exception = null;
4537     private boolean procResultFound = false;
4538     private boolean done = false;
4539     private boolean cancelled = false;
4540     private boolean waitForOpResult = false;
4541     private V result = null;
4542 
4543     private final HBaseAdmin admin;
4544     private final Long procId;
4545 
4546     public ProcedureFuture(final HBaseAdmin admin, final Long procId) {
4547       this.admin = admin;
4548       this.procId = procId;
4549     }
4550 
4551     public ProcedureFuture(final HBaseAdmin admin, final Long procId,
4552         final boolean waitForOpResult) {
4553       this.admin = admin;
4554       this.procId = procId;
4555       this.waitForOpResult = waitForOpResult;
4556     }
4557 
4558     @Override
4559     public boolean cancel(boolean mayInterruptIfRunning) {
4560       AbortProcedureRequest abortProcRequest = AbortProcedureRequest.newBuilder()
4561           .setProcId(procId).setMayInterruptIfRunning(mayInterruptIfRunning).build();
4562       try {
4563         cancelled = abortProcedureResult(abortProcRequest).getIsProcedureAborted();
4564         if (cancelled) {
4565           done = true;
4566         }
4567       } catch (IOException e) {
4568         // Cancell thrown exception for some reason. At this time, we are not sure whether
4569         // the cancell succeeds or fails. We assume that it is failed, but print out a warning
4570         // for debugging purpose.
4571         LOG.warn(
4572           "Cancelling the procedure with procId=" + procId + " throws exception " + e.getMessage(),
4573           e);
4574         cancelled = false;
4575       }
4576       return cancelled;
4577     }
4578 
4579     @Override
4580     public boolean isCancelled() {
4581       return cancelled;
4582     }
4583 
4584     protected AbortProcedureResponse abortProcedureResult(
4585         final AbortProcedureRequest request) throws IOException {
4586       return admin.executeCallable(new MasterCallable<AbortProcedureResponse>(
4587           admin.getConnection()) {
4588         @Override
4589         public AbortProcedureResponse call(int callTimeout) throws ServiceException {
4590           PayloadCarryingRpcController controller = admin.getRpcControllerFactory().newController();
4591           controller.setCallTimeout(callTimeout);
4592           return master.abortProcedure(controller, request);
4593         }
4594       });
4595     }
4596 
4597     @Override
4598     public V get() throws InterruptedException, ExecutionException {
4599       // TODO: should we ever spin forever?
4600       throw new UnsupportedOperationException();
4601     }
4602 
4603     @Override
4604     public V get(long timeout, TimeUnit unit)
4605         throws InterruptedException, ExecutionException, TimeoutException {
4606       if (!done) {
4607         long deadlineTs = EnvironmentEdgeManager.currentTime() + unit.toMillis(timeout);
4608         try {
4609           try {
4610             // if the master support procedures, try to wait the result
4611             if (procId != null) {
4612               result = waitProcedureResult(procId, deadlineTs);
4613             }
4614             // if we don't have a proc result, try the compatibility wait
4615             if (!procResultFound || waitForOpResult) {
4616               result = waitOperationResult(deadlineTs);
4617             }
4618             result = postOperationResult(result, deadlineTs);
4619             done = true;
4620           } catch (IOException e) {
4621             result = postOpeartionFailure(e, deadlineTs);
4622             done = true;
4623           }
4624         } catch (IOException e) {
4625           exception = new ExecutionException(e);
4626           done = true;
4627         }
4628       }
4629       if (exception != null) {
4630         throw exception;
4631       }
4632       return result;
4633     }
4634 
4635     @Override
4636     public boolean isDone() {
4637       return done;
4638     }
4639 
4640     protected HBaseAdmin getAdmin() {
4641       return admin;
4642     }
4643 
4644     private V waitProcedureResult(long procId, long deadlineTs)
4645         throws IOException, TimeoutException, InterruptedException {
4646       GetProcedureResultRequest request = GetProcedureResultRequest.newBuilder()
4647           .setProcId(procId)
4648           .build();
4649 
4650       int tries = 0;
4651       IOException serviceEx = null;
4652       while (EnvironmentEdgeManager.currentTime() < deadlineTs) {
4653         GetProcedureResultResponse response = null;
4654         try {
4655           // Try to fetch the result
4656           response = getProcedureResult(request);
4657         } catch (IOException e) {
4658           serviceEx = unwrapException(e);
4659 
4660           // the master may be down
4661           LOG.warn("failed to get the procedure result procId=" + procId, serviceEx);
4662 
4663           // Not much to do, if we have a DoNotRetryIOException
4664           if (serviceEx instanceof DoNotRetryIOException ||
4665               serviceEx instanceof NeedUnmanagedConnectionException) {
4666             // TODO: looks like there is no way to unwrap this exception and get the proper
4667             // UnsupportedOperationException aside from looking at the message.
4668             // anyway, if we fail here we just failover to the compatibility side
4669             // and that is always a valid solution.
4670             LOG.warn("Proc-v2 is unsupported on this master: " + serviceEx.getMessage(), serviceEx);
4671             procResultFound = false;
4672             waitForOpResult = false;
4673             return null;
4674           }
4675         }
4676 
4677         // If the procedure is no longer running, we should have a result
4678         if (response != null && response.getState() != GetProcedureResultResponse.State.RUNNING) {
4679           procResultFound = response.getState() != GetProcedureResultResponse.State.NOT_FOUND;
4680           return convertResult(response);
4681         }
4682 
4683         try {
4684           Thread.sleep(getAdmin().getPauseTime(tries++));
4685         } catch (InterruptedException e) {
4686           throw new InterruptedException(
4687             "Interrupted while waiting for the result of proc " + procId);
4688         }
4689       }
4690       if (serviceEx != null) {
4691         throw serviceEx;
4692       } else {
4693         throw new TimeoutException("The procedure " + procId + " is still running");
4694       }
4695     }
4696 
4697     private static IOException unwrapException(IOException e) {
4698       if (e instanceof RemoteException) {
4699         return ((RemoteException)e).unwrapRemoteException();
4700       }
4701       return e;
4702     }
4703 
4704     protected GetProcedureResultResponse getProcedureResult(final GetProcedureResultRequest request)
4705         throws IOException {
4706       return admin.executeCallable(new MasterCallable<GetProcedureResultResponse>(
4707           admin.getConnection()) {
4708         @Override
4709         public GetProcedureResultResponse call(int callTimeout) throws ServiceException {
4710           return master.getProcedureResult(null, request);
4711         }
4712       });
4713     }
4714 
4715     /**
4716      * Convert the procedure result response to a specified type.
4717      * @param response the procedure result object to parse
4718      * @return the result data of the procedure.
4719      */
4720     protected V convertResult(final GetProcedureResultResponse response) throws IOException {
4721       if (response.hasException()) {
4722         throw ForeignExceptionUtil.toIOException(response.getException());
4723       }
4724       return null;
4725     }
4726 
4727     /**
4728      * Fallback implementation in case the procedure is not supported by the server.
4729      * It should try to wait until the operation is completed.
4730      * @param deadlineTs the timestamp after which this method should throw a TimeoutException
4731      * @return the result data of the operation
4732      */
4733     protected V waitOperationResult(final long deadlineTs)
4734         throws IOException, TimeoutException {
4735       return null;
4736     }
4737 
4738     /**
4739      * Called after the operation is completed and the result fetched.
4740      * this allows to perform extra steps after the procedure is completed.
4741      * it allows to apply transformations to the result that will be returned by get().
4742      * @param result the result of the procedure
4743      * @param deadlineTs the timestamp after which this method should throw a TimeoutException
4744      * @return the result of the procedure, which may be the same as the passed one
4745      */
4746     protected V postOperationResult(final V result, final long deadlineTs)
4747         throws IOException, TimeoutException {
4748       return result;
4749     }
4750 
4751     /**
4752      * Called after the operation is terminated with a failure.
4753      * this allows to perform extra steps after the procedure is terminated.
4754      * it allows to apply transformations to the result that will be returned by get().
4755      * The default implementation will rethrow the exception
4756      * @param exception the exception got from fetching the result
4757      * @param deadlineTs the timestamp after which this method should throw a TimeoutException
4758      * @return the result of the procedure, which may be the same as the passed one
4759      */
4760     protected V postOpeartionFailure(final IOException exception, final long deadlineTs)
4761         throws IOException, TimeoutException {
4762       throw exception;
4763     }
4764 
4765     protected interface WaitForStateCallable {
4766       boolean checkState(int tries) throws IOException;
4767       void throwInterruptedException() throws InterruptedIOException;
4768       void throwTimeoutException(long elapsed) throws TimeoutException;
4769     }
4770 
4771     protected void waitForState(final long deadlineTs, final WaitForStateCallable callable)
4772         throws IOException, TimeoutException {
4773       int tries = 0;
4774       IOException serverEx = null;
4775       long startTime = EnvironmentEdgeManager.currentTime();
4776       while (EnvironmentEdgeManager.currentTime() < deadlineTs) {
4777         serverEx = null;
4778         try {
4779           if (callable.checkState(tries)) {
4780             return;
4781           }
4782         } catch (IOException e) {
4783           serverEx = e;
4784         }
4785         try {
4786           Thread.sleep(getAdmin().getPauseTime(tries++));
4787         } catch (InterruptedException e) {
4788           callable.throwInterruptedException();
4789         }
4790       }
4791       if (serverEx != null) {
4792         throw unwrapException(serverEx);
4793       } else {
4794         callable.throwTimeoutException(EnvironmentEdgeManager.currentTime() - startTime);
4795       }
4796     }
4797   }
4798 
4799   @Override
4800   public List<SecurityCapability> getSecurityCapabilities() throws IOException {
4801     try {
4802       return executeCallable(new MasterCallable<List<SecurityCapability>>(getConnection()) {
4803         @Override
4804         public List<SecurityCapability> call(int callTimeout) throws ServiceException {
4805           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
4806           controller.setCallTimeout(callTimeout);
4807           SecurityCapabilitiesRequest req = SecurityCapabilitiesRequest.newBuilder().build();
4808           return ProtobufUtil.toSecurityCapabilityList(
4809             master.getSecurityCapabilities(controller, req).getCapabilitiesList());
4810         }
4811       });
4812     } catch (IOException e) {
4813       if (e instanceof RemoteException) {
4814         e = ((RemoteException)e).unwrapRemoteException();
4815       }
4816       throw e;
4817     }
4818   }
4819 
4820   @Override
4821   public boolean[] setSplitOrMergeEnabled(final boolean enabled, final boolean synchronous,
4822                                           final MasterSwitchType... switchTypes)
4823     throws IOException {
4824     return executeCallable(new MasterCallable<boolean[]>(getConnection()) {
4825       @Override
4826       public boolean[] call(int callTimeout) throws ServiceException {
4827         MasterProtos.SetSplitOrMergeEnabledResponse response = master.setSplitOrMergeEnabled(null,
4828           RequestConverter.buildSetSplitOrMergeEnabledRequest(enabled, synchronous, switchTypes));
4829         boolean[] result = new boolean[switchTypes.length];
4830         int i = 0;
4831         for (Boolean prevValue : response.getPrevValueList()) {
4832           result[i++] = prevValue;
4833         }
4834         return result;
4835       }
4836     });
4837   }
4838 
4839   @Override
4840   public boolean isSplitOrMergeEnabled(final MasterSwitchType switchType) throws IOException {
4841     return executeCallable(new MasterCallable<Boolean>(getConnection()) {
4842       @Override
4843       public Boolean call(int callTimeout) throws ServiceException {
4844         return master.isSplitOrMergeEnabled(null,
4845           RequestConverter.buildIsSplitOrMergeEnabledRequest(switchType)).getEnabled();
4846       }
4847     });
4848   }
4849 
4850   private RpcControllerFactory getRpcControllerFactory() {
4851     return rpcControllerFactory;
4852   }
4853 }