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