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  
20  package org.apache.hadoop.hbase.rest.client;
21  
22  import java.io.IOException;
23  import java.io.InterruptedIOException;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.TreeMap;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.hbase.Cell;
36  import org.apache.hadoop.hbase.CellUtil;
37  import org.apache.hadoop.hbase.HBaseConfiguration;
38  import org.apache.hadoop.hbase.HConstants;
39  import org.apache.hadoop.hbase.HTableDescriptor;
40  import org.apache.hadoop.hbase.KeyValue;
41  import org.apache.hadoop.hbase.TableName;
42  import org.apache.hadoop.hbase.classification.InterfaceAudience;
43  import org.apache.hadoop.hbase.classification.InterfaceStability;
44  import org.apache.hadoop.hbase.client.Append;
45  import org.apache.hadoop.hbase.client.Delete;
46  import org.apache.hadoop.hbase.client.Durability;
47  import org.apache.hadoop.hbase.client.Get;
48  import org.apache.hadoop.hbase.client.Increment;
49  import org.apache.hadoop.hbase.client.Put;
50  import org.apache.hadoop.hbase.client.Result;
51  import org.apache.hadoop.hbase.client.ResultScanner;
52  import org.apache.hadoop.hbase.client.Row;
53  import org.apache.hadoop.hbase.client.RowMutations;
54  import org.apache.hadoop.hbase.client.Scan;
55  import org.apache.hadoop.hbase.client.Table;
56  import org.apache.hadoop.hbase.client.coprocessor.Batch;
57  import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
58  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
59  import org.apache.hadoop.hbase.io.TimeRange;
60  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
61  import org.apache.hadoop.hbase.rest.Constants;
62  import org.apache.hadoop.hbase.rest.model.CellModel;
63  import org.apache.hadoop.hbase.rest.model.CellSetModel;
64  import org.apache.hadoop.hbase.rest.model.RowModel;
65  import org.apache.hadoop.hbase.rest.model.ScannerModel;
66  import org.apache.hadoop.hbase.rest.model.TableSchemaModel;
67  import org.apache.hadoop.hbase.util.Bytes;
68  import org.apache.hadoop.util.StringUtils;
69  
70  import com.google.protobuf.Descriptors;
71  import com.google.protobuf.Message;
72  import com.google.protobuf.Service;
73  import com.google.protobuf.ServiceException;
74  
75  /**
76   * HTable interface to remote tables accessed via REST gateway
77   */
78  @InterfaceAudience.Public
79  @InterfaceStability.Stable
80  public class RemoteHTable implements Table {
81  
82    private static final Log LOG = LogFactory.getLog(RemoteHTable.class);
83  
84    final Client client;
85    final Configuration conf;
86    final byte[] name;
87    final int maxRetries;
88    final long sleepTime;
89  
90    @SuppressWarnings("rawtypes")
91    protected String buildRowSpec(final byte[] row, final Map familyMap,
92        final long startTime, final long endTime, final int maxVersions) {
93      StringBuffer sb = new StringBuffer();
94      sb.append('/');
95      sb.append(Bytes.toStringBinary(name));
96      sb.append('/');
97      sb.append(Bytes.toStringBinary(row));
98      Set families = familyMap.entrySet();
99      if (families != null) {
100       Iterator i = familyMap.entrySet().iterator();
101       sb.append('/');
102       while (i.hasNext()) {
103         Map.Entry e = (Map.Entry)i.next();
104         Collection quals = (Collection)e.getValue();
105         if (quals == null || quals.isEmpty()) {
106           // this is an unqualified family. append the family name and NO ':'
107           sb.append(Bytes.toStringBinary((byte[])e.getKey()));
108         } else {
109           Iterator ii = quals.iterator();
110           while (ii.hasNext()) {
111             sb.append(Bytes.toStringBinary((byte[])e.getKey()));
112             sb.append(':');
113             Object o = ii.next();
114             // Puts use byte[] but Deletes use KeyValue
115             if (o instanceof byte[]) {
116               sb.append(Bytes.toStringBinary((byte[])o));
117             } else if (o instanceof KeyValue) {
118               sb.append(Bytes.toStringBinary(((KeyValue) o).getQualifierArray(),
119                 ((KeyValue) o).getQualifierOffset(), ((KeyValue) o).getQualifierLength()));
120             } else {
121               throw new RuntimeException("object type not handled");
122             }
123             if (ii.hasNext()) {
124               sb.append(',');
125             }
126           }
127         }
128         if (i.hasNext()) {
129           sb.append(',');
130         }
131       }
132     }
133     if (startTime >= 0 && endTime != Long.MAX_VALUE) {
134       sb.append('/');
135       sb.append(startTime);
136       if (startTime != endTime) {
137         sb.append(',');
138         sb.append(endTime);
139       }
140     } else if (endTime != Long.MAX_VALUE) {
141       sb.append('/');
142       sb.append(endTime);
143     }
144     if (maxVersions > 1) {
145       sb.append("?v=");
146       sb.append(maxVersions);
147     }
148     return sb.toString();
149   }
150 
151   protected String buildMultiRowSpec(final byte[][] rows, int maxVersions) {
152     StringBuilder sb = new StringBuilder();
153     sb.append('/');
154     sb.append(Bytes.toStringBinary(name));
155     sb.append("/multiget/");
156     if (rows == null || rows.length == 0) {
157       return sb.toString();
158     }
159     sb.append("?");
160     for(int i=0; i<rows.length; i++) {
161       byte[] rk = rows[i];
162       if (i != 0) {
163         sb.append('&');
164       }
165       sb.append("row=");
166       sb.append(Bytes.toStringBinary(rk));
167     }
168     sb.append("&v=");
169     sb.append(maxVersions);
170 
171     return sb.toString();
172   }
173 
174   protected Result[] buildResultFromModel(final CellSetModel model) {
175     List<Result> results = new ArrayList<Result>();
176     for (RowModel row: model.getRows()) {
177       List<Cell> kvs = new ArrayList<Cell>();
178       for (CellModel cell: row.getCells()) {
179         byte[][] split = KeyValue.parseColumn(cell.getColumn());
180         byte[] column = split[0];
181         byte[] qualifier = null;
182         if (split.length == 1) {
183           qualifier = HConstants.EMPTY_BYTE_ARRAY;
184         } else if (split.length == 2) {
185           qualifier = split[1];
186         } else {
187           throw new IllegalArgumentException("Invalid familyAndQualifier provided.");
188         }
189         kvs.add(new KeyValue(row.getKey(), column, qualifier,
190           cell.getTimestamp(), cell.getValue()));
191       }
192       results.add(Result.create(kvs));
193     }
194     return results.toArray(new Result[results.size()]);
195   }
196 
197   protected CellSetModel buildModelFromPut(Put put) {
198     RowModel row = new RowModel(put.getRow());
199     long ts = put.getTimeStamp();
200     for (List<Cell> cells: put.getFamilyCellMap().values()) {
201       for (Cell cell: cells) {
202         row.addCell(new CellModel(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell),
203           ts != HConstants.LATEST_TIMESTAMP ? ts : cell.getTimestamp(),
204           CellUtil.cloneValue(cell)));
205       }
206     }
207     CellSetModel model = new CellSetModel();
208     model.addRow(row);
209     return model;
210   }
211 
212   /**
213    * Constructor
214    * @param client
215    * @param name
216    */
217   public RemoteHTable(Client client, String name) {
218     this(client, HBaseConfiguration.create(), Bytes.toBytes(name));
219   }
220 
221   /**
222    * Constructor
223    * @param client
224    * @param conf
225    * @param name
226    */
227   public RemoteHTable(Client client, Configuration conf, String name) {
228     this(client, conf, Bytes.toBytes(name));
229   }
230 
231   /**
232    * Constructor
233    * @param client
234    * @param conf
235    * @param name
236    */
237   public RemoteHTable(Client client, Configuration conf, byte[] name) {
238     this.client = client;
239     this.conf = conf;
240     this.name = name;
241     this.maxRetries = conf.getInt("hbase.rest.client.max.retries", 10);
242     this.sleepTime = conf.getLong("hbase.rest.client.sleep", 1000);
243   }
244 
245   public byte[] getTableName() {
246     return name.clone();
247   }
248 
249   @Override
250   public TableName getName() {
251     return TableName.valueOf(name);
252   }
253 
254   @Override
255   public Configuration getConfiguration() {
256     return conf;
257   }
258 
259   @Override
260   public HTableDescriptor getTableDescriptor() throws IOException {
261     StringBuilder sb = new StringBuilder();
262     sb.append('/');
263     sb.append(Bytes.toStringBinary(name));
264     sb.append('/');
265     sb.append("schema");
266     for (int i = 0; i < maxRetries; i++) {
267       Response response = client.get(sb.toString(), Constants.MIMETYPE_PROTOBUF);
268       int code = response.getCode();
269       switch (code) {
270       case 200:
271         TableSchemaModel schema = new TableSchemaModel();
272         schema.getObjectFromMessage(response.getBody());
273         return schema.getTableDescriptor();
274       case 509:
275         try {
276           Thread.sleep(sleepTime);
277         } catch (InterruptedException e) {
278           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
279         }
280         break;
281       default:
282         throw new IOException("schema request returned " + code);
283       }
284     }
285     throw new IOException("schema request timed out");
286   }
287 
288   @Override
289   public void close() throws IOException {
290     client.shutdown();
291   }
292 
293   @Override
294   public Result get(Get get) throws IOException {
295     TimeRange range = get.getTimeRange();
296     String spec = buildRowSpec(get.getRow(), get.getFamilyMap(),
297       range.getMin(), range.getMax(), get.getMaxVersions());
298     if (get.getFilter() != null) {
299       LOG.warn("filters not supported on gets");
300     }
301     Result[] results = getResults(spec);
302     if (results.length > 0) {
303       if (results.length > 1) {
304         LOG.warn("too many results for get (" + results.length + ")");
305       }
306       return results[0];
307     } else {
308       return new Result();
309     }
310   }
311 
312   @Override
313   public Result[] get(List<Get> gets) throws IOException {
314     byte[][] rows = new byte[gets.size()][];
315     int maxVersions = 1;
316     int count = 0;
317 
318     for(Get g:gets) {
319 
320       if ( count == 0 ) {
321         maxVersions = g.getMaxVersions();
322       } else if (g.getMaxVersions() != maxVersions) {
323         LOG.warn("MaxVersions on Gets do not match, using the first in the list ("+maxVersions+")");
324       }
325 
326       if (g.getFilter() != null) {
327         LOG.warn("filters not supported on gets");
328       }
329 
330       rows[count] = g.getRow();
331       count ++;
332     }
333 
334     String spec = buildMultiRowSpec(rows, maxVersions);
335 
336     return getResults(spec);
337   }
338 
339   private Result[] getResults(String spec) throws IOException {
340     for (int i = 0; i < maxRetries; i++) {
341       Response response = client.get(spec, Constants.MIMETYPE_PROTOBUF);
342       int code = response.getCode();
343       switch (code) {
344         case 200:
345           CellSetModel model = new CellSetModel();
346           model.getObjectFromMessage(response.getBody());
347           Result[] results = buildResultFromModel(model);
348           if ( results.length > 0) {
349             return results;
350           }
351           // fall through
352         case 404:
353           return new Result[0];
354 
355         case 509:
356           try {
357             Thread.sleep(sleepTime);
358           } catch (InterruptedException e) {
359             throw (InterruptedIOException)new InterruptedIOException().initCause(e);
360           }
361           break;
362         default:
363           throw new IOException("get request returned " + code);
364       }
365     }
366     throw new IOException("get request timed out");
367   }
368 
369   @Override
370   public boolean exists(Get get) throws IOException {
371     LOG.warn("exists() is really get(), just use get()");
372     Result result = get(get);
373     return (result != null && !(result.isEmpty()));
374   }
375 
376   /**
377    * exists(List) is really a list of get() calls. Just use get().
378    * @param gets list of Get to test for the existence
379    */
380   @Override
381   public boolean[] existsAll(List<Get> gets) throws IOException {
382     LOG.warn("exists(List<Get>) is really list of get() calls, just use get()");
383     boolean[] results = new boolean[gets.size()];
384     for (int i = 0; i < results.length; i++) {
385       results[i] = exists(gets.get(i));
386     }
387     return results;
388   }
389 
390   @Deprecated
391   public Boolean[] exists(List<Get> gets) throws IOException {
392     boolean[] results = existsAll(gets);
393     Boolean[] objectResults = new Boolean[results.length];
394     for (int i = 0; i < results.length; ++i) {
395       objectResults[i] = results[i];
396     }
397     return objectResults;
398   }
399 
400   @Override
401   public void put(Put put) throws IOException {
402     CellSetModel model = buildModelFromPut(put);
403     StringBuilder sb = new StringBuilder();
404     sb.append('/');
405     sb.append(Bytes.toStringBinary(name));
406     sb.append('/');
407     sb.append(Bytes.toStringBinary(put.getRow()));
408     for (int i = 0; i < maxRetries; i++) {
409       Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
410         model.createProtobufOutput());
411       int code = response.getCode();
412       switch (code) {
413       case 200:
414         return;
415       case 509:
416         try {
417           Thread.sleep(sleepTime);
418         } catch (InterruptedException e) {
419           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
420         }
421         break;
422       default:
423         throw new IOException("put request failed with " + code);
424       }
425     }
426     throw new IOException("put request timed out");
427   }
428 
429   @Override
430   public void put(List<Put> puts) throws IOException {
431     // this is a trick: The gateway accepts multiple rows in a cell set and
432     // ignores the row specification in the URI
433 
434     // separate puts by row
435     TreeMap<byte[],List<Cell>> map =
436       new TreeMap<byte[],List<Cell>>(Bytes.BYTES_COMPARATOR);
437     for (Put put: puts) {
438       byte[] row = put.getRow();
439       List<Cell> cells = map.get(row);
440       if (cells == null) {
441         cells = new ArrayList<Cell>();
442         map.put(row, cells);
443       }
444       for (List<Cell> l: put.getFamilyCellMap().values()) {
445         cells.addAll(l);
446       }
447     }
448 
449     // build the cell set
450     CellSetModel model = new CellSetModel();
451     for (Map.Entry<byte[], List<Cell>> e: map.entrySet()) {
452       RowModel row = new RowModel(e.getKey());
453       for (Cell cell: e.getValue()) {
454         row.addCell(new CellModel(cell));
455       }
456       model.addRow(row);
457     }
458 
459     // build path for multiput
460     StringBuilder sb = new StringBuilder();
461     sb.append('/');
462     sb.append(Bytes.toStringBinary(name));
463     sb.append("/$multiput"); // can be any nonexistent row
464     for (int i = 0; i < maxRetries; i++) {
465       Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF,
466         model.createProtobufOutput());
467       int code = response.getCode();
468       switch (code) {
469       case 200:
470         return;
471       case 509:
472         try {
473           Thread.sleep(sleepTime);
474         } catch (InterruptedException e) {
475           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
476         }
477         break;
478       default:
479         throw new IOException("multiput request failed with " + code);
480       }
481     }
482     throw new IOException("multiput request timed out");
483   }
484 
485   @Override
486   public void delete(Delete delete) throws IOException {
487     String spec = buildRowSpec(delete.getRow(), delete.getFamilyCellMap(),
488       delete.getTimeStamp(), delete.getTimeStamp(), 1);
489     for (int i = 0; i < maxRetries; i++) {
490       Response response = client.delete(spec);
491       int code = response.getCode();
492       switch (code) {
493       case 200:
494         return;
495       case 509:
496         try {
497           Thread.sleep(sleepTime);
498         } catch (InterruptedException e) {
499           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
500         }
501         break;
502       default:
503         throw new IOException("delete request failed with " + code);
504       }
505     }
506     throw new IOException("delete request timed out");
507   }
508 
509   @Override
510   public void delete(List<Delete> deletes) throws IOException {
511     for (Delete delete: deletes) {
512       delete(delete);
513     }
514   }
515 
516   public void flushCommits() throws IOException {
517     // no-op
518   }
519 
520   class Scanner implements ResultScanner {
521 
522     String uri;
523 
524     public Scanner(Scan scan) throws IOException {
525       ScannerModel model;
526       try {
527         model = ScannerModel.fromScan(scan);
528       } catch (Exception e) {
529         throw new IOException(e);
530       }
531       StringBuffer sb = new StringBuffer();
532       sb.append('/');
533       sb.append(Bytes.toStringBinary(name));
534       sb.append('/');
535       sb.append("scanner");
536       for (int i = 0; i < maxRetries; i++) {
537         Response response = client.post(sb.toString(),
538           Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
539         int code = response.getCode();
540         switch (code) {
541         case 201:
542           uri = response.getLocation();
543           return;
544         case 509:
545           try {
546             Thread.sleep(sleepTime);
547           } catch (InterruptedException e) {
548             throw (InterruptedIOException)new InterruptedIOException().initCause(e);
549           }
550           break;
551         default:
552           throw new IOException("scan request failed with " + code);
553         }
554       }
555       throw new IOException("scan request timed out");
556     }
557 
558     @Override
559     public Result[] next(int nbRows) throws IOException {
560       StringBuilder sb = new StringBuilder(uri);
561       sb.append("?n=");
562       sb.append(nbRows);
563       for (int i = 0; i < maxRetries; i++) {
564         Response response = client.get(sb.toString(),
565           Constants.MIMETYPE_PROTOBUF);
566         int code = response.getCode();
567         switch (code) {
568         case 200:
569           CellSetModel model = new CellSetModel();
570           model.getObjectFromMessage(response.getBody());
571           return buildResultFromModel(model);
572         case 204:
573         case 206:
574           return null;
575         case 509:
576           try {
577             Thread.sleep(sleepTime);
578           } catch (InterruptedException e) {
579             throw (InterruptedIOException)new InterruptedIOException().initCause(e);
580           }
581           break;
582         default:
583           throw new IOException("scanner.next request failed with " + code);
584         }
585       }
586       throw new IOException("scanner.next request timed out");
587     }
588 
589     @Override
590     public Result next() throws IOException {
591       Result[] results = next(1);
592       if (results == null || results.length < 1) {
593         return null;
594       }
595       return results[0];
596     }
597 
598     class Iter implements Iterator<Result> {
599 
600       Result cache;
601 
602       public Iter() {
603         try {
604           cache = Scanner.this.next();
605         } catch (IOException e) {
606           LOG.warn(StringUtils.stringifyException(e));
607         }
608       }
609 
610       @Override
611       public boolean hasNext() {
612         return cache != null;
613       }
614 
615       @Override
616       public Result next() {
617         Result result = cache;
618         try {
619           cache = Scanner.this.next();
620         } catch (IOException e) {
621           LOG.warn(StringUtils.stringifyException(e));
622           cache = null;
623         }
624         return result;
625       }
626 
627       @Override
628       public void remove() {
629         throw new RuntimeException("remove() not supported");
630       }
631 
632     }
633 
634     @Override
635     public Iterator<Result> iterator() {
636       return new Iter();
637     }
638 
639     @Override
640     public void close() {
641       try {
642         client.delete(uri);
643       } catch (IOException e) {
644         LOG.warn(StringUtils.stringifyException(e));
645       }
646     }
647 
648     @Override
649     public boolean renewLease() {
650       throw new RuntimeException("renewLease() not supported");
651     }
652   }
653 
654   @Override
655   public ResultScanner getScanner(Scan scan) throws IOException {
656     return new Scanner(scan);
657   }
658 
659   @Override
660   public ResultScanner getScanner(byte[] family) throws IOException {
661     Scan scan = new Scan();
662     scan.addFamily(family);
663     return new Scanner(scan);
664   }
665 
666   @Override
667   public ResultScanner getScanner(byte[] family, byte[] qualifier)
668       throws IOException {
669     Scan scan = new Scan();
670     scan.addColumn(family, qualifier);
671     return new Scanner(scan);
672   }
673 
674   public boolean isAutoFlush() {
675     return true;
676   }
677 
678   @Override
679   public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
680       byte[] value, Put put) throws IOException {
681     // column to check-the-value
682     put.add(new KeyValue(row, family, qualifier, value));
683 
684     CellSetModel model = buildModelFromPut(put);
685     StringBuilder sb = new StringBuilder();
686     sb.append('/');
687     sb.append(Bytes.toStringBinary(name));
688     sb.append('/');
689     sb.append(Bytes.toStringBinary(put.getRow()));
690     sb.append("?check=put");
691 
692     for (int i = 0; i < maxRetries; i++) {
693       Response response = client.put(sb.toString(),
694         Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
695       int code = response.getCode();
696       switch (code) {
697       case 200:
698         return true;
699       case 304: // NOT-MODIFIED
700         return false;
701       case 509:
702         try {
703           Thread.sleep(sleepTime);
704         } catch (final InterruptedException e) {
705           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
706         }
707         break;
708       default:
709         throw new IOException("checkAndPut request failed with " + code);
710       }
711     }
712     throw new IOException("checkAndPut request timed out");
713   }
714 
715   @Override
716   public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
717       CompareOp compareOp, byte[] value, Put put) throws IOException {
718     throw new IOException("checkAndPut for non-equal comparison not implemented");
719   }
720 
721   @Override
722   public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
723       byte[] value, Delete delete) throws IOException {
724     Put put = new Put(row);
725     // column to check-the-value
726     put.add(new KeyValue(row, family, qualifier, value));
727     CellSetModel model = buildModelFromPut(put);
728     StringBuilder sb = new StringBuilder();
729     sb.append('/');
730     sb.append(Bytes.toStringBinary(name));
731     sb.append('/');
732     sb.append(Bytes.toStringBinary(row));
733     sb.append("?check=delete");
734 
735     for (int i = 0; i < maxRetries; i++) {
736       Response response = client.put(sb.toString(),
737         Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput());
738       int code = response.getCode();
739       switch (code) {
740       case 200:
741         return true;
742       case 304: // NOT-MODIFIED
743         return false;
744       case 509:
745         try {
746           Thread.sleep(sleepTime);
747         } catch (final InterruptedException e) {
748           throw (InterruptedIOException)new InterruptedIOException().initCause(e);
749         }
750         break;
751       default:
752         throw new IOException("checkAndDelete request failed with " + code);
753       }
754     }
755     throw new IOException("checkAndDelete request timed out");
756   }
757 
758   @Override
759   public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
760       CompareOp compareOp, byte[] value, Delete delete) throws IOException {
761     throw new IOException("checkAndDelete for non-equal comparison not implemented");
762   }
763 
764   @Override
765   public Result increment(Increment increment) throws IOException {
766     throw new IOException("Increment not supported");
767   }
768 
769   @Override
770   public Result append(Append append) throws IOException {
771     throw new IOException("Append not supported");
772   }
773 
774   @Override
775   public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier,
776       long amount) throws IOException {
777     throw new IOException("incrementColumnValue not supported");
778   }
779 
780   @Override
781   public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier,
782       long amount, Durability durability) throws IOException {
783     throw new IOException("incrementColumnValue not supported");
784   }
785 
786   @Override
787   public void batch(List<? extends Row> actions, Object[] results) throws IOException {
788     throw new IOException("batch not supported");
789   }
790 
791   @Override
792   public <R> void batchCallback(List<? extends Row> actions, Object[] results,
793       Batch.Callback<R> callback) throws IOException, InterruptedException {
794     throw new IOException("batchCallback not supported");
795   }
796 
797   @Override
798   public CoprocessorRpcChannel coprocessorService(byte[] row) {
799     throw new UnsupportedOperationException("coprocessorService not implemented");
800   }
801 
802   @Override
803   public <T extends Service, R> Map<byte[], R> coprocessorService(Class<T> service,
804       byte[] startKey, byte[] endKey, Batch.Call<T, R> callable)
805       throws ServiceException, Throwable {
806     throw new UnsupportedOperationException("coprocessorService not implemented");
807   }
808 
809   @Override
810   public <T extends Service, R> void coprocessorService(Class<T> service,
811       byte[] startKey, byte[] endKey, Batch.Call<T, R> callable, Batch.Callback<R> callback)
812       throws ServiceException, Throwable {
813     throw new UnsupportedOperationException("coprocessorService not implemented");
814   }
815 
816   @Override
817   public void mutateRow(RowMutations rm) throws IOException {
818     throw new IOException("atomicMutation not supported");
819   }
820 
821   @Override
822   public long getWriteBufferSize() {
823     throw new UnsupportedOperationException("getWriteBufferSize not implemented");
824   }
825 
826   @Override
827   public void setWriteBufferSize(long writeBufferSize) throws IOException {
828     throw new IOException("setWriteBufferSize not supported");
829   }
830 
831   @Override
832   public <R extends Message> Map<byte[], R> batchCoprocessorService(
833       Descriptors.MethodDescriptor method, Message request,
834       byte[] startKey, byte[] endKey, R responsePrototype) throws ServiceException, Throwable {
835     throw new UnsupportedOperationException("batchCoprocessorService not implemented");
836   }
837 
838   @Override
839   public <R extends Message> void batchCoprocessorService(
840       Descriptors.MethodDescriptor method, Message request,
841       byte[] startKey, byte[] endKey, R responsePrototype, Callback<R> callback)
842       throws ServiceException, Throwable {
843     throw new UnsupportedOperationException("batchCoprocessorService not implemented");
844   }
845 
846   @Override public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier,
847       CompareOp compareOp, byte[] value, RowMutations rm) throws IOException {
848     throw new UnsupportedOperationException("checkAndMutate not implemented");
849   }
850 }