001/* 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020package org.apache.hadoop.hbase.rest.client; 021 022import com.google.protobuf.Descriptors; 023import com.google.protobuf.Message; 024import com.google.protobuf.Service; 025import com.google.protobuf.ServiceException; 026 027import java.io.IOException; 028import java.io.InterruptedIOException; 029import java.io.UnsupportedEncodingException; 030import java.net.URLEncoder; 031import java.util.ArrayList; 032import java.util.Collection; 033import java.util.Iterator; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037import java.util.TreeMap; 038import java.util.concurrent.TimeUnit; 039import org.apache.commons.lang3.NotImplementedException; 040import org.apache.hadoop.conf.Configuration; 041import org.apache.hadoop.hbase.Cell; 042import org.apache.hadoop.hbase.CellUtil; 043import org.apache.hadoop.hbase.CompareOperator; 044import org.apache.hadoop.hbase.HBaseConfiguration; 045import org.apache.hadoop.hbase.HConstants; 046import org.apache.hadoop.hbase.HTableDescriptor; 047import org.apache.hadoop.hbase.KeyValue; 048import org.apache.hadoop.hbase.TableName; 049import org.apache.hadoop.hbase.client.Durability; 050import org.apache.hadoop.hbase.filter.Filter; 051import org.apache.yetus.audience.InterfaceAudience; 052import org.slf4j.Logger; 053import org.slf4j.LoggerFactory; 054import org.apache.hadoop.hbase.client.Append; 055import org.apache.hadoop.hbase.client.Delete; 056import org.apache.hadoop.hbase.client.Get; 057import org.apache.hadoop.hbase.client.Increment; 058import org.apache.hadoop.hbase.client.Put; 059import org.apache.hadoop.hbase.client.RegionLocator; 060import org.apache.hadoop.hbase.client.Result; 061import org.apache.hadoop.hbase.client.ResultScanner; 062import org.apache.hadoop.hbase.client.Row; 063import org.apache.hadoop.hbase.client.RowMutations; 064import org.apache.hadoop.hbase.client.Scan; 065import org.apache.hadoop.hbase.client.Table; 066import org.apache.hadoop.hbase.client.TableDescriptor; 067import org.apache.hadoop.hbase.client.coprocessor.Batch; 068import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback; 069import org.apache.hadoop.hbase.client.metrics.ScanMetrics; 070import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp; 071import org.apache.hadoop.hbase.io.TimeRange; 072import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; 073import org.apache.hadoop.hbase.rest.Constants; 074import org.apache.hadoop.hbase.rest.model.CellModel; 075import org.apache.hadoop.hbase.rest.model.CellSetModel; 076import org.apache.hadoop.hbase.rest.model.RowModel; 077import org.apache.hadoop.hbase.rest.model.ScannerModel; 078import org.apache.hadoop.hbase.rest.model.TableSchemaModel; 079import org.apache.hadoop.hbase.util.Bytes; 080import org.apache.hadoop.util.StringUtils; 081 082import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 083 084/** 085 * HTable interface to remote tables accessed via REST gateway 086 */ 087@InterfaceAudience.Private 088public class RemoteHTable implements Table { 089 090 private static final Logger LOG = LoggerFactory.getLogger(RemoteHTable.class); 091 092 final Client client; 093 final Configuration conf; 094 final byte[] name; 095 final int maxRetries; 096 final long sleepTime; 097 098 @SuppressWarnings("rawtypes") 099 protected String buildRowSpec(final byte[] row, final Map familyMap, 100 final long startTime, final long endTime, final int maxVersions) { 101 StringBuffer sb = new StringBuffer(); 102 sb.append('/'); 103 sb.append(Bytes.toString(name)); 104 sb.append('/'); 105 sb.append(toURLEncodedBytes(row)); 106 Set families = familyMap.entrySet(); 107 if (families != null) { 108 Iterator i = familyMap.entrySet().iterator(); 109 sb.append('/'); 110 while (i.hasNext()) { 111 Map.Entry e = (Map.Entry)i.next(); 112 Collection quals = (Collection)e.getValue(); 113 if (quals == null || quals.isEmpty()) { 114 // this is an unqualified family. append the family name and NO ':' 115 sb.append(toURLEncodedBytes((byte[])e.getKey())); 116 } else { 117 Iterator ii = quals.iterator(); 118 while (ii.hasNext()) { 119 sb.append(toURLEncodedBytes((byte[])e.getKey())); 120 Object o = ii.next(); 121 // Puts use byte[] but Deletes use KeyValue 122 if (o instanceof byte[]) { 123 sb.append(':'); 124 sb.append(toURLEncodedBytes((byte[]) o)); 125 } else if (o instanceof KeyValue) { 126 if (((KeyValue) o).getQualifierLength() != 0) { 127 sb.append(':'); 128 sb.append(toURLEncodedBytes(CellUtil.cloneQualifier((KeyValue) o))); 129 } 130 } else { 131 throw new RuntimeException("object type not handled"); 132 } 133 if (ii.hasNext()) { 134 sb.append(','); 135 } 136 } 137 } 138 if (i.hasNext()) { 139 sb.append(','); 140 } 141 } 142 } 143 if (startTime >= 0 && endTime != Long.MAX_VALUE) { 144 sb.append('/'); 145 sb.append(startTime); 146 if (startTime != endTime) { 147 sb.append(','); 148 sb.append(endTime); 149 } 150 } else if (endTime != Long.MAX_VALUE) { 151 sb.append('/'); 152 sb.append(endTime); 153 } 154 if (maxVersions > 1) { 155 sb.append("?v="); 156 sb.append(maxVersions); 157 } 158 return sb.toString(); 159 } 160 161 protected String buildMultiRowSpec(final byte[][] rows, int maxVersions) { 162 StringBuilder sb = new StringBuilder(); 163 sb.append('/'); 164 sb.append(Bytes.toString(name)); 165 sb.append("/multiget/"); 166 if (rows == null || rows.length == 0) { 167 return sb.toString(); 168 } 169 sb.append("?"); 170 for(int i=0; i<rows.length; i++) { 171 byte[] rk = rows[i]; 172 if (i != 0) { 173 sb.append('&'); 174 } 175 sb.append("row="); 176 sb.append(toURLEncodedBytes(rk)); 177 } 178 sb.append("&v="); 179 sb.append(maxVersions); 180 181 return sb.toString(); 182 } 183 184 protected Result[] buildResultFromModel(final CellSetModel model) { 185 List<Result> results = new ArrayList<>(); 186 for (RowModel row: model.getRows()) { 187 List<Cell> kvs = new ArrayList<>(row.getCells().size()); 188 for (CellModel cell: row.getCells()) { 189 byte[][] split = CellUtil.parseColumn(cell.getColumn()); 190 byte[] column = split[0]; 191 byte[] qualifier = null; 192 if (split.length == 1) { 193 qualifier = HConstants.EMPTY_BYTE_ARRAY; 194 } else if (split.length == 2) { 195 qualifier = split[1]; 196 } else { 197 throw new IllegalArgumentException("Invalid familyAndQualifier provided."); 198 } 199 kvs.add(new KeyValue(row.getKey(), column, qualifier, 200 cell.getTimestamp(), cell.getValue())); 201 } 202 results.add(Result.create(kvs)); 203 } 204 return results.toArray(new Result[results.size()]); 205 } 206 207 protected CellSetModel buildModelFromPut(Put put) { 208 RowModel row = new RowModel(put.getRow()); 209 long ts = put.getTimestamp(); 210 for (List<Cell> cells: put.getFamilyCellMap().values()) { 211 for (Cell cell: cells) { 212 row.addCell(new CellModel(CellUtil.cloneFamily(cell), CellUtil.cloneQualifier(cell), 213 ts != HConstants.LATEST_TIMESTAMP ? ts : cell.getTimestamp(), 214 CellUtil.cloneValue(cell))); 215 } 216 } 217 CellSetModel model = new CellSetModel(); 218 model.addRow(row); 219 return model; 220 } 221 222 /** 223 * Constructor 224 */ 225 public RemoteHTable(Client client, String name) { 226 this(client, HBaseConfiguration.create(), Bytes.toBytes(name)); 227 } 228 229 /** 230 * Constructor 231 */ 232 public RemoteHTable(Client client, Configuration conf, String name) { 233 this(client, conf, Bytes.toBytes(name)); 234 } 235 236 /** 237 * Constructor 238 */ 239 public RemoteHTable(Client client, Configuration conf, byte[] name) { 240 this.client = client; 241 this.conf = conf; 242 this.name = name; 243 this.maxRetries = conf.getInt("hbase.rest.client.max.retries", 10); 244 this.sleepTime = conf.getLong("hbase.rest.client.sleep", 1000); 245 } 246 247 public byte[] getTableName() { 248 return name.clone(); 249 } 250 251 @Override 252 public TableName getName() { 253 return TableName.valueOf(name); 254 } 255 256 @Override 257 public Configuration getConfiguration() { 258 return conf; 259 } 260 261 @Override 262 @Deprecated 263 public HTableDescriptor getTableDescriptor() throws IOException { 264 StringBuilder sb = new StringBuilder(); 265 sb.append('/'); 266 sb.append(Bytes.toString(name)); 267 sb.append('/'); 268 sb.append("schema"); 269 for (int i = 0; i < maxRetries; i++) { 270 Response response = client.get(sb.toString(), Constants.MIMETYPE_PROTOBUF); 271 int code = response.getCode(); 272 switch (code) { 273 case 200: 274 TableSchemaModel schema = new TableSchemaModel(); 275 schema.getObjectFromMessage(response.getBody()); 276 return schema.getTableDescriptor(); 277 case 509: 278 try { 279 Thread.sleep(sleepTime); 280 } catch (InterruptedException e) { 281 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 282 } 283 break; 284 default: 285 throw new IOException("schema request returned " + code); 286 } 287 } 288 throw new IOException("schema request timed out"); 289 } 290 291 @Override 292 public void close() throws IOException { 293 client.shutdown(); 294 } 295 296 @Override 297 public Result get(Get get) throws IOException { 298 TimeRange range = get.getTimeRange(); 299 String spec = buildRowSpec(get.getRow(), get.getFamilyMap(), 300 range.getMin(), range.getMax(), get.getMaxVersions()); 301 if (get.getFilter() != null) { 302 LOG.warn("filters not supported on gets"); 303 } 304 Result[] results = getResults(spec); 305 if (results.length > 0) { 306 if (results.length > 1) { 307 LOG.warn("too many results for get (" + results.length + ")"); 308 } 309 return results[0]; 310 } else { 311 return new Result(); 312 } 313 } 314 315 @Override 316 public Result[] get(List<Get> gets) throws IOException { 317 byte[][] rows = new byte[gets.size()][]; 318 int maxVersions = 1; 319 int count = 0; 320 321 for(Get g:gets) { 322 323 if ( count == 0 ) { 324 maxVersions = g.getMaxVersions(); 325 } else if (g.getMaxVersions() != maxVersions) { 326 LOG.warn("MaxVersions on Gets do not match, using the first in the list ("+maxVersions+")"); 327 } 328 329 if (g.getFilter() != null) { 330 LOG.warn("filters not supported on gets"); 331 } 332 333 rows[count] = g.getRow(); 334 count ++; 335 } 336 337 String spec = buildMultiRowSpec(rows, maxVersions); 338 339 return getResults(spec); 340 } 341 342 private Result[] getResults(String spec) throws IOException { 343 for (int i = 0; i < maxRetries; i++) { 344 Response response = client.get(spec, Constants.MIMETYPE_PROTOBUF); 345 int code = response.getCode(); 346 switch (code) { 347 case 200: 348 CellSetModel model = new CellSetModel(); 349 model.getObjectFromMessage(response.getBody()); 350 Result[] results = buildResultFromModel(model); 351 if ( results.length > 0) { 352 return results; 353 } 354 // fall through 355 case 404: 356 return new Result[0]; 357 358 case 509: 359 try { 360 Thread.sleep(sleepTime); 361 } catch (InterruptedException e) { 362 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 363 } 364 break; 365 default: 366 throw new IOException("get request returned " + code); 367 } 368 } 369 throw new IOException("get request timed out"); 370 } 371 372 @Override 373 public boolean exists(Get get) throws IOException { 374 LOG.warn("exists() is really get(), just use get()"); 375 Result result = get(get); 376 return (result != null && !(result.isEmpty())); 377 } 378 379 @Override 380 public boolean[] exists(List<Get> gets) throws IOException { 381 LOG.warn("exists(List<Get>) is really list of get() calls, just use get()"); 382 boolean[] results = new boolean[gets.size()]; 383 for (int i = 0; i < results.length; i++) { 384 results[i] = exists(gets.get(i)); 385 } 386 return results; 387 } 388 389 @Override 390 public void put(Put put) throws IOException { 391 CellSetModel model = buildModelFromPut(put); 392 StringBuilder sb = new StringBuilder(); 393 sb.append('/'); 394 sb.append(Bytes.toString(name)); 395 sb.append('/'); 396 sb.append(toURLEncodedBytes(put.getRow())); 397 for (int i = 0; i < maxRetries; i++) { 398 Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF, 399 model.createProtobufOutput()); 400 int code = response.getCode(); 401 switch (code) { 402 case 200: 403 return; 404 case 509: 405 try { 406 Thread.sleep(sleepTime); 407 } catch (InterruptedException e) { 408 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 409 } 410 break; 411 default: 412 throw new IOException("put request failed with " + code); 413 } 414 } 415 throw new IOException("put request timed out"); 416 } 417 418 @Override 419 public void put(List<Put> puts) throws IOException { 420 // this is a trick: The gateway accepts multiple rows in a cell set and 421 // ignores the row specification in the URI 422 423 // separate puts by row 424 TreeMap<byte[],List<Cell>> map = new TreeMap<>(Bytes.BYTES_COMPARATOR); 425 for (Put put: puts) { 426 byte[] row = put.getRow(); 427 List<Cell> cells = map.get(row); 428 if (cells == null) { 429 cells = new ArrayList<>(); 430 map.put(row, cells); 431 } 432 for (List<Cell> l: put.getFamilyCellMap().values()) { 433 cells.addAll(l); 434 } 435 } 436 437 // build the cell set 438 CellSetModel model = new CellSetModel(); 439 for (Map.Entry<byte[], List<Cell>> e: map.entrySet()) { 440 RowModel row = new RowModel(e.getKey()); 441 for (Cell cell: e.getValue()) { 442 row.addCell(new CellModel(cell)); 443 } 444 model.addRow(row); 445 } 446 447 // build path for multiput 448 StringBuilder sb = new StringBuilder(); 449 sb.append('/'); 450 sb.append(Bytes.toString(name)); 451 sb.append("/$multiput"); // can be any nonexistent row 452 for (int i = 0; i < maxRetries; i++) { 453 Response response = client.put(sb.toString(), Constants.MIMETYPE_PROTOBUF, 454 model.createProtobufOutput()); 455 int code = response.getCode(); 456 switch (code) { 457 case 200: 458 return; 459 case 509: 460 try { 461 Thread.sleep(sleepTime); 462 } catch (InterruptedException e) { 463 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 464 } 465 break; 466 default: 467 throw new IOException("multiput request failed with " + code); 468 } 469 } 470 throw new IOException("multiput request timed out"); 471 } 472 473 @Override 474 public void delete(Delete delete) throws IOException { 475 String spec = buildRowSpec(delete.getRow(), delete.getFamilyCellMap(), 476 delete.getTimestamp(), delete.getTimestamp(), 1); 477 for (int i = 0; i < maxRetries; i++) { 478 Response response = client.delete(spec); 479 int code = response.getCode(); 480 switch (code) { 481 case 200: 482 return; 483 case 509: 484 try { 485 Thread.sleep(sleepTime); 486 } catch (InterruptedException e) { 487 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 488 } 489 break; 490 default: 491 throw new IOException("delete request failed with " + code); 492 } 493 } 494 throw new IOException("delete request timed out"); 495 } 496 497 @Override 498 public void delete(List<Delete> deletes) throws IOException { 499 for (Delete delete: deletes) { 500 delete(delete); 501 } 502 } 503 504 public void flushCommits() throws IOException { 505 // no-op 506 } 507 508 @Override 509 public TableDescriptor getDescriptor() throws IOException { 510 return getTableDescriptor(); 511 } 512 513 class Scanner implements ResultScanner { 514 515 String uri; 516 517 public Scanner(Scan scan) throws IOException { 518 ScannerModel model; 519 try { 520 model = ScannerModel.fromScan(scan); 521 } catch (Exception e) { 522 throw new IOException(e); 523 } 524 StringBuffer sb = new StringBuffer(); 525 sb.append('/'); 526 sb.append(Bytes.toString(name)); 527 sb.append('/'); 528 sb.append("scanner"); 529 for (int i = 0; i < maxRetries; i++) { 530 Response response = client.post(sb.toString(), 531 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput()); 532 int code = response.getCode(); 533 switch (code) { 534 case 201: 535 uri = response.getLocation(); 536 return; 537 case 509: 538 try { 539 Thread.sleep(sleepTime); 540 } catch (InterruptedException e) { 541 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 542 } 543 break; 544 default: 545 throw new IOException("scan request failed with " + code); 546 } 547 } 548 throw new IOException("scan request timed out"); 549 } 550 551 @Override 552 public Result[] next(int nbRows) throws IOException { 553 StringBuilder sb = new StringBuilder(uri); 554 sb.append("?n="); 555 sb.append(nbRows); 556 for (int i = 0; i < maxRetries; i++) { 557 Response response = client.get(sb.toString(), 558 Constants.MIMETYPE_PROTOBUF); 559 int code = response.getCode(); 560 switch (code) { 561 case 200: 562 CellSetModel model = new CellSetModel(); 563 model.getObjectFromMessage(response.getBody()); 564 return buildResultFromModel(model); 565 case 204: 566 case 206: 567 return null; 568 case 509: 569 try { 570 Thread.sleep(sleepTime); 571 } catch (InterruptedException e) { 572 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 573 } 574 break; 575 default: 576 throw new IOException("scanner.next request failed with " + code); 577 } 578 } 579 throw new IOException("scanner.next request timed out"); 580 } 581 582 @Override 583 public Result next() throws IOException { 584 Result[] results = next(1); 585 if (results == null || results.length < 1) { 586 return null; 587 } 588 return results[0]; 589 } 590 591 class Iter implements Iterator<Result> { 592 593 Result cache; 594 595 public Iter() { 596 try { 597 cache = Scanner.this.next(); 598 } catch (IOException e) { 599 LOG.warn(StringUtils.stringifyException(e)); 600 } 601 } 602 603 @Override 604 public boolean hasNext() { 605 return cache != null; 606 } 607 608 @Override 609 public Result next() { 610 Result result = cache; 611 try { 612 cache = Scanner.this.next(); 613 } catch (IOException e) { 614 LOG.warn(StringUtils.stringifyException(e)); 615 cache = null; 616 } 617 return result; 618 } 619 620 @Override 621 public void remove() { 622 throw new RuntimeException("remove() not supported"); 623 } 624 625 } 626 627 @Override 628 public Iterator<Result> iterator() { 629 return new Iter(); 630 } 631 632 @Override 633 public void close() { 634 try { 635 client.delete(uri); 636 } catch (IOException e) { 637 LOG.warn(StringUtils.stringifyException(e)); 638 } 639 } 640 641 @Override 642 public boolean renewLease() { 643 throw new RuntimeException("renewLease() not supported"); 644 } 645 646 @Override 647 public ScanMetrics getScanMetrics() { 648 throw new RuntimeException("getScanMetrics() not supported"); 649 } 650 } 651 652 @Override 653 public ResultScanner getScanner(Scan scan) throws IOException { 654 return new Scanner(scan); 655 } 656 657 @Override 658 public ResultScanner getScanner(byte[] family) throws IOException { 659 Scan scan = new Scan(); 660 scan.addFamily(family); 661 return new Scanner(scan); 662 } 663 664 @Override 665 public ResultScanner getScanner(byte[] family, byte[] qualifier) 666 throws IOException { 667 Scan scan = new Scan(); 668 scan.addColumn(family, qualifier); 669 return new Scanner(scan); 670 } 671 672 public boolean isAutoFlush() { 673 return true; 674 } 675 676 @Override 677 @Deprecated 678 public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, 679 byte[] value, Put put) throws IOException { 680 return doCheckAndPut(row, family, qualifier, value, put); 681 } 682 683 private boolean doCheckAndPut(byte[] row, byte[] family, byte[] qualifier, 684 byte[] value, Put put) throws IOException { 685 // column to check-the-value 686 put.add(new KeyValue(row, family, qualifier, value)); 687 688 CellSetModel model = buildModelFromPut(put); 689 StringBuilder sb = new StringBuilder(); 690 sb.append('/'); 691 sb.append(Bytes.toString(name)); 692 sb.append('/'); 693 sb.append(toURLEncodedBytes(put.getRow())); 694 sb.append("?check=put"); 695 696 for (int i = 0; i < maxRetries; i++) { 697 Response response = client.put(sb.toString(), 698 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput()); 699 int code = response.getCode(); 700 switch (code) { 701 case 200: 702 return true; 703 case 304: // NOT-MODIFIED 704 return false; 705 case 509: 706 try { 707 Thread.sleep(sleepTime); 708 } catch (final InterruptedException e) { 709 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 710 } 711 break; 712 default: 713 throw new IOException("checkAndPut request failed with " + code); 714 } 715 } 716 throw new IOException("checkAndPut request timed out"); 717 } 718 719 @Override 720 @Deprecated 721 public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, 722 CompareOp compareOp, byte[] value, Put put) throws IOException { 723 throw new IOException("checkAndPut for non-equal comparison not implemented"); 724 } 725 726 @Override 727 @Deprecated 728 public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, 729 CompareOperator compareOp, byte[] value, Put put) throws IOException { 730 throw new IOException("checkAndPut for non-equal comparison not implemented"); 731 } 732 733 @Override 734 public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, 735 byte[] value, Delete delete) throws IOException { 736 return doCheckAndDelete(row, family, qualifier, value, delete); 737 } 738 739 private boolean doCheckAndDelete(byte[] row, byte[] family, byte[] qualifier, 740 byte[] value, Delete delete) throws IOException { 741 Put put = new Put(row); 742 put.setFamilyCellMap(delete.getFamilyCellMap()); 743 // column to check-the-value 744 put.add(new KeyValue(row, family, qualifier, value)); 745 CellSetModel model = buildModelFromPut(put); 746 StringBuilder sb = new StringBuilder(); 747 sb.append('/'); 748 sb.append(Bytes.toString(name)); 749 sb.append('/'); 750 sb.append(toURLEncodedBytes(row)); 751 sb.append("?check=delete"); 752 753 for (int i = 0; i < maxRetries; i++) { 754 Response response = client.put(sb.toString(), 755 Constants.MIMETYPE_PROTOBUF, model.createProtobufOutput()); 756 int code = response.getCode(); 757 switch (code) { 758 case 200: 759 return true; 760 case 304: // NOT-MODIFIED 761 return false; 762 case 509: 763 try { 764 Thread.sleep(sleepTime); 765 } catch (final InterruptedException e) { 766 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 767 } 768 break; 769 default: 770 throw new IOException("checkAndDelete request failed with " + code); 771 } 772 } 773 throw new IOException("checkAndDelete request timed out"); 774 } 775 776 @Override 777 @Deprecated 778 public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, 779 CompareOp compareOp, byte[] value, Delete delete) throws IOException { 780 throw new IOException("checkAndDelete for non-equal comparison not implemented"); 781 } 782 783 @Override 784 @Deprecated 785 public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, 786 CompareOperator compareOp, byte[] value, Delete delete) throws IOException { 787 throw new IOException("checkAndDelete for non-equal comparison not implemented"); 788 } 789 790 @Override 791 public CheckAndMutateBuilder checkAndMutate(byte[] row, byte[] family) { 792 return new CheckAndMutateBuilderImpl(row, family); 793 } 794 795 @Override 796 public CheckAndMutateWithFilterBuilder checkAndMutate(byte[] row, Filter filter) { 797 throw new NotImplementedException("Implement later"); 798 } 799 800 @Override 801 @Deprecated 802 public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier, 803 CompareOp compareOp, byte[] value, RowMutations rm) throws IOException { 804 throw new UnsupportedOperationException("checkAndMutate not implemented"); 805 } 806 807 @Override 808 @Deprecated 809 public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier, 810 CompareOperator compareOp, byte[] value, RowMutations rm) throws IOException { 811 throw new UnsupportedOperationException("checkAndMutate not implemented"); 812 } 813 814 @Override 815 public Result increment(Increment increment) throws IOException { 816 throw new IOException("Increment not supported"); 817 } 818 819 @Override 820 public Result append(Append append) throws IOException { 821 throw new IOException("Append not supported"); 822 } 823 824 @Override 825 public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, 826 long amount) throws IOException { 827 throw new IOException("incrementColumnValue not supported"); 828 } 829 830 @Override 831 public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, 832 long amount, Durability durability) throws IOException { 833 throw new IOException("incrementColumnValue not supported"); 834 } 835 836 @Override 837 public void batch(List<? extends Row> actions, Object[] results) throws IOException { 838 throw new IOException("batch not supported"); 839 } 840 841 @Override 842 public <R> void batchCallback(List<? extends Row> actions, Object[] results, 843 Batch.Callback<R> callback) throws IOException, InterruptedException { 844 throw new IOException("batchCallback not supported"); 845 } 846 847 @Override 848 public CoprocessorRpcChannel coprocessorService(byte[] row) { 849 throw new UnsupportedOperationException("coprocessorService not implemented"); 850 } 851 852 @Override 853 public <T extends Service, R> Map<byte[], R> coprocessorService(Class<T> service, 854 byte[] startKey, byte[] endKey, Batch.Call<T, R> callable) 855 throws ServiceException, Throwable { 856 throw new UnsupportedOperationException("coprocessorService not implemented"); 857 } 858 859 @Override 860 public <T extends Service, R> void coprocessorService(Class<T> service, 861 byte[] startKey, byte[] endKey, Batch.Call<T, R> callable, Batch.Callback<R> callback) 862 throws ServiceException, Throwable { 863 throw new UnsupportedOperationException("coprocessorService not implemented"); 864 } 865 866 @Override 867 public void mutateRow(RowMutations rm) throws IOException { 868 throw new IOException("atomicMutation not supported"); 869 } 870 871 @Override 872 public <R extends Message> Map<byte[], R> batchCoprocessorService( 873 Descriptors.MethodDescriptor method, Message request, 874 byte[] startKey, byte[] endKey, R responsePrototype) throws ServiceException, Throwable { 875 throw new UnsupportedOperationException("batchCoprocessorService not implemented"); 876 } 877 878 @Override 879 public <R extends Message> void batchCoprocessorService( 880 Descriptors.MethodDescriptor method, Message request, 881 byte[] startKey, byte[] endKey, R responsePrototype, Callback<R> callback) 882 throws ServiceException, Throwable { 883 throw new UnsupportedOperationException("batchCoprocessorService not implemented"); 884 } 885 886 @Override 887 @Deprecated 888 public void setOperationTimeout(int operationTimeout) { 889 throw new UnsupportedOperationException(); 890 } 891 892 @Override 893 @Deprecated 894 public int getOperationTimeout() { 895 throw new UnsupportedOperationException(); 896 } 897 898 @Override 899 @Deprecated 900 public void setRpcTimeout(int rpcTimeout) { 901 throw new UnsupportedOperationException(); 902 } 903 904 @Override 905 public long getReadRpcTimeout(TimeUnit unit) { 906 throw new UnsupportedOperationException(); 907 } 908 909 @Override 910 @Deprecated 911 public int getRpcTimeout() { 912 throw new UnsupportedOperationException(); 913 } 914 915 @Override 916 public long getRpcTimeout(TimeUnit unit) { 917 throw new UnsupportedOperationException(); 918 } 919 920 @Override 921 @Deprecated 922 public int getReadRpcTimeout() { 923 throw new UnsupportedOperationException(); 924 } 925 926 @Override 927 @Deprecated 928 public void setReadRpcTimeout(int readRpcTimeout) { 929 throw new UnsupportedOperationException(); 930 } 931 932 @Override 933 public long getWriteRpcTimeout(TimeUnit unit) { 934 throw new UnsupportedOperationException(); 935 } 936 937 @Override 938 @Deprecated 939 public int getWriteRpcTimeout() { 940 throw new UnsupportedOperationException(); 941 } 942 943 @Override 944 @Deprecated 945 public void setWriteRpcTimeout(int writeRpcTimeout) { 946 throw new UnsupportedOperationException(); 947 } 948 949 @Override 950 public long getOperationTimeout(TimeUnit unit) { 951 throw new UnsupportedOperationException(); 952 } 953 954 /* 955 * Only a small subset of characters are valid in URLs. 956 * 957 * Row keys, column families, and qualifiers cannot be appended to URLs without first URL 958 * escaping. Table names are ok because they can only contain alphanumeric, ".","_", and "-" 959 * which are valid characters in URLs. 960 */ 961 private static String toURLEncodedBytes(byte[] row) { 962 try { 963 return URLEncoder.encode(new String(row, "UTF-8"), "UTF-8"); 964 } catch (UnsupportedEncodingException e) { 965 throw new IllegalStateException("URLEncoder doesn't support UTF-8", e); 966 } 967 } 968 969 private class CheckAndMutateBuilderImpl implements CheckAndMutateBuilder { 970 971 private final byte[] row; 972 private final byte[] family; 973 private byte[] qualifier; 974 private byte[] value; 975 976 CheckAndMutateBuilderImpl(byte[] row, byte[] family) { 977 this.row = Preconditions.checkNotNull(row, "row is null"); 978 this.family = Preconditions.checkNotNull(family, "family is null"); 979 } 980 981 @Override 982 public CheckAndMutateBuilder qualifier(byte[] qualifier) { 983 this.qualifier = Preconditions.checkNotNull(qualifier, "qualifier is null. Consider using" + 984 " an empty byte array, or just do not call this method if you want a null qualifier"); 985 return this; 986 } 987 988 @Override 989 public CheckAndMutateBuilder timeRange(TimeRange timeRange) { 990 throw new UnsupportedOperationException("timeRange not implemented"); 991 } 992 993 @Override 994 public CheckAndMutateBuilder ifNotExists() { 995 throw new UnsupportedOperationException("CheckAndMutate for non-equal comparison " 996 + "not implemented"); 997 } 998 999 @Override 1000 public CheckAndMutateBuilder ifMatches(CompareOperator compareOp, byte[] value) { 1001 if (compareOp == CompareOperator.EQUAL) { 1002 this.value = Preconditions.checkNotNull(value, "value is null"); 1003 return this; 1004 } else { 1005 throw new UnsupportedOperationException("CheckAndMutate for non-equal comparison " + 1006 "not implemented"); 1007 } 1008 } 1009 1010 @Override 1011 public CheckAndMutateBuilder ifEquals(byte[] value) { 1012 this.value = Preconditions.checkNotNull(value, "value is null"); 1013 return this; 1014 } 1015 1016 @Override 1017 public boolean thenPut(Put put) throws IOException { 1018 return doCheckAndPut(row, family, qualifier, value, put); 1019 } 1020 1021 @Override 1022 public boolean thenDelete(Delete delete) throws IOException { 1023 return doCheckAndDelete(row, family, qualifier, value, delete); 1024 } 1025 1026 @Override 1027 public boolean thenMutate(RowMutations mutation) throws IOException { 1028 throw new UnsupportedOperationException("thenMutate not implemented"); 1029 } 1030 } 1031 1032 @Override 1033 public RegionLocator getRegionLocator() throws IOException { 1034 throw new UnsupportedOperationException(); 1035 } 1036}