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.util.ArrayList;
24  import java.util.Collections;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.NavigableMap;
28  import java.util.TreeMap;
29  import java.util.concurrent.Callable;
30  import java.util.concurrent.ExecutionException;
31  import java.util.concurrent.ExecutorService;
32  import java.util.concurrent.Future;
33  import java.util.concurrent.SynchronousQueue;
34  import java.util.concurrent.ThreadPoolExecutor;
35  import java.util.concurrent.TimeUnit;
36  
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.hadoop.hbase.classification.InterfaceAudience;
40  import org.apache.hadoop.hbase.classification.InterfaceStability;
41  import org.apache.hadoop.conf.Configuration;
42  import org.apache.hadoop.hbase.Cell;
43  import org.apache.hadoop.hbase.HBaseConfiguration;
44  import org.apache.hadoop.hbase.DoNotRetryIOException;
45  import org.apache.hadoop.hbase.HConstants;
46  import org.apache.hadoop.hbase.HRegionInfo;
47  import org.apache.hadoop.hbase.HRegionLocation;
48  import org.apache.hadoop.hbase.HTableDescriptor;
49  import org.apache.hadoop.hbase.KeyValueUtil;
50  import org.apache.hadoop.hbase.ServerName;
51  import org.apache.hadoop.hbase.TableName;
52  import org.apache.hadoop.hbase.client.AsyncProcess.AsyncRequestFuture;
53  import org.apache.hadoop.hbase.client.coprocessor.Batch;
54  import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
55  import org.apache.hadoop.hbase.filter.BinaryComparator;
56  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
57  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
58  import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
59  import org.apache.hadoop.hbase.ipc.RegionCoprocessorRpcChannel;
60  import org.apache.hadoop.hbase.ipc.RpcControllerFactory;
61  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
62  import org.apache.hadoop.hbase.protobuf.RequestConverter;
63  import org.apache.hadoop.hbase.protobuf.ResponseConverter;
64  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
65  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MultiRequest;
66  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateRequest;
67  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutateResponse;
68  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionAction;
69  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.CompareType;
70  import org.apache.hadoop.hbase.util.Bytes;
71  import org.apache.hadoop.hbase.util.Pair;
72  import org.apache.hadoop.hbase.util.ReflectionUtils;
73  import org.apache.hadoop.hbase.util.Threads;
74  
75  import com.google.common.annotations.VisibleForTesting;
76  import com.google.protobuf.Descriptors;
77  import com.google.protobuf.Message;
78  import com.google.protobuf.Service;
79  import com.google.protobuf.ServiceException;
80  
81  /**
82   * An implementation of {@link Table}. Used to communicate with a single HBase table.
83   * Lightweight. Get as needed and just close when done.
84   * Instances of this class SHOULD NOT be constructed directly.
85   * Obtain an instance via {@link Connection}. See {@link ConnectionFactory}
86   * class comment for an example of how.
87   *
88   * <p>This class is NOT thread safe for reads nor writes.
89   * In the case of writes (Put, Delete), the underlying write buffer can
90   * be corrupted if multiple threads contend over a single HTable instance.
91   * In the case of reads, some fields used by a Scan are shared among all threads.
92   *
93   * <p>HTable is no longer a client API. Use {@link Table} instead. It is marked
94   * InterfaceAudience.Private as of hbase-1.0.0 indicating that this is an
95   * HBase-internal class as defined in
96   * <a href="https://hadoop.apache.org/docs/current/hadoop-project-dist/hadoop-common/InterfaceClassification.html">Hadoop
97   * Interface Classification</a>. There are no guarantees for backwards
98   * source / binary compatibility and methods or the class can
99   * change or go away without deprecation.
100  * <p>Near all methods of this * class made it out to the new {@link Table}
101  * Interface or were * instantiations of methods defined in {@link HTableInterface}.
102  * A few did not. Namely, the {@link #getStartEndKeys}, {@link #getEndKeys},
103  * and {@link #getStartKeys} methods. These three methods are available
104  * in {@link RegionLocator} as of 1.0.0 but were NOT marked as
105  * deprecated when we released 1.0.0. In spite of this oversight on our
106  * part, these methods will be removed in 2.0.0.
107  *
108  * @see Table
109  * @see Admin
110  * @see Connection
111  * @see ConnectionFactory
112  */
113 @InterfaceAudience.Private
114 @InterfaceStability.Stable
115 public class HTable implements HTableInterface, RegionLocator {
116   private static final Log LOG = LogFactory.getLog(HTable.class);
117   protected ClusterConnection connection;
118   private final TableName tableName;
119   private volatile Configuration configuration;
120   private ConnectionConfiguration connConfiguration;
121   protected BufferedMutatorImpl mutator;
122   private boolean autoFlush = true;
123   private boolean closed = false;
124   protected int scannerCaching;
125   protected long scannerMaxResultSize;
126   private ExecutorService pool;  // For Multi & Scan
127   private int operationTimeout; // global timeout for each blocking method with retrying rpc
128   private int rpcTimeout; // timeout for each rpc request
129   private final boolean cleanupPoolOnClose; // shutdown the pool in close()
130   private final boolean cleanupConnectionOnClose; // close the connection in close()
131   private Consistency defaultConsistency = Consistency.STRONG;
132   private HRegionLocator locator;
133 
134   /** The Async process for batch */
135   protected AsyncProcess multiAp;
136   private RpcRetryingCallerFactory rpcCallerFactory;
137   private RpcControllerFactory rpcControllerFactory;
138 
139   /**
140    * Creates an object to access a HBase table.
141    * @param conf Configuration object to use.
142    * @param tableName Name of the table.
143    * @throws IOException if a remote or network exception occurs
144    * @deprecated Constructing HTable objects manually has been deprecated. Please use
145    * {@link Connection} to instantiate a {@link Table} instead.
146    */
147   @Deprecated
148   public HTable(Configuration conf, final String tableName)
149   throws IOException {
150     this(conf, TableName.valueOf(tableName));
151   }
152 
153   /**
154    * Creates an object to access a HBase table.
155    * @param conf Configuration object to use.
156    * @param tableName Name of the table.
157    * @throws IOException if a remote or network exception occurs
158    * @deprecated Constructing HTable objects manually has been deprecated. Please use
159    * {@link Connection} to instantiate a {@link Table} instead.
160    */
161   @Deprecated
162   public HTable(Configuration conf, final byte[] tableName)
163   throws IOException {
164     this(conf, TableName.valueOf(tableName));
165   }
166 
167   /**
168    * Creates an object to access a HBase table.
169    * @param conf Configuration object to use.
170    * @param tableName table name pojo
171    * @throws IOException if a remote or network exception occurs
172    * @deprecated Constructing HTable objects manually has been deprecated. Please use
173    * {@link Connection} to instantiate a {@link Table} instead.
174    */
175   @Deprecated
176   public HTable(Configuration conf, final TableName tableName)
177   throws IOException {
178     this.tableName = tableName;
179     this.cleanupPoolOnClose = this.cleanupConnectionOnClose = true;
180     if (conf == null) {
181       this.connection = null;
182       return;
183     }
184     this.connection = ConnectionManager.getConnectionInternal(conf);
185     this.configuration = conf;
186 
187     this.pool = getDefaultExecutor(conf);
188     this.finishSetup();
189   }
190 
191   /**
192    * Creates an object to access a HBase table.
193    * @param tableName Name of the table.
194    * @param connection HConnection to be used.
195    * @throws IOException if a remote or network exception occurs
196    * @deprecated Do not use.
197    */
198   @Deprecated
199   public HTable(TableName tableName, Connection connection) throws IOException {
200     this.tableName = tableName;
201     this.cleanupPoolOnClose = true;
202     this.cleanupConnectionOnClose = false;
203     this.connection = (ClusterConnection)connection;
204     this.configuration = connection.getConfiguration();
205 
206     this.pool = getDefaultExecutor(this.configuration);
207     this.finishSetup();
208   }
209 
210   // Marked Private @since 1.0
211   @InterfaceAudience.Private
212   public static ThreadPoolExecutor getDefaultExecutor(Configuration conf) {
213     int maxThreads = conf.getInt("hbase.htable.threads.max", Integer.MAX_VALUE);
214     if (maxThreads == 0) {
215       maxThreads = 1; // is there a better default?
216     }
217     long keepAliveTime = conf.getLong("hbase.htable.threads.keepalivetime", 60);
218 
219     // Using the "direct handoff" approach, new threads will only be created
220     // if it is necessary and will grow unbounded. This could be bad but in HCM
221     // we only create as many Runnables as there are region servers. It means
222     // it also scales when new region servers are added.
223     ThreadPoolExecutor pool = new ThreadPoolExecutor(1, maxThreads, keepAliveTime, TimeUnit.SECONDS,
224         new SynchronousQueue<Runnable>(), Threads.newDaemonThreadFactory("htable"));
225     pool.allowCoreThreadTimeOut(true);
226     return pool;
227   }
228 
229   /**
230    * Creates an object to access a HBase table.
231    * @param conf Configuration object to use.
232    * @param tableName Name of the table.
233    * @param pool ExecutorService to be used.
234    * @throws IOException if a remote or network exception occurs
235    * @deprecated Constructing HTable objects manually has been deprecated. Please use
236    * {@link Connection} to instantiate a {@link Table} instead.
237    */
238   @Deprecated
239   public HTable(Configuration conf, final byte[] tableName, final ExecutorService pool)
240       throws IOException {
241     this(conf, TableName.valueOf(tableName), pool);
242   }
243 
244   /**
245    * Creates an object to access a HBase table.
246    * @param conf Configuration object to use.
247    * @param tableName Name of the table.
248    * @param pool ExecutorService to be used.
249    * @throws IOException if a remote or network exception occurs
250    * @deprecated Constructing HTable objects manually has been deprecated. Please use
251    * {@link Connection} to instantiate a {@link Table} instead.
252    */
253   @Deprecated
254   public HTable(Configuration conf, final TableName tableName, final ExecutorService pool)
255       throws IOException {
256     this.connection = ConnectionManager.getConnectionInternal(conf);
257     this.configuration = conf;
258     this.pool = pool;
259     if (pool == null) {
260       this.pool = getDefaultExecutor(conf);
261       this.cleanupPoolOnClose = true;
262     } else {
263       this.cleanupPoolOnClose = false;
264     }
265     this.tableName = tableName;
266     this.cleanupConnectionOnClose = true;
267     this.finishSetup();
268   }
269 
270   /**
271    * Creates an object to access a HBase table.
272    * @param tableName Name of the table.
273    * @param connection HConnection to be used.
274    * @param pool ExecutorService to be used.
275    * @throws IOException if a remote or network exception occurs.
276    * @deprecated Do not use, internal ctor.
277    */
278   @Deprecated
279   public HTable(final byte[] tableName, final Connection connection,
280       final ExecutorService pool) throws IOException {
281     this(TableName.valueOf(tableName), connection, pool);
282   }
283 
284   /** @deprecated Do not use, internal ctor. */
285   @Deprecated
286   public HTable(TableName tableName, final Connection connection,
287       final ExecutorService pool) throws IOException {
288     this(tableName, (ClusterConnection)connection, null, null, null, pool);
289   }
290 
291   /**
292    * Creates an object to access a HBase table.
293    * Used by HBase internally.  DO NOT USE. See {@link ConnectionFactory} class comment for how to
294    * get a {@link Table} instance (use {@link Table} instead of {@link HTable}).
295    * @param tableName Name of the table.
296    * @param connection HConnection to be used.
297    * @param pool ExecutorService to be used.
298    * @throws IOException if a remote or network exception occurs
299    */
300   @InterfaceAudience.Private
301   public HTable(TableName tableName, final ClusterConnection connection,
302       final ConnectionConfiguration tableConfig,
303       final RpcRetryingCallerFactory rpcCallerFactory,
304       final RpcControllerFactory rpcControllerFactory,
305       final ExecutorService pool) throws IOException {
306     if (connection == null || connection.isClosed()) {
307       throw new IllegalArgumentException("Connection is null or closed.");
308     }
309     this.tableName = tableName;
310     this.cleanupConnectionOnClose = false;
311     this.connection = connection;
312     this.configuration = connection.getConfiguration();
313     this.connConfiguration = tableConfig;
314     this.pool = pool;
315     if (pool == null) {
316       this.pool = getDefaultExecutor(this.configuration);
317       this.cleanupPoolOnClose = true;
318     } else {
319       this.cleanupPoolOnClose = false;
320     }
321 
322     this.rpcCallerFactory = rpcCallerFactory;
323     this.rpcControllerFactory = rpcControllerFactory;
324 
325     this.finishSetup();
326   }
327 
328   /**
329    * For internal testing. Uses Connection provided in {@code params}.
330    * @throws IOException
331    */
332   @VisibleForTesting
333   protected HTable(ClusterConnection conn, BufferedMutatorParams params) throws IOException {
334     connection = conn;
335     tableName = params.getTableName();
336     connConfiguration = new ConnectionConfiguration(connection.getConfiguration());
337     cleanupPoolOnClose = false;
338     cleanupConnectionOnClose = false;
339     // used from tests, don't trust the connection is real
340     this.mutator = new BufferedMutatorImpl(conn, null, null, params);
341   }
342 
343   /**
344    * @return maxKeyValueSize from configuration.
345    */
346   public static int getMaxKeyValueSize(Configuration conf) {
347     return conf.getInt("hbase.client.keyvalue.maxsize", -1);
348   }
349 
350   /**
351    * setup this HTable's parameter based on the passed configuration
352    */
353   private void finishSetup() throws IOException {
354     if (connConfiguration == null) {
355       connConfiguration = new ConnectionConfiguration(configuration);
356     }
357     this.operationTimeout = tableName.isSystemTable() ?
358         connConfiguration.getMetaOperationTimeout() : connConfiguration.getOperationTimeout();
359     this.rpcTimeout = configuration.getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
360         HConstants.DEFAULT_HBASE_RPC_TIMEOUT);
361     this.scannerCaching = connConfiguration.getScannerCaching();
362     this.scannerMaxResultSize = connConfiguration.getScannerMaxResultSize();
363     if (this.rpcCallerFactory == null) {
364       this.rpcCallerFactory = connection.getNewRpcRetryingCallerFactory(configuration);
365     }
366     if (this.rpcControllerFactory == null) {
367       this.rpcControllerFactory = RpcControllerFactory.instantiate(configuration);
368     }
369 
370     // puts need to track errors globally due to how the APIs currently work.
371     multiAp = this.connection.getAsyncProcess();
372 
373     this.closed = false;
374 
375     this.locator = new HRegionLocator(tableName, connection);
376   }
377 
378   /**
379    * {@inheritDoc}
380    */
381   @Override
382   public Configuration getConfiguration() {
383     return configuration;
384   }
385 
386   /**
387    * Tells whether or not a table is enabled or not. This method creates a
388    * new HBase configuration, so it might make your unit tests fail due to
389    * incorrect ZK client port.
390    * @param tableName Name of table to check.
391    * @return {@code true} if table is online.
392    * @throws IOException if a remote or network exception occurs
393    * @deprecated use {@link HBaseAdmin#isTableEnabled(byte[])}
394    */
395   @Deprecated
396   public static boolean isTableEnabled(String tableName) throws IOException {
397     return isTableEnabled(TableName.valueOf(tableName));
398   }
399 
400   /**
401    * Tells whether or not a table is enabled or not. This method creates a
402    * new HBase configuration, so it might make your unit tests fail due to
403    * incorrect ZK client port.
404    * @param tableName Name of table to check.
405    * @return {@code true} if table is online.
406    * @throws IOException if a remote or network exception occurs
407    * @deprecated use {@link HBaseAdmin#isTableEnabled(byte[])}
408    */
409   @Deprecated
410   public static boolean isTableEnabled(byte[] tableName) throws IOException {
411     return isTableEnabled(TableName.valueOf(tableName));
412   }
413 
414   /**
415    * Tells whether or not a table is enabled or not. This method creates a
416    * new HBase configuration, so it might make your unit tests fail due to
417    * incorrect ZK client port.
418    * @param tableName Name of table to check.
419    * @return {@code true} if table is online.
420    * @throws IOException if a remote or network exception occurs
421    * @deprecated use {@link HBaseAdmin#isTableEnabled(byte[])}
422    */
423   @Deprecated
424   public static boolean isTableEnabled(TableName tableName) throws IOException {
425     return isTableEnabled(HBaseConfiguration.create(), tableName);
426   }
427 
428   /**
429    * Tells whether or not a table is enabled or not.
430    * @param conf The Configuration object to use.
431    * @param tableName Name of table to check.
432    * @return {@code true} if table is online.
433    * @throws IOException if a remote or network exception occurs
434    * @deprecated use {@link HBaseAdmin#isTableEnabled(byte[])}
435    */
436   @Deprecated
437   public static boolean isTableEnabled(Configuration conf, String tableName)
438   throws IOException {
439     return isTableEnabled(conf, TableName.valueOf(tableName));
440   }
441 
442   /**
443    * Tells whether or not a table is enabled or not.
444    * @param conf The Configuration object to use.
445    * @param tableName Name of table to check.
446    * @return {@code true} if table is online.
447    * @throws IOException if a remote or network exception occurs
448    * @deprecated use {@link HBaseAdmin#isTableEnabled(byte[])}
449    */
450   @Deprecated
451   public static boolean isTableEnabled(Configuration conf, byte[] tableName)
452   throws IOException {
453     return isTableEnabled(conf, TableName.valueOf(tableName));
454   }
455 
456   /**
457    * Tells whether or not a table is enabled or not.
458    * @param conf The Configuration object to use.
459    * @param tableName Name of table to check.
460    * @return {@code true} if table is online.
461    * @throws IOException if a remote or network exception occurs
462    * @deprecated use {@link HBaseAdmin#isTableEnabled(org.apache.hadoop.hbase.TableName tableName)}
463    */
464   @Deprecated
465   public static boolean isTableEnabled(Configuration conf,
466       final TableName tableName) throws IOException {
467     return HConnectionManager.execute(new HConnectable<Boolean>(conf) {
468       @Override
469       public Boolean connect(HConnection connection) throws IOException {
470         return connection.isTableEnabled(tableName);
471       }
472     });
473   }
474 
475   /**
476    * Find region location hosting passed row using cached info
477    * @param row Row to find.
478    * @return The location of the given row.
479    * @throws IOException if a remote or network exception occurs
480    * @deprecated Use {@link RegionLocator#getRegionLocation(byte[])}
481    */
482   @Deprecated
483   public HRegionLocation getRegionLocation(final String row)
484   throws IOException {
485     return getRegionLocation(Bytes.toBytes(row), false);
486   }
487 
488   /**
489    * @deprecated Use {@link RegionLocator#getRegionLocation(byte[])} instead.
490    */
491   @Override
492   @Deprecated
493   public HRegionLocation getRegionLocation(final byte [] row)
494   throws IOException {
495     return locator.getRegionLocation(row);
496   }
497 
498   /**
499    * @deprecated Use {@link RegionLocator#getRegionLocation(byte[], boolean)} instead.
500    */
501   @Override
502   @Deprecated
503   public HRegionLocation getRegionLocation(final byte [] row, boolean reload)
504   throws IOException {
505     return locator.getRegionLocation(row, reload);
506   }
507 
508   /**
509    * {@inheritDoc}
510    */
511   @Override
512   public byte [] getTableName() {
513     return this.tableName.getName();
514   }
515 
516   @Override
517   public TableName getName() {
518     return tableName;
519   }
520 
521   /**
522    * <em>INTERNAL</em> Used by unit tests and tools to do low-level
523    * manipulations.
524    * @return An HConnection instance.
525    * @deprecated This method will be changed from public to package protected.
526    */
527   // TODO(tsuna): Remove this.  Unit tests shouldn't require public helpers.
528   @Deprecated
529   @VisibleForTesting
530   public HConnection getConnection() {
531     return this.connection;
532   }
533 
534   /**
535    * Gets the number of rows that a scanner will fetch at once.
536    * <p>
537    * The default value comes from {@code hbase.client.scanner.caching}.
538    * @deprecated Use {@link Scan#setCaching(int)} and {@link Scan#getCaching()}
539    */
540   @Deprecated
541   public int getScannerCaching() {
542     return scannerCaching;
543   }
544 
545   /**
546    * Kept in 0.96 for backward compatibility
547    * @deprecated  since 0.96. This is an internal buffer that should not be read nor write.
548    */
549   @Deprecated
550   public List<Row> getWriteBuffer() {
551     return mutator == null ? null : mutator.getWriteBuffer();
552   }
553 
554   /**
555    * Sets the number of rows that a scanner will fetch at once.
556    * <p>
557    * This will override the value specified by
558    * {@code hbase.client.scanner.caching}.
559    * Increasing this value will reduce the amount of work needed each time
560    * {@code next()} is called on a scanner, at the expense of memory use
561    * (since more rows will need to be maintained in memory by the scanners).
562    * @param scannerCaching the number of rows a scanner will fetch at once.
563    * @deprecated Use {@link Scan#setCaching(int)}
564    */
565   @Deprecated
566   public void setScannerCaching(int scannerCaching) {
567     this.scannerCaching = scannerCaching;
568   }
569 
570   /**
571    * {@inheritDoc}
572    */
573   @Override
574   public HTableDescriptor getTableDescriptor() throws IOException {
575     HTableDescriptor htd = HBaseAdmin.getTableDescriptor(tableName, connection, rpcCallerFactory,
576       rpcControllerFactory, operationTimeout, rpcTimeout);
577     if (htd != null) {
578       return new UnmodifyableHTableDescriptor(htd);
579     }
580     return null;
581   }
582 
583   /**
584    * To be removed in 2.0.0.
585    * @deprecated Since 1.1.0. Use {@link RegionLocator#getStartEndKeys()} instead
586    */
587   @Override
588   @Deprecated
589   public byte [][] getStartKeys() throws IOException {
590     return locator.getStartKeys();
591   }
592 
593   /**
594    * To be removed in 2.0.0.
595    * @deprecated Since 1.1.0. Use {@link RegionLocator#getEndKeys()} instead;
596    */
597   @Override
598   @Deprecated
599   public byte[][] getEndKeys() throws IOException {
600     return locator.getEndKeys();
601   }
602 
603   /**
604    * To be removed in 2.0.0.
605    * @deprecated Since 1.1.0. Use {@link RegionLocator#getStartEndKeys()} instead;
606    */
607   @Override
608   @Deprecated
609   public Pair<byte[][],byte[][]> getStartEndKeys() throws IOException {
610     return locator.getStartEndKeys();
611   }
612 
613   /**
614    * Gets all the regions and their address for this table.
615    * <p>
616    * This is mainly useful for the MapReduce integration.
617    * @return A map of HRegionInfo with it's server address
618    * @throws IOException if a remote or network exception occurs
619    * @deprecated This is no longer a public API.  Use {@link #getAllRegionLocations()} instead.
620    */
621   @Deprecated
622   public NavigableMap<HRegionInfo, ServerName> getRegionLocations() throws IOException {
623     // TODO: Odd that this returns a Map of HRI to SN whereas getRegionLocator, singular, returns an HRegionLocation.
624     return MetaScanner.allTableRegions(this.connection, getName());
625   }
626 
627   /**
628    * Gets all the regions and their address for this table.
629    * <p>
630    * This is mainly useful for the MapReduce integration.
631    * @return A map of HRegionInfo with it's server address
632    * @throws IOException if a remote or network exception occurs
633    *
634    * @deprecated Use {@link RegionLocator#getAllRegionLocations()} instead;
635    */
636   @Override
637   @Deprecated
638   public List<HRegionLocation> getAllRegionLocations() throws IOException {
639     return locator.getAllRegionLocations();
640   }
641 
642   /**
643    * Get the corresponding regions for an arbitrary range of keys.
644    * <p>
645    * @param startKey Starting row in range, inclusive
646    * @param endKey Ending row in range, exclusive
647    * @return A list of HRegionLocations corresponding to the regions that
648    * contain the specified range
649    * @throws IOException if a remote or network exception occurs
650    * @deprecated This is no longer a public API
651    */
652   @Deprecated
653   public List<HRegionLocation> getRegionsInRange(final byte [] startKey,
654     final byte [] endKey) throws IOException {
655     return getRegionsInRange(startKey, endKey, false);
656   }
657 
658   /**
659    * Get the corresponding regions for an arbitrary range of keys.
660    * <p>
661    * @param startKey Starting row in range, inclusive
662    * @param endKey Ending row in range, exclusive
663    * @param reload true to reload information or false to use cached information
664    * @return A list of HRegionLocations corresponding to the regions that
665    * contain the specified range
666    * @throws IOException if a remote or network exception occurs
667    * @deprecated This is no longer a public API
668    */
669   @Deprecated
670   public List<HRegionLocation> getRegionsInRange(final byte [] startKey,
671       final byte [] endKey, final boolean reload) throws IOException {
672     return getKeysAndRegionsInRange(startKey, endKey, false, reload).getSecond();
673   }
674 
675   /**
676    * Get the corresponding start keys and regions for an arbitrary range of
677    * keys.
678    * <p>
679    * @param startKey Starting row in range, inclusive
680    * @param endKey Ending row in range
681    * @param includeEndKey true if endRow is inclusive, false if exclusive
682    * @return A pair of list of start keys and list of HRegionLocations that
683    *         contain the specified range
684    * @throws IOException if a remote or network exception occurs
685    * @deprecated This is no longer a public API
686    */
687   @Deprecated
688   private Pair<List<byte[]>, List<HRegionLocation>> getKeysAndRegionsInRange(
689       final byte[] startKey, final byte[] endKey, final boolean includeEndKey)
690       throws IOException {
691     return getKeysAndRegionsInRange(startKey, endKey, includeEndKey, false);
692   }
693 
694   /**
695    * Get the corresponding start keys and regions for an arbitrary range of
696    * keys.
697    * <p>
698    * @param startKey Starting row in range, inclusive
699    * @param endKey Ending row in range
700    * @param includeEndKey true if endRow is inclusive, false if exclusive
701    * @param reload true to reload information or false to use cached information
702    * @return A pair of list of start keys and list of HRegionLocations that
703    *         contain the specified range
704    * @throws IOException if a remote or network exception occurs
705    * @deprecated This is no longer a public API
706    */
707   @Deprecated
708   private Pair<List<byte[]>, List<HRegionLocation>> getKeysAndRegionsInRange(
709       final byte[] startKey, final byte[] endKey, final boolean includeEndKey,
710       final boolean reload) throws IOException {
711     final boolean endKeyIsEndOfTable = Bytes.equals(endKey,HConstants.EMPTY_END_ROW);
712     if ((Bytes.compareTo(startKey, endKey) > 0) && !endKeyIsEndOfTable) {
713       throw new IllegalArgumentException(
714         "Invalid range: " + Bytes.toStringBinary(startKey) +
715         " > " + Bytes.toStringBinary(endKey));
716     }
717     List<byte[]> keysInRange = new ArrayList<byte[]>();
718     List<HRegionLocation> regionsInRange = new ArrayList<HRegionLocation>();
719     byte[] currentKey = startKey;
720     do {
721       HRegionLocation regionLocation = getRegionLocation(currentKey, reload);
722       keysInRange.add(currentKey);
723       regionsInRange.add(regionLocation);
724       currentKey = regionLocation.getRegionInfo().getEndKey();
725     } while (!Bytes.equals(currentKey, HConstants.EMPTY_END_ROW)
726         && (endKeyIsEndOfTable || Bytes.compareTo(currentKey, endKey) < 0
727             || (includeEndKey && Bytes.compareTo(currentKey, endKey) == 0)));
728     return new Pair<List<byte[]>, List<HRegionLocation>>(keysInRange,
729         regionsInRange);
730   }
731 
732   /**
733    * {@inheritDoc}
734    * @deprecated Use reversed scan instead.
735    */
736    @Override
737    @Deprecated
738    public Result getRowOrBefore(final byte[] row, final byte[] family)
739        throws IOException {
740      RegionServerCallable<Result> callable = new RegionServerCallable<Result>(this.connection,
741          tableName, row) {
742        @Override
743       public Result call(int callTimeout) throws IOException {
744          PayloadCarryingRpcController controller = rpcControllerFactory.newController();
745          controller.setPriority(tableName);
746          controller.setCallTimeout(callTimeout);
747          ClientProtos.GetRequest request = RequestConverter.buildGetRowOrBeforeRequest(
748              getLocation().getRegionInfo().getRegionName(), row, family);
749          try {
750            ClientProtos.GetResponse response = getStub().get(controller, request);
751            if (!response.hasResult()) return null;
752            return ProtobufUtil.toResult(response.getResult(), controller.cellScanner());
753          } catch (ServiceException se) {
754            throw ProtobufUtil.getRemoteException(se);
755          }
756        }
757      };
758      return rpcCallerFactory.<Result>newCaller(rpcTimeout).callWithRetries(callable,
759          this.operationTimeout);
760    }
761 
762   /**
763    * The underlying {@link HTable} must not be closed.
764    * {@link HTableInterface#getScanner(Scan)} has other usage details.
765    */
766   @Override
767   public ResultScanner getScanner(final Scan scan) throws IOException {
768     if (scan.getBatch() > 0 && scan.isSmall()) {
769       throw new IllegalArgumentException("Small scan should not be used with batching");
770     }
771 
772     if (scan.getCaching() <= 0) {
773       scan.setCaching(getScannerCaching());
774     }
775     if (scan.getMaxResultSize() <= 0) {
776       scan.setMaxResultSize(scannerMaxResultSize);
777     }
778 
779     if (scan.isReversed()) {
780       if (scan.isSmall()) {
781         return new ClientSmallReversedScanner(getConfiguration(), scan, getName(),
782             this.connection, this.rpcCallerFactory, this.rpcControllerFactory,
783             pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan());
784       } else {
785         return new ReversedClientScanner(getConfiguration(), scan, getName(),
786             this.connection, this.rpcCallerFactory, this.rpcControllerFactory,
787             pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan());
788       }
789     }
790 
791     if (scan.isSmall()) {
792       return new ClientSmallScanner(getConfiguration(), scan, getName(),
793           this.connection, this.rpcCallerFactory, this.rpcControllerFactory,
794           pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan());
795     } else {
796       return new ClientScanner(getConfiguration(), scan, getName(), this.connection,
797           this.rpcCallerFactory, this.rpcControllerFactory,
798           pool, connConfiguration.getReplicaCallTimeoutMicroSecondScan());
799     }
800   }
801 
802   /**
803    * The underlying {@link HTable} must not be closed.
804    * {@link HTableInterface#getScanner(byte[])} has other usage details.
805    */
806   @Override
807   public ResultScanner getScanner(byte [] family) throws IOException {
808     Scan scan = new Scan();
809     scan.addFamily(family);
810     return getScanner(scan);
811   }
812 
813   /**
814    * The underlying {@link HTable} must not be closed.
815    * {@link HTableInterface#getScanner(byte[], byte[])} has other usage details.
816    */
817   @Override
818   public ResultScanner getScanner(byte [] family, byte [] qualifier)
819   throws IOException {
820     Scan scan = new Scan();
821     scan.addColumn(family, qualifier);
822     return getScanner(scan);
823   }
824 
825   /**
826    * {@inheritDoc}
827    */
828   @Override
829   public Result get(final Get get) throws IOException {
830     return get(get, get.isCheckExistenceOnly());
831   }
832 
833   private Result get(Get get, final boolean checkExistenceOnly) throws IOException {
834     // if we are changing settings to the get, clone it.
835     if (get.isCheckExistenceOnly() != checkExistenceOnly || get.getConsistency() == null) {
836       get = ReflectionUtils.newInstance(get.getClass(), get);
837       get.setCheckExistenceOnly(checkExistenceOnly);
838       if (get.getConsistency() == null){
839         get.setConsistency(defaultConsistency);
840       }
841     }
842 
843     if (get.getConsistency() == Consistency.STRONG) {
844       // Good old call.
845       final Get getReq = get;
846       RegionServerCallable<Result> callable = new RegionServerCallable<Result>(this.connection,
847           getName(), get.getRow()) {
848         @Override
849         public Result call(int callTimeout) throws IOException {
850           ClientProtos.GetRequest request =
851             RequestConverter.buildGetRequest(getLocation().getRegionInfo().getRegionName(), getReq);
852           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
853           controller.setPriority(tableName);
854           controller.setCallTimeout(callTimeout);
855           try {
856             ClientProtos.GetResponse response = getStub().get(controller, request);
857             if (response == null) return null;
858             return ProtobufUtil.toResult(response.getResult(), controller.cellScanner());
859           } catch (ServiceException se) {
860             throw ProtobufUtil.getRemoteException(se);
861           }
862         }
863       };
864       return rpcCallerFactory.<Result>newCaller(rpcTimeout).callWithRetries(callable,
865           this.operationTimeout);
866     }
867 
868     // Call that takes into account the replica
869     RpcRetryingCallerWithReadReplicas callable = new RpcRetryingCallerWithReadReplicas(
870       rpcControllerFactory, tableName, this.connection, get, pool,
871       connConfiguration.getRetriesNumber(),
872       operationTimeout,
873       connConfiguration.getPrimaryCallTimeoutMicroSecond());
874     return callable.call();
875   }
876 
877 
878   /**
879    * {@inheritDoc}
880    */
881   @Override
882   public Result[] get(List<Get> gets) throws IOException {
883     if (gets.size() == 1) {
884       return new Result[]{get(gets.get(0))};
885     }
886     try {
887       Object [] r1 = batch((List)gets);
888 
889       // translate.
890       Result [] results = new Result[r1.length];
891       int i=0;
892       for (Object o : r1) {
893         // batch ensures if there is a failure we get an exception instead
894         results[i++] = (Result) o;
895       }
896 
897       return results;
898     } catch (InterruptedException e) {
899       throw (InterruptedIOException)new InterruptedIOException().initCause(e);
900     }
901   }
902 
903   /**
904    * {@inheritDoc}
905    */
906   @Override
907   public void batch(final List<? extends Row> actions, final Object[] results)
908       throws InterruptedException, IOException {
909     AsyncRequestFuture ars = multiAp.submitAll(pool, tableName, actions, null, results);
910     ars.waitUntilDone();
911     if (ars.hasError()) {
912       throw ars.getErrors();
913     }
914   }
915 
916   /**
917    * {@inheritDoc}
918    * @deprecated If any exception is thrown by one of the actions, there is no way to
919    * retrieve the partially executed results. Use {@link #batch(List, Object[])} instead.
920    */
921   @Deprecated
922   @Override
923   public Object[] batch(final List<? extends Row> actions)
924      throws InterruptedException, IOException {
925     Object[] results = new Object[actions.size()];
926     batch(actions, results);
927     return results;
928   }
929 
930   /**
931    * {@inheritDoc}
932    */
933   @Override
934   public <R> void batchCallback(
935       final List<? extends Row> actions, final Object[] results, final Batch.Callback<R> callback)
936       throws IOException, InterruptedException {
937     connection.processBatchCallback(actions, tableName, pool, results, callback);
938   }
939 
940   /**
941    * {@inheritDoc}
942    * @deprecated If any exception is thrown by one of the actions, there is no way to
943    * retrieve the partially executed results. Use
944    * {@link #batchCallback(List, Object[], org.apache.hadoop.hbase.client.coprocessor.Batch.Callback)}
945    * instead.
946    */
947   @Deprecated
948   @Override
949   public <R> Object[] batchCallback(
950     final List<? extends Row> actions, final Batch.Callback<R> callback) throws IOException,
951       InterruptedException {
952     Object[] results = new Object[actions.size()];
953     batchCallback(actions, results, callback);
954     return results;
955   }
956 
957   /**
958    * {@inheritDoc}
959    */
960   @Override
961   public void delete(final Delete delete)
962   throws IOException {
963     RegionServerCallable<Boolean> callable = new RegionServerCallable<Boolean>(connection,
964         tableName, delete.getRow()) {
965       @Override
966       public Boolean call(int callTimeout) throws IOException {
967         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
968         controller.setPriority(tableName);
969         controller.setCallTimeout(callTimeout);
970 
971         try {
972           MutateRequest request = RequestConverter.buildMutateRequest(
973             getLocation().getRegionInfo().getRegionName(), delete);
974           MutateResponse response = getStub().mutate(controller, request);
975           return Boolean.valueOf(response.getProcessed());
976         } catch (ServiceException se) {
977           throw ProtobufUtil.getRemoteException(se);
978         }
979       }
980     };
981     rpcCallerFactory.<Boolean> newCaller(rpcTimeout).callWithRetries(callable,
982         this.operationTimeout);
983   }
984 
985   /**
986    * {@inheritDoc}
987    */
988   @Override
989   public void delete(final List<Delete> deletes)
990   throws IOException {
991     Object[] results = new Object[deletes.size()];
992     try {
993       batch(deletes, results);
994     } catch (InterruptedException e) {
995       throw (InterruptedIOException)new InterruptedIOException().initCause(e);
996     } finally {
997       // mutate list so that it is empty for complete success, or contains only failed records
998       // results are returned in the same order as the requests in list
999       // walk the list backwards, so we can remove from list without impacting the indexes of earlier members
1000       for (int i = results.length - 1; i>=0; i--) {
1001         // if result is not null, it succeeded
1002         if (results[i] instanceof Result) {
1003           deletes.remove(i);
1004         }
1005       }
1006     }
1007   }
1008 
1009   /**
1010    * {@inheritDoc}
1011    * @throws IOException
1012    */
1013   @Override
1014   public void put(final Put put) throws IOException {
1015     getBufferedMutator().mutate(put);
1016     if (autoFlush) {
1017       flushCommits();
1018     }
1019   }
1020 
1021   /**
1022    * {@inheritDoc}
1023    * @throws IOException
1024    */
1025   @Override
1026   public void put(final List<Put> puts) throws IOException {
1027     getBufferedMutator().mutate(puts);
1028     if (autoFlush) {
1029       flushCommits();
1030     }
1031   }
1032 
1033   /**
1034    * {@inheritDoc}
1035    */
1036   @Override
1037   public void mutateRow(final RowMutations rm) throws IOException {
1038     final RetryingTimeTracker tracker = new RetryingTimeTracker().start();
1039     PayloadCarryingServerCallable<MultiResponse> callable =
1040       new PayloadCarryingServerCallable<MultiResponse>(connection, getName(), rm.getRow(),
1041           rpcControllerFactory) {
1042         @Override
1043         public MultiResponse call(int callTimeout) throws IOException {
1044           controller.setPriority(tableName);
1045           int remainingTime = tracker.getRemainingTime(callTimeout);
1046           if (remainingTime == 0) {
1047             throw new DoNotRetryIOException("Timeout for mutate row");
1048           }
1049           controller.setCallTimeout(remainingTime);
1050           try {
1051             RegionAction.Builder regionMutationBuilder = RequestConverter.buildRegionAction(
1052                 getLocation().getRegionInfo().getRegionName(), rm);
1053             regionMutationBuilder.setAtomic(true);
1054             MultiRequest request =
1055                 MultiRequest.newBuilder().addRegionAction(regionMutationBuilder.build()).build();
1056             ClientProtos.MultiResponse response = getStub().multi(controller, request);
1057             ClientProtos.RegionActionResult res = response.getRegionActionResultList().get(0);
1058             if (res.hasException()) {
1059               Throwable ex = ProtobufUtil.toException(res.getException());
1060               if (ex instanceof IOException) {
1061                 throw (IOException) ex;
1062               }
1063               throw new IOException("Failed to mutate row: " +
1064                   Bytes.toStringBinary(rm.getRow()), ex);
1065             }
1066             return ResponseConverter.getResults(request, response, controller.cellScanner());
1067           } catch (ServiceException se) {
1068             throw ProtobufUtil.getRemoteException(se);
1069           }
1070         }
1071       };
1072     AsyncRequestFuture ars = multiAp.submitAll(pool, tableName, rm.getMutations(),
1073         null, null, callable, operationTimeout, rpcTimeout);
1074     ars.waitUntilDone();
1075     if (ars.hasError()) {
1076       throw ars.getErrors();
1077     }
1078   }
1079 
1080   /**
1081    * {@inheritDoc}
1082    */
1083   @Override
1084   public Result append(final Append append) throws IOException {
1085     if (append.numFamilies() == 0) {
1086       throw new IOException(
1087           "Invalid arguments to append, no columns specified");
1088     }
1089 
1090     NonceGenerator ng = this.connection.getNonceGenerator();
1091     final long nonceGroup = ng.getNonceGroup(), nonce = ng.newNonce();
1092     RegionServerCallable<Result> callable =
1093       new RegionServerCallable<Result>(this.connection, getName(), append.getRow()) {
1094         @Override
1095         public Result call(int callTimeout) throws IOException {
1096           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
1097           controller.setPriority(getTableName());
1098           controller.setCallTimeout(callTimeout);
1099           try {
1100             MutateRequest request = RequestConverter.buildMutateRequest(
1101               getLocation().getRegionInfo().getRegionName(), append, nonceGroup, nonce);
1102             MutateResponse response = getStub().mutate(controller, request);
1103             if (!response.hasResult()) return null;
1104             return ProtobufUtil.toResult(response.getResult(), controller.cellScanner());
1105           } catch (ServiceException se) {
1106             throw ProtobufUtil.getRemoteException(se);
1107           }
1108         }
1109       };
1110     return rpcCallerFactory.<Result> newCaller(rpcTimeout).callWithRetries(callable,
1111         this.operationTimeout);
1112   }
1113 
1114   /**
1115    * {@inheritDoc}
1116    */
1117   @Override
1118   public Result increment(final Increment increment) throws IOException {
1119     if (!increment.hasFamilies()) {
1120       throw new IOException(
1121           "Invalid arguments to increment, no columns specified");
1122     }
1123     NonceGenerator ng = this.connection.getNonceGenerator();
1124     final long nonceGroup = ng.getNonceGroup(), nonce = ng.newNonce();
1125     RegionServerCallable<Result> callable = new RegionServerCallable<Result>(this.connection,
1126         getName(), increment.getRow()) {
1127       @Override
1128       public Result call(int callTimeout) throws IOException {
1129         PayloadCarryingRpcController controller = rpcControllerFactory.newController();
1130         controller.setPriority(getTableName());
1131         controller.setCallTimeout(callTimeout);
1132         try {
1133           MutateRequest request = RequestConverter.buildMutateRequest(
1134             getLocation().getRegionInfo().getRegionName(), increment, nonceGroup, nonce);
1135           MutateResponse response = getStub().mutate(controller, request);
1136           return ProtobufUtil.toResult(response.getResult(), controller.cellScanner());
1137         } catch (ServiceException se) {
1138           throw ProtobufUtil.getRemoteException(se);
1139         }
1140       }
1141     };
1142     return rpcCallerFactory.<Result> newCaller(rpcTimeout).callWithRetries(callable,
1143         this.operationTimeout);
1144   }
1145 
1146   /**
1147    * {@inheritDoc}
1148    */
1149   @Override
1150   public long incrementColumnValue(final byte [] row, final byte [] family,
1151       final byte [] qualifier, final long amount)
1152   throws IOException {
1153     return incrementColumnValue(row, family, qualifier, amount, Durability.SYNC_WAL);
1154   }
1155 
1156   /**
1157    * @deprecated As of release 0.96
1158    *             (<a href="https://issues.apache.org/jira/browse/HBASE-9508">HBASE-9508</a>).
1159    *             This will be removed in HBase 2.0.0.
1160    *             Use {@link #incrementColumnValue(byte[], byte[], byte[], long, Durability)}.
1161    */
1162   @Deprecated
1163   @Override
1164   public long incrementColumnValue(final byte [] row, final byte [] family,
1165       final byte [] qualifier, final long amount, final boolean writeToWAL)
1166   throws IOException {
1167     return incrementColumnValue(row, family, qualifier, amount,
1168       writeToWAL? Durability.SYNC_WAL: Durability.SKIP_WAL);
1169   }
1170 
1171   /**
1172    * {@inheritDoc}
1173    */
1174   @Override
1175   public long incrementColumnValue(final byte [] row, final byte [] family,
1176       final byte [] qualifier, final long amount, final Durability durability)
1177   throws IOException {
1178     NullPointerException npe = null;
1179     if (row == null) {
1180       npe = new NullPointerException("row is null");
1181     } else if (family == null) {
1182       npe = new NullPointerException("family is null");
1183     } else if (qualifier == null) {
1184       npe = new NullPointerException("qualifier is null");
1185     }
1186     if (npe != null) {
1187       throw new IOException(
1188           "Invalid arguments to incrementColumnValue", npe);
1189     }
1190 
1191     NonceGenerator ng = this.connection.getNonceGenerator();
1192     final long nonceGroup = ng.getNonceGroup(), nonce = ng.newNonce();
1193     RegionServerCallable<Long> callable =
1194       new RegionServerCallable<Long>(connection, getName(), row) {
1195         @Override
1196         public Long call(int callTimeout) throws IOException {
1197           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
1198           controller.setPriority(getTableName());
1199           controller.setCallTimeout(callTimeout);
1200           try {
1201             MutateRequest request = RequestConverter.buildIncrementRequest(
1202               getLocation().getRegionInfo().getRegionName(), row, family,
1203               qualifier, amount, durability, nonceGroup, nonce);
1204             MutateResponse response = getStub().mutate(controller, request);
1205             Result result =
1206               ProtobufUtil.toResult(response.getResult(), controller.cellScanner());
1207             return Long.valueOf(Bytes.toLong(result.getValue(family, qualifier)));
1208           } catch (ServiceException se) {
1209             throw ProtobufUtil.getRemoteException(se);
1210           }
1211         }
1212       };
1213     return rpcCallerFactory.<Long> newCaller(rpcTimeout).callWithRetries(callable,
1214         this.operationTimeout);
1215   }
1216 
1217   /**
1218    * {@inheritDoc}
1219    */
1220   @Override
1221   public boolean checkAndPut(final byte [] row,
1222       final byte [] family, final byte [] qualifier, final byte [] value,
1223       final Put put)
1224   throws IOException {
1225     RegionServerCallable<Boolean> callable =
1226       new RegionServerCallable<Boolean>(connection, getName(), row) {
1227         @Override
1228         public Boolean call(int callTimeout) throws IOException {
1229           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
1230           controller.setPriority(tableName);
1231           controller.setCallTimeout(callTimeout);
1232           try {
1233             MutateRequest request = RequestConverter.buildMutateRequest(
1234                 getLocation().getRegionInfo().getRegionName(), row, family, qualifier,
1235                 new BinaryComparator(value), CompareType.EQUAL, put);
1236             MutateResponse response = getStub().mutate(controller, request);
1237             return Boolean.valueOf(response.getProcessed());
1238           } catch (ServiceException se) {
1239             throw ProtobufUtil.getRemoteException(se);
1240           }
1241         }
1242       };
1243     return rpcCallerFactory.<Boolean> newCaller(rpcTimeout).callWithRetries(callable,
1244         this.operationTimeout);
1245   }
1246 
1247   /**
1248    * {@inheritDoc}
1249    */
1250   @Override
1251   public boolean checkAndPut(final byte [] row, final byte [] family,
1252       final byte [] qualifier, final CompareOp compareOp, final byte [] value,
1253       final Put put)
1254   throws IOException {
1255     RegionServerCallable<Boolean> callable =
1256       new RegionServerCallable<Boolean>(connection, getName(), row) {
1257         @Override
1258         public Boolean call(int callTimeout) throws IOException {
1259           PayloadCarryingRpcController controller = new PayloadCarryingRpcController();
1260           controller.setPriority(tableName);
1261           controller.setCallTimeout(callTimeout);
1262           try {
1263             CompareType compareType = CompareType.valueOf(compareOp.name());
1264             MutateRequest request = RequestConverter.buildMutateRequest(
1265               getLocation().getRegionInfo().getRegionName(), row, family, qualifier,
1266                 new BinaryComparator(value), compareType, put);
1267             MutateResponse response = getStub().mutate(controller, request);
1268             return Boolean.valueOf(response.getProcessed());
1269           } catch (ServiceException se) {
1270             throw ProtobufUtil.getRemoteException(se);
1271           }
1272         }
1273       };
1274     return rpcCallerFactory.<Boolean> newCaller(rpcTimeout).callWithRetries(callable,
1275         this.operationTimeout);
1276   }
1277 
1278   /**
1279    * {@inheritDoc}
1280    */
1281   @Override
1282   public boolean checkAndDelete(final byte [] row,
1283       final byte [] family, final byte [] qualifier, final byte [] value,
1284       final Delete delete)
1285   throws IOException {
1286     RegionServerCallable<Boolean> callable =
1287       new RegionServerCallable<Boolean>(connection, getName(), row) {
1288         @Override
1289         public Boolean call(int callTimeout) throws IOException {
1290           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
1291           controller.setPriority(tableName);
1292           controller.setCallTimeout(callTimeout);
1293           try {
1294             MutateRequest request = RequestConverter.buildMutateRequest(
1295               getLocation().getRegionInfo().getRegionName(), row, family, qualifier,
1296                 new BinaryComparator(value), CompareType.EQUAL, delete);
1297             MutateResponse response = getStub().mutate(controller, request);
1298             return Boolean.valueOf(response.getProcessed());
1299           } catch (ServiceException se) {
1300             throw ProtobufUtil.getRemoteException(se);
1301           }
1302         }
1303       };
1304     return rpcCallerFactory.<Boolean> newCaller(rpcTimeout).callWithRetries(callable,
1305         this.operationTimeout);
1306   }
1307 
1308   /**
1309    * {@inheritDoc}
1310    */
1311   @Override
1312   public boolean checkAndDelete(final byte [] row, final byte [] family,
1313       final byte [] qualifier, final CompareOp compareOp, final byte [] value,
1314       final Delete delete)
1315   throws IOException {
1316     RegionServerCallable<Boolean> callable =
1317       new RegionServerCallable<Boolean>(connection, getName(), row) {
1318         @Override
1319         public Boolean call(int callTimeout) throws IOException {
1320           PayloadCarryingRpcController controller = rpcControllerFactory.newController();
1321           controller.setPriority(tableName);
1322           controller.setCallTimeout(callTimeout);
1323           try {
1324             CompareType compareType = CompareType.valueOf(compareOp.name());
1325             MutateRequest request = RequestConverter.buildMutateRequest(
1326               getLocation().getRegionInfo().getRegionName(), row, family, qualifier,
1327                 new BinaryComparator(value), compareType, delete);
1328             MutateResponse response = getStub().mutate(controller, request);
1329             return Boolean.valueOf(response.getProcessed());
1330           } catch (ServiceException se) {
1331             throw ProtobufUtil.getRemoteException(se);
1332           }
1333         }
1334       };
1335     return rpcCallerFactory.<Boolean> newCaller(rpcTimeout).callWithRetries(callable,
1336         this.operationTimeout);
1337   }
1338 
1339   /**
1340    * {@inheritDoc}
1341    */
1342   @Override
1343   public boolean checkAndMutate(final byte [] row, final byte [] family, final byte [] qualifier,
1344     final CompareOp compareOp, final byte [] value, final RowMutations rm)
1345     throws IOException {
1346     final RetryingTimeTracker tracker = new RetryingTimeTracker().start();
1347     PayloadCarryingServerCallable<MultiResponse> callable =
1348       new PayloadCarryingServerCallable<MultiResponse>(connection, getName(), rm.getRow(),
1349         rpcControllerFactory) {
1350         @Override
1351         public MultiResponse call(int callTimeout) throws IOException {
1352           controller.setPriority(tableName);
1353           int remainingTime = tracker.getRemainingTime(callTimeout);
1354           if (remainingTime == 0) {
1355             throw new DoNotRetryIOException("Timeout for mutate row");
1356           }
1357           controller.setCallTimeout(remainingTime);
1358           try {
1359             CompareType compareType = CompareType.valueOf(compareOp.name());
1360             MultiRequest request = RequestConverter.buildMutateRequest(
1361               getLocation().getRegionInfo().getRegionName(), row, family, qualifier,
1362               new BinaryComparator(value), compareType, rm);
1363             ClientProtos.MultiResponse response = getStub().multi(controller, request);
1364             ClientProtos.RegionActionResult res = response.getRegionActionResultList().get(0);
1365             if (res.hasException()) {
1366               Throwable ex = ProtobufUtil.toException(res.getException());
1367               if(ex instanceof IOException) {
1368                 throw (IOException)ex;
1369               }
1370               throw new IOException("Failed to checkAndMutate row: "+
1371                                     Bytes.toStringBinary(rm.getRow()), ex);
1372             }
1373             return ResponseConverter.getResults(request, response, controller.cellScanner());
1374           } catch (ServiceException se) {
1375             throw ProtobufUtil.getRemoteException(se);
1376           }
1377         }
1378       };
1379     /**
1380      *  Currently, we use one array to store 'processed' flag which is returned by server.
1381      *  It is excessive to send such a large array, but that is required by the framework right now
1382      * */
1383     Object[] results = new Object[rm.getMutations().size()];
1384     AsyncRequestFuture ars = multiAp.submitAll(pool, tableName, rm.getMutations(),
1385       null, results, callable, operationTimeout, rpcTimeout);
1386     ars.waitUntilDone();
1387     if (ars.hasError()) {
1388       throw ars.getErrors();
1389     }
1390 
1391     return ((Result)results[0]).getExists();
1392   }
1393 
1394   /**
1395    * {@inheritDoc}
1396    */
1397   @Override
1398   public boolean exists(final Get get) throws IOException {
1399     Result r = get(get, true);
1400     assert r.getExists() != null;
1401     return r.getExists();
1402   }
1403 
1404   /**
1405    * {@inheritDoc}
1406    */
1407   @Override
1408   public boolean[] existsAll(final List<Get> gets) throws IOException {
1409     if (gets.isEmpty()) return new boolean[]{};
1410     if (gets.size() == 1) return new boolean[]{exists(gets.get(0))};
1411 
1412     ArrayList<Get> exists = new ArrayList<Get>(gets.size());
1413     for (Get g: gets){
1414       Get ge = new Get(g);
1415       ge.setCheckExistenceOnly(true);
1416       exists.add(ge);
1417     }
1418 
1419     Object[] r1;
1420     try {
1421       r1 = batch(exists);
1422     } catch (InterruptedException e) {
1423       throw (InterruptedIOException)new InterruptedIOException().initCause(e);
1424     }
1425 
1426     // translate.
1427     boolean[] results = new boolean[r1.length];
1428     int i = 0;
1429     for (Object o : r1) {
1430       // batch ensures if there is a failure we get an exception instead
1431       results[i++] = ((Result)o).getExists();
1432     }
1433 
1434     return results;
1435   }
1436 
1437   /**
1438    * {@inheritDoc}
1439    */
1440   @Override
1441   @Deprecated
1442   public Boolean[] exists(final List<Get> gets) throws IOException {
1443     boolean[] results = existsAll(gets);
1444     Boolean[] objectResults = new Boolean[results.length];
1445     for (int i = 0; i < results.length; ++i) {
1446       objectResults[i] = results[i];
1447     }
1448     return objectResults;
1449   }
1450 
1451   /**
1452    * {@inheritDoc}
1453    * @throws IOException
1454    */
1455   @Override
1456   public void flushCommits() throws IOException {
1457     if (mutator == null) {
1458       // nothing to flush if there's no mutator; don't bother creating one.
1459       return;
1460     }
1461     getBufferedMutator().flush();
1462   }
1463 
1464   /**
1465    * Process a mixed batch of Get, Put and Delete actions. All actions for a
1466    * RegionServer are forwarded in one RPC call. Queries are executed in parallel.
1467    *
1468    * @param list The collection of actions.
1469    * @param results An empty array, same size as list. If an exception is thrown,
1470    * you can test here for partial results, and to determine which actions
1471    * processed successfully.
1472    * @throws IOException if there are problems talking to META. Per-item
1473    * exceptions are stored in the results array.
1474    */
1475   public <R> void processBatchCallback(
1476     final List<? extends Row> list, final Object[] results, final Batch.Callback<R> callback)
1477     throws IOException, InterruptedException {
1478     this.batchCallback(list, results, callback);
1479   }
1480 
1481 
1482   /**
1483    * Parameterized batch processing, allowing varying return types for different
1484    * {@link Row} implementations.
1485    */
1486   public void processBatch(final List<? extends Row> list, final Object[] results)
1487     throws IOException, InterruptedException {
1488     this.batch(list, results);
1489   }
1490 
1491 
1492   @Override
1493   public void close() throws IOException {
1494     if (this.closed) {
1495       return;
1496     }
1497     flushCommits();
1498     if (cleanupPoolOnClose) {
1499       this.pool.shutdown();
1500       try {
1501         boolean terminated = false;
1502         do {
1503           // wait until the pool has terminated
1504           terminated = this.pool.awaitTermination(60, TimeUnit.SECONDS);
1505         } while (!terminated);
1506       } catch (InterruptedException e) {
1507         this.pool.shutdownNow();
1508         LOG.warn("waitForTermination interrupted");
1509       }
1510     }
1511     if (cleanupConnectionOnClose) {
1512       if (this.connection != null) {
1513         this.connection.close();
1514       }
1515     }
1516     this.closed = true;
1517   }
1518 
1519   // validate for well-formedness
1520   public void validatePut(final Put put) throws IllegalArgumentException {
1521     validatePut(put, connConfiguration.getMaxKeyValueSize());
1522   }
1523 
1524   // validate for well-formedness
1525   public static void validatePut(Put put, int maxKeyValueSize) throws IllegalArgumentException {
1526     if (put.isEmpty()) {
1527       throw new IllegalArgumentException("No columns to insert");
1528     }
1529     if (maxKeyValueSize > 0) {
1530       for (List<Cell> list : put.getFamilyCellMap().values()) {
1531         for (Cell cell : list) {
1532           if (KeyValueUtil.length(cell) > maxKeyValueSize) {
1533             throw new IllegalArgumentException("KeyValue size too large");
1534           }
1535         }
1536       }
1537     }
1538   }
1539 
1540   /**
1541    * {@inheritDoc}
1542    */
1543   @Override
1544   public boolean isAutoFlush() {
1545     return autoFlush;
1546   }
1547 
1548   /**
1549    * {@inheritDoc}
1550    */
1551   @Deprecated
1552   @Override
1553   public void setAutoFlush(boolean autoFlush) {
1554     this.autoFlush = autoFlush;
1555   }
1556 
1557   /**
1558    * {@inheritDoc}
1559    */
1560   @Override
1561   public void setAutoFlushTo(boolean autoFlush) {
1562     this.autoFlush = autoFlush;
1563   }
1564 
1565   /**
1566    * {@inheritDoc}
1567    */
1568   @Override
1569   public void setAutoFlush(boolean autoFlush, boolean clearBufferOnFail) {
1570     this.autoFlush = autoFlush;
1571   }
1572 
1573   /**
1574    * Returns the maximum size in bytes of the write buffer for this HTable.
1575    * <p>
1576    * The default value comes from the configuration parameter
1577    * {@code hbase.client.write.buffer}.
1578    * @return The size of the write buffer in bytes.
1579    */
1580   @Override
1581   public long getWriteBufferSize() {
1582     if (mutator == null) {
1583       return connConfiguration.getWriteBufferSize();
1584     } else {
1585       return mutator.getWriteBufferSize();
1586     }
1587   }
1588 
1589   /**
1590    * Sets the size of the buffer in bytes.
1591    * <p>
1592    * If the new size is less than the current amount of data in the
1593    * write buffer, the buffer gets flushed.
1594    * @param writeBufferSize The new write buffer size, in bytes.
1595    * @throws IOException if a remote or network exception occurs.
1596    */
1597   @Override
1598   public void setWriteBufferSize(long writeBufferSize) throws IOException {
1599     getBufferedMutator();
1600     mutator.setWriteBufferSize(writeBufferSize);
1601   }
1602 
1603   /**
1604    * The pool is used for mutli requests for this HTable
1605    * @return the pool used for mutli
1606    */
1607   ExecutorService getPool() {
1608     return this.pool;
1609   }
1610 
1611   /**
1612    * Enable or disable region cache prefetch for the table. It will be
1613    * applied for the given table's all HTable instances who share the same
1614    * connection. By default, the cache prefetch is enabled.
1615    * @param tableName name of table to configure.
1616    * @param enable Set to true to enable region cache prefetch. Or set to
1617    * false to disable it.
1618    * @throws IOException
1619    * @deprecated does nothing since 0.99
1620    */
1621   @Deprecated
1622   public static void setRegionCachePrefetch(final byte[] tableName,
1623       final boolean enable)  throws IOException {
1624   }
1625 
1626   /**
1627    * @deprecated does nothing since 0.99
1628    */
1629   @Deprecated
1630   public static void setRegionCachePrefetch(
1631       final TableName tableName,
1632       final boolean enable) throws IOException {
1633   }
1634 
1635   /**
1636    * Enable or disable region cache prefetch for the table. It will be
1637    * applied for the given table's all HTable instances who share the same
1638    * connection. By default, the cache prefetch is enabled.
1639    * @param conf The Configuration object to use.
1640    * @param tableName name of table to configure.
1641    * @param enable Set to true to enable region cache prefetch. Or set to
1642    * false to disable it.
1643    * @throws IOException
1644    * @deprecated does nothing since 0.99
1645    */
1646   @Deprecated
1647   public static void setRegionCachePrefetch(final Configuration conf,
1648       final byte[] tableName, final boolean enable) throws IOException {
1649   }
1650 
1651   /**
1652    * @deprecated does nothing since 0.99
1653    */
1654   @Deprecated
1655   public static void setRegionCachePrefetch(final Configuration conf,
1656       final TableName tableName,
1657       final boolean enable) throws IOException {
1658   }
1659 
1660   /**
1661    * Check whether region cache prefetch is enabled or not for the table.
1662    * @param conf The Configuration object to use.
1663    * @param tableName name of table to check
1664    * @return true if table's region cache prefecth is enabled. Otherwise
1665    * it is disabled.
1666    * @throws IOException
1667    * @deprecated always return false since 0.99
1668    */
1669   @Deprecated
1670   public static boolean getRegionCachePrefetch(final Configuration conf,
1671       final byte[] tableName) throws IOException {
1672     return false;
1673   }
1674 
1675   /**
1676    * @deprecated always return false since 0.99
1677    */
1678   @Deprecated
1679   public static boolean getRegionCachePrefetch(final Configuration conf,
1680       final TableName tableName) throws IOException {
1681     return false;
1682   }
1683 
1684   /**
1685    * Check whether region cache prefetch is enabled or not for the table.
1686    * @param tableName name of table to check
1687    * @return true if table's region cache prefecth is enabled. Otherwise
1688    * it is disabled.
1689    * @throws IOException
1690    * @deprecated always return false since 0.99
1691    */
1692   @Deprecated
1693   public static boolean getRegionCachePrefetch(final byte[] tableName) throws IOException {
1694     return false;
1695   }
1696 
1697   /**
1698    * @deprecated always return false since 0.99
1699    */
1700   @Deprecated
1701   public static boolean getRegionCachePrefetch(
1702       final TableName tableName) throws IOException {
1703     return false;
1704   }
1705 
1706   /**
1707    * Explicitly clears the region cache to fetch the latest value from META.
1708    * This is a power user function: avoid unless you know the ramifications.
1709    */
1710   public void clearRegionCache() {
1711     this.connection.clearRegionCache();
1712   }
1713 
1714   /**
1715    * {@inheritDoc}
1716    */
1717   @Override
1718   public CoprocessorRpcChannel coprocessorService(byte[] row) {
1719     return new RegionCoprocessorRpcChannel(connection, tableName, row);
1720   }
1721 
1722   /**
1723    * {@inheritDoc}
1724    */
1725   @Override
1726   public <T extends Service, R> Map<byte[],R> coprocessorService(final Class<T> service,
1727       byte[] startKey, byte[] endKey, final Batch.Call<T,R> callable)
1728       throws ServiceException, Throwable {
1729     final Map<byte[],R> results =  Collections.synchronizedMap(
1730         new TreeMap<byte[], R>(Bytes.BYTES_COMPARATOR));
1731     coprocessorService(service, startKey, endKey, callable, new Batch.Callback<R>() {
1732       @Override
1733       public void update(byte[] region, byte[] row, R value) {
1734         if (region != null) {
1735           results.put(region, value);
1736         }
1737       }
1738     });
1739     return results;
1740   }
1741 
1742   /**
1743    * {@inheritDoc}
1744    */
1745   @Override
1746   public <T extends Service, R> void coprocessorService(final Class<T> service,
1747       byte[] startKey, byte[] endKey, final Batch.Call<T,R> callable,
1748       final Batch.Callback<R> callback) throws ServiceException, Throwable {
1749 
1750     // get regions covered by the row range
1751     List<byte[]> keys = getStartKeysInRange(startKey, endKey);
1752 
1753     Map<byte[],Future<R>> futures =
1754         new TreeMap<byte[],Future<R>>(Bytes.BYTES_COMPARATOR);
1755     for (final byte[] r : keys) {
1756       final RegionCoprocessorRpcChannel channel =
1757           new RegionCoprocessorRpcChannel(connection, tableName, r);
1758       Future<R> future = pool.submit(
1759           new Callable<R>() {
1760             @Override
1761             public R call() throws Exception {
1762               T instance = ProtobufUtil.newServiceStub(service, channel);
1763               R result = callable.call(instance);
1764               byte[] region = channel.getLastRegion();
1765               if (callback != null) {
1766                 callback.update(region, r, result);
1767               }
1768               return result;
1769             }
1770           });
1771       futures.put(r, future);
1772     }
1773     for (Map.Entry<byte[],Future<R>> e : futures.entrySet()) {
1774       try {
1775         e.getValue().get();
1776       } catch (ExecutionException ee) {
1777         LOG.warn("Error calling coprocessor service " + service.getName() + " for row "
1778             + Bytes.toStringBinary(e.getKey()), ee);
1779         throw ee.getCause();
1780       } catch (InterruptedException ie) {
1781         throw new InterruptedIOException("Interrupted calling coprocessor service " + service.getName()
1782             + " for row " + Bytes.toStringBinary(e.getKey()))
1783             .initCause(ie);
1784       }
1785     }
1786   }
1787 
1788   private List<byte[]> getStartKeysInRange(byte[] start, byte[] end)
1789   throws IOException {
1790     if (start == null) {
1791       start = HConstants.EMPTY_START_ROW;
1792     }
1793     if (end == null) {
1794       end = HConstants.EMPTY_END_ROW;
1795     }
1796     return getKeysAndRegionsInRange(start, end, true).getFirst();
1797   }
1798 
1799   public void setOperationTimeout(int operationTimeout) {
1800     this.operationTimeout = operationTimeout;
1801     if (mutator != null) {
1802       mutator.setOperationTimeout(operationTimeout);
1803     }
1804     multiAp.setOperationTimeout(operationTimeout);
1805   }
1806 
1807   public int getOperationTimeout() {
1808     return operationTimeout;
1809   }
1810 
1811   @Override public void setRpcTimeout(int rpcTimeout) {
1812     this.rpcTimeout = rpcTimeout;
1813     if (mutator != null) {
1814       mutator.setRpcTimeout(rpcTimeout);
1815     }
1816     multiAp.setRpcTimeout(rpcTimeout);
1817   }
1818 
1819   @Override public int getRpcTimeout() {
1820     return rpcTimeout;
1821   }
1822 
1823   @Override
1824   public String toString() {
1825     return tableName + ";" + connection;
1826   }
1827 
1828   /**
1829    * {@inheritDoc}
1830    */
1831   @Override
1832   public <R extends Message> Map<byte[], R> batchCoprocessorService(
1833       Descriptors.MethodDescriptor methodDescriptor, Message request,
1834       byte[] startKey, byte[] endKey, R responsePrototype) throws ServiceException, Throwable {
1835     final Map<byte[], R> results = Collections.synchronizedMap(new TreeMap<byte[], R>(
1836         Bytes.BYTES_COMPARATOR));
1837     batchCoprocessorService(methodDescriptor, request, startKey, endKey, responsePrototype,
1838         new Callback<R>() {
1839 
1840           @Override
1841           public void update(byte[] region, byte[] row, R result) {
1842             if (region != null) {
1843               results.put(region, result);
1844             }
1845           }
1846         });
1847     return results;
1848   }
1849 
1850   /**
1851    * {@inheritDoc}
1852    */
1853   @Override
1854   public <R extends Message> void batchCoprocessorService(
1855       final Descriptors.MethodDescriptor methodDescriptor, final Message request,
1856       byte[] startKey, byte[] endKey, final R responsePrototype, final Callback<R> callback)
1857       throws ServiceException, Throwable {
1858 
1859     if (startKey == null) {
1860       startKey = HConstants.EMPTY_START_ROW;
1861     }
1862     if (endKey == null) {
1863       endKey = HConstants.EMPTY_END_ROW;
1864     }
1865     // get regions covered by the row range
1866     Pair<List<byte[]>, List<HRegionLocation>> keysAndRegions =
1867         getKeysAndRegionsInRange(startKey, endKey, true);
1868     List<byte[]> keys = keysAndRegions.getFirst();
1869     List<HRegionLocation> regions = keysAndRegions.getSecond();
1870 
1871     // check if we have any calls to make
1872     if (keys.isEmpty()) {
1873       LOG.info("No regions were selected by key range start=" + Bytes.toStringBinary(startKey) +
1874           ", end=" + Bytes.toStringBinary(endKey));
1875       return;
1876     }
1877 
1878     List<RegionCoprocessorServiceExec> execs = new ArrayList<RegionCoprocessorServiceExec>();
1879     final Map<byte[], RegionCoprocessorServiceExec> execsByRow =
1880         new TreeMap<byte[], RegionCoprocessorServiceExec>(Bytes.BYTES_COMPARATOR);
1881     for (int i = 0; i < keys.size(); i++) {
1882       final byte[] rowKey = keys.get(i);
1883       final byte[] region = regions.get(i).getRegionInfo().getRegionName();
1884       RegionCoprocessorServiceExec exec =
1885           new RegionCoprocessorServiceExec(region, rowKey, methodDescriptor, request);
1886       execs.add(exec);
1887       execsByRow.put(rowKey, exec);
1888     }
1889 
1890     // tracking for any possible deserialization errors on success callback
1891     // TODO: it would be better to be able to reuse AsyncProcess.BatchErrors here
1892     final List<Throwable> callbackErrorExceptions = new ArrayList<Throwable>();
1893     final List<Row> callbackErrorActions = new ArrayList<Row>();
1894     final List<String> callbackErrorServers = new ArrayList<String>();
1895     Object[] results = new Object[execs.size()];
1896 
1897     AsyncProcess asyncProcess =
1898         new AsyncProcess(connection, configuration, pool,
1899             RpcRetryingCallerFactory.instantiate(configuration, connection.getStatisticsTracker()),
1900             true, RpcControllerFactory.instantiate(configuration), rpcTimeout);
1901 
1902     AsyncRequestFuture future = asyncProcess.submitAll(tableName, execs,
1903         new Callback<ClientProtos.CoprocessorServiceResult>() {
1904           @Override
1905           public void update(byte[] region, byte[] row,
1906                               ClientProtos.CoprocessorServiceResult serviceResult) {
1907             if (LOG.isTraceEnabled()) {
1908               LOG.trace("Received result for endpoint " + methodDescriptor.getFullName() +
1909                   ": region=" + Bytes.toStringBinary(region) +
1910                   ", row=" + Bytes.toStringBinary(row) +
1911                   ", value=" + serviceResult.getValue().getValue());
1912             }
1913             try {
1914               Message.Builder builder = responsePrototype.newBuilderForType();
1915               ProtobufUtil.mergeFrom(builder, serviceResult.getValue().getValue());
1916               callback.update(region, row, (R) builder.build());
1917             } catch (IOException e) {
1918               LOG.error("Unexpected response type from endpoint " + methodDescriptor.getFullName(),
1919                   e);
1920               callbackErrorExceptions.add(e);
1921               callbackErrorActions.add(execsByRow.get(row));
1922               callbackErrorServers.add("null");
1923             }
1924           }
1925         }, results);
1926 
1927     future.waitUntilDone();
1928 
1929     if (future.hasError()) {
1930       throw future.getErrors();
1931     } else if (!callbackErrorExceptions.isEmpty()) {
1932       throw new RetriesExhaustedWithDetailsException(callbackErrorExceptions, callbackErrorActions,
1933           callbackErrorServers);
1934     }
1935   }
1936 
1937   public RegionLocator getRegionLocator() {
1938     return this.locator;
1939   }
1940 
1941   @VisibleForTesting
1942   BufferedMutator getBufferedMutator() throws IOException {
1943     if (mutator == null) {
1944       this.mutator = (BufferedMutatorImpl) connection.getBufferedMutator(
1945           new BufferedMutatorParams(tableName)
1946               .pool(pool)
1947               .writeBufferSize(connConfiguration.getWriteBufferSize())
1948               .maxKeyValueSize(connConfiguration.getMaxKeyValueSize())
1949       );
1950       mutator.setRpcTimeout(rpcTimeout);
1951       mutator.setOperationTimeout(operationTimeout);
1952     }
1953     return mutator;
1954   }
1955 }