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