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