001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.rest.model; 019 020import com.fasterxml.jackson.annotation.JsonInclude; 021import java.io.IOException; 022import java.io.Serializable; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Base64; 026import java.util.List; 027import java.util.Map; 028import java.util.NavigableSet; 029import java.util.Objects; 030import javax.xml.bind.annotation.XmlAttribute; 031import javax.xml.bind.annotation.XmlElement; 032import javax.xml.bind.annotation.XmlRootElement; 033import org.apache.hadoop.hbase.CompareOperator; 034import org.apache.hadoop.hbase.HConstants; 035import org.apache.hadoop.hbase.client.Scan; 036import org.apache.hadoop.hbase.filter.BinaryComparator; 037import org.apache.hadoop.hbase.filter.BinaryPrefixComparator; 038import org.apache.hadoop.hbase.filter.BitComparator; 039import org.apache.hadoop.hbase.filter.ByteArrayComparable; 040import org.apache.hadoop.hbase.filter.ColumnCountGetFilter; 041import org.apache.hadoop.hbase.filter.ColumnPaginationFilter; 042import org.apache.hadoop.hbase.filter.ColumnPrefixFilter; 043import org.apache.hadoop.hbase.filter.ColumnRangeFilter; 044import org.apache.hadoop.hbase.filter.CompareFilter; 045import org.apache.hadoop.hbase.filter.DependentColumnFilter; 046import org.apache.hadoop.hbase.filter.FamilyFilter; 047import org.apache.hadoop.hbase.filter.Filter; 048import org.apache.hadoop.hbase.filter.FilterList; 049import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter; 050import org.apache.hadoop.hbase.filter.FuzzyRowFilter; 051import org.apache.hadoop.hbase.filter.InclusiveStopFilter; 052import org.apache.hadoop.hbase.filter.KeyOnlyFilter; 053import org.apache.hadoop.hbase.filter.MultiRowRangeFilter; 054import org.apache.hadoop.hbase.filter.MultiRowRangeFilter.RowRange; 055import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter; 056import org.apache.hadoop.hbase.filter.NullComparator; 057import org.apache.hadoop.hbase.filter.PageFilter; 058import org.apache.hadoop.hbase.filter.PrefixFilter; 059import org.apache.hadoop.hbase.filter.QualifierFilter; 060import org.apache.hadoop.hbase.filter.RandomRowFilter; 061import org.apache.hadoop.hbase.filter.RegexStringComparator; 062import org.apache.hadoop.hbase.filter.RowFilter; 063import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter; 064import org.apache.hadoop.hbase.filter.SingleColumnValueFilter; 065import org.apache.hadoop.hbase.filter.SkipFilter; 066import org.apache.hadoop.hbase.filter.SubstringComparator; 067import org.apache.hadoop.hbase.filter.TimestampsFilter; 068import org.apache.hadoop.hbase.filter.ValueFilter; 069import org.apache.hadoop.hbase.filter.WhileMatchFilter; 070import org.apache.hadoop.hbase.rest.ProtobufMessageHandler; 071import org.apache.hadoop.hbase.rest.RestUtil; 072import org.apache.hadoop.hbase.rest.protobuf.generated.ScannerMessage.Scanner; 073import org.apache.hadoop.hbase.security.visibility.Authorizations; 074import org.apache.hadoop.hbase.util.Bytes; 075import org.apache.hadoop.hbase.util.Pair; 076import org.apache.yetus.audience.InterfaceAudience; 077 078import org.apache.hbase.thirdparty.com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider; 079import org.apache.hbase.thirdparty.com.google.protobuf.ByteString; 080import org.apache.hbase.thirdparty.com.google.protobuf.CodedInputStream; 081import org.apache.hbase.thirdparty.com.google.protobuf.Message; 082import org.apache.hbase.thirdparty.com.google.protobuf.UnsafeByteOperations; 083import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType; 084 085/** 086 * A representation of Scanner parameters. 087 * 088 * <pre> 089 * <complexType name="Scanner"> 090 * <sequence> 091 * <element name="column" type="base64Binary" minOccurs="0" maxOccurs="unbounded"/> 092 * <element name="filter" type="string" minOccurs="0" maxOccurs="1"></element> 093 * </sequence> 094 * <attribute name="startRow" type="base64Binary"></attribute> 095 * <attribute name="endRow" type="base64Binary"></attribute> 096 * <attribute name="batch" type="int"></attribute> 097 * <attribute name="caching" type="int"></attribute> 098 * <attribute name="startTime" type="int"></attribute> 099 * <attribute name="endTime" type="int"></attribute> 100 * <attribute name="maxVersions" type="int"></attribute> 101 * </complexType> 102 * </pre> 103 */ 104@XmlRootElement(name = "Scanner") 105@JsonInclude(JsonInclude.Include.NON_NULL) 106@InterfaceAudience.Private 107public class ScannerModel implements ProtobufMessageHandler, Serializable { 108 109 private static final long serialVersionUID = 1L; 110 111 private byte[] startRow = HConstants.EMPTY_START_ROW; 112 private byte[] endRow = HConstants.EMPTY_END_ROW; 113 private List<byte[]> columns = new ArrayList<>(); 114 private int batch = Integer.MAX_VALUE; 115 private long startTime = 0; 116 private long endTime = Long.MAX_VALUE; 117 private String filter = null; 118 private int maxVersions = Integer.MAX_VALUE; 119 private int caching = -1; 120 private List<String> labels = new ArrayList<>(); 121 private boolean cacheBlocks = true; 122 private int limit = -1; 123 124 private boolean includeStartRow = true; 125 126 private boolean includeStopRow = false; 127 128 @XmlAttribute 129 public boolean isIncludeStopRow() { 130 return includeStopRow; 131 } 132 133 public void setIncludeStopRow(boolean includeStopRow) { 134 this.includeStopRow = includeStopRow; 135 } 136 137 @XmlAttribute 138 public boolean isIncludeStartRow() { 139 return includeStartRow; 140 } 141 142 public void setIncludeStartRow(boolean includeStartRow) { 143 this.includeStartRow = includeStartRow; 144 } 145 146 /** 147 * Implement lazily-instantiated singleton as per recipe here: 148 * http://literatejava.com/jvm/fastest-threadsafe-singleton-jvm/ 149 */ 150 private static class JaxbJsonProviderHolder { 151 static final JacksonJaxbJsonProvider INSTANCE = new JacksonJaxbJsonProvider(); 152 } 153 154 @XmlRootElement 155 static class FilterModel { 156 157 @XmlRootElement 158 static class ByteArrayComparableModel { 159 @XmlAttribute 160 public String type; 161 @XmlAttribute 162 public String value; 163 @XmlAttribute 164 public String op; 165 166 static enum ComparatorType { 167 BinaryComparator, 168 BinaryPrefixComparator, 169 BitComparator, 170 NullComparator, 171 RegexStringComparator, 172 SubstringComparator 173 } 174 175 public ByteArrayComparableModel() { 176 } 177 178 public ByteArrayComparableModel(ByteArrayComparable comparator) { 179 String typeName = comparator.getClass().getSimpleName(); 180 ComparatorType type = ComparatorType.valueOf(typeName); 181 this.type = typeName; 182 switch (type) { 183 case BinaryComparator: 184 case BinaryPrefixComparator: 185 this.value = Bytes.toString(Base64.getEncoder().encode(comparator.getValue())); 186 break; 187 case BitComparator: 188 this.value = Bytes.toString(Base64.getEncoder().encode(comparator.getValue())); 189 this.op = ((BitComparator) comparator).getOperator().toString(); 190 break; 191 case NullComparator: 192 break; 193 case RegexStringComparator: 194 case SubstringComparator: 195 this.value = Bytes.toString(comparator.getValue()); 196 break; 197 default: 198 throw new RuntimeException("unhandled filter type: " + type); 199 } 200 } 201 202 public ByteArrayComparable build() { 203 ByteArrayComparable comparator; 204 switch (ComparatorType.valueOf(type)) { 205 case BinaryComparator: 206 comparator = new BinaryComparator(Base64.getDecoder().decode(value)); 207 break; 208 case BinaryPrefixComparator: 209 comparator = new BinaryPrefixComparator(Base64.getDecoder().decode(value)); 210 break; 211 case BitComparator: 212 comparator = new BitComparator(Base64.getDecoder().decode(value), 213 BitComparator.BitwiseOp.valueOf(op)); 214 break; 215 case NullComparator: 216 comparator = new NullComparator(); 217 break; 218 case RegexStringComparator: 219 comparator = new RegexStringComparator(value); 220 break; 221 case SubstringComparator: 222 comparator = new SubstringComparator(value); 223 break; 224 default: 225 throw new RuntimeException("unhandled comparator type: " + type); 226 } 227 return comparator; 228 } 229 230 } 231 232 /** 233 * This DTO omits the pseudo-getters in MultiRowRangeFilter.RowRange which break Jackson 234 * deserialization. It also avoids adding those as dummy JSON elements. 235 */ 236 static class RowRangeModel { 237 238 protected byte[] startRow; 239 240 protected boolean startRowInclusive = true; 241 242 protected byte[] stopRow; 243 244 protected boolean stopRowInclusive = false; 245 246 public RowRangeModel() { 247 } 248 249 public RowRangeModel(MultiRowRangeFilter.RowRange rr) { 250 this.startRow = rr.getStartRow(); 251 this.startRowInclusive = rr.isStartRowInclusive(); 252 this.stopRow = rr.getStopRow(); 253 this.stopRowInclusive = rr.isStopRowInclusive(); 254 } 255 256 public MultiRowRangeFilter.RowRange build() { 257 return new MultiRowRangeFilter.RowRange(startRow, startRowInclusive, stopRow, 258 stopRowInclusive); 259 } 260 261 public byte[] getStartRow() { 262 return startRow; 263 } 264 265 public byte[] getStopRow() { 266 return stopRow; 267 } 268 269 /** Returns if start row is inclusive. */ 270 public boolean isStartRowInclusive() { 271 return startRowInclusive; 272 } 273 274 /** Returns if stop row is inclusive. */ 275 public boolean isStopRowInclusive() { 276 return stopRowInclusive; 277 } 278 279 @Override 280 public int hashCode() { 281 final int prime = 31; 282 int result = 1; 283 result = prime * result + Arrays.hashCode(startRow); 284 result = prime * result + Arrays.hashCode(stopRow); 285 result = prime * result + Objects.hash(startRowInclusive, stopRowInclusive); 286 return result; 287 } 288 289 @Override 290 public boolean equals(Object obj) { 291 if (this == obj) { 292 return true; 293 } 294 if (!(obj instanceof RowRangeModel)) { 295 return false; 296 } 297 RowRangeModel other = (RowRangeModel) obj; 298 return Arrays.equals(startRow, other.startRow) 299 && startRowInclusive == other.startRowInclusive && Arrays.equals(stopRow, other.stopRow) 300 && stopRowInclusive == other.stopRowInclusive; 301 } 302 303 } 304 305 static class FuzzyKeyModel { 306 307 protected byte[] key; 308 309 protected byte[] mask; 310 311 public FuzzyKeyModel() { 312 } 313 314 public FuzzyKeyModel(Pair<byte[], byte[]> keyWithMask) { 315 this.key = keyWithMask.getFirst(); 316 this.mask = keyWithMask.getSecond(); 317 } 318 319 public Pair<byte[], byte[]> build() { 320 return new Pair<>(key, mask); 321 } 322 323 public byte[] getKey() { 324 return key; 325 } 326 327 public void setKey(byte[] key) { 328 this.key = key; 329 } 330 331 public byte[] getMask() { 332 return mask; 333 } 334 335 public void setMask(byte[] mask) { 336 this.mask = mask; 337 } 338 339 @Override 340 public int hashCode() { 341 final int prime = 31; 342 int result = 1; 343 result = prime * result + Arrays.hashCode(key); 344 result = prime * result + Arrays.hashCode(mask); 345 return result; 346 } 347 348 @Override 349 public boolean equals(Object obj) { 350 if (this == obj) { 351 return true; 352 } 353 if (!(obj instanceof FuzzyKeyModel)) { 354 return false; 355 } 356 FuzzyKeyModel other = (FuzzyKeyModel) obj; 357 return Arrays.equals(key, other.key) && Arrays.equals(mask, other.mask); 358 } 359 360 } 361 362 // A grab bag of fields, would have been a union if this were C. 363 // These are null by default and will only be serialized if set (non null). 364 @XmlAttribute 365 public String type; 366 @XmlAttribute 367 public String op; 368 @XmlElement 369 ByteArrayComparableModel comparator; 370 @XmlAttribute 371 public String value; 372 @XmlElement 373 public List<FilterModel> filters; 374 @XmlAttribute 375 public Integer limit; 376 @XmlAttribute 377 public Integer offset; 378 @XmlAttribute 379 public String family; 380 @XmlAttribute 381 public String qualifier; 382 @XmlAttribute 383 public Boolean ifMissing; 384 @XmlAttribute 385 public Boolean latestVersion; 386 @XmlAttribute 387 public String minColumn; 388 @XmlAttribute 389 public Boolean minColumnInclusive; 390 @XmlAttribute 391 public String maxColumn; 392 @XmlAttribute 393 public Boolean maxColumnInclusive; 394 @XmlAttribute 395 public Boolean dropDependentColumn; 396 @XmlAttribute 397 public Float chance; 398 @XmlElement 399 public List<String> prefixes; 400 @XmlElement 401 private List<RowRangeModel> ranges; 402 @XmlElement 403 public List<Long> timestamps; 404 @XmlElement 405 private List<FuzzyKeyModel> fuzzyKeys; 406 407 static enum FilterType { 408 ColumnCountGetFilter, 409 ColumnPaginationFilter, 410 ColumnPrefixFilter, 411 ColumnRangeFilter, 412 DependentColumnFilter, 413 FamilyFilter, 414 FilterList, 415 FirstKeyOnlyFilter, 416 InclusiveStopFilter, 417 KeyOnlyFilter, 418 MultipleColumnPrefixFilter, 419 MultiRowRangeFilter, 420 PageFilter, 421 PrefixFilter, 422 QualifierFilter, 423 RandomRowFilter, 424 RowFilter, 425 SingleColumnValueExcludeFilter, 426 SingleColumnValueFilter, 427 SkipFilter, 428 TimestampsFilter, 429 ValueFilter, 430 WhileMatchFilter, 431 FuzzyRowFilter 432 } 433 434 public FilterModel() { 435 } 436 437 public FilterModel(Filter filter) { 438 String typeName = filter.getClass().getSimpleName(); 439 FilterType type = FilterType.valueOf(typeName); 440 this.type = typeName; 441 switch (type) { 442 case ColumnCountGetFilter: 443 this.limit = ((ColumnCountGetFilter) filter).getLimit(); 444 break; 445 case ColumnPaginationFilter: 446 this.limit = ((ColumnPaginationFilter) filter).getLimit(); 447 this.offset = ((ColumnPaginationFilter) filter).getOffset(); 448 break; 449 case ColumnPrefixFilter: 450 byte[] src = ((ColumnPrefixFilter) filter).getPrefix(); 451 this.value = Bytes.toString(Base64.getEncoder().encode(src)); 452 break; 453 case ColumnRangeFilter: 454 ColumnRangeFilter crf = (ColumnRangeFilter) filter; 455 this.minColumn = Bytes.toString(Base64.getEncoder().encode(crf.getMinColumn())); 456 this.minColumnInclusive = crf.getMinColumnInclusive(); 457 this.maxColumn = Bytes.toString(Base64.getEncoder().encode(crf.getMaxColumn())); 458 this.maxColumnInclusive = crf.getMaxColumnInclusive(); 459 break; 460 case DependentColumnFilter: { 461 DependentColumnFilter dcf = (DependentColumnFilter) filter; 462 this.family = Bytes.toString(Base64.getEncoder().encode(dcf.getFamily())); 463 byte[] qualifier = dcf.getQualifier(); 464 if (qualifier != null) { 465 this.qualifier = Bytes.toString(Base64.getEncoder().encode(qualifier)); 466 } 467 this.op = dcf.getCompareOperator().toString(); 468 this.comparator = new ByteArrayComparableModel(dcf.getComparator()); 469 this.dropDependentColumn = dcf.dropDependentColumn(); 470 } 471 break; 472 case FilterList: 473 this.op = ((FilterList) filter).getOperator().toString(); 474 this.filters = new ArrayList<>(); 475 for (Filter child : ((FilterList) filter).getFilters()) { 476 this.filters.add(new FilterModel(child)); 477 } 478 break; 479 case FirstKeyOnlyFilter: 480 case KeyOnlyFilter: 481 break; 482 case InclusiveStopFilter: 483 this.value = Bytes 484 .toString(Base64.getEncoder().encode(((InclusiveStopFilter) filter).getStopRowKey())); 485 break; 486 case MultipleColumnPrefixFilter: 487 this.prefixes = new ArrayList<>(); 488 for (byte[] prefix : ((MultipleColumnPrefixFilter) filter).getPrefix()) { 489 this.prefixes.add(Bytes.toString(Base64.getEncoder().encode(prefix))); 490 } 491 break; 492 case MultiRowRangeFilter: 493 this.ranges = new ArrayList<>(); 494 for (RowRange range : ((MultiRowRangeFilter) filter).getRowRanges()) { 495 this.ranges.add(new RowRangeModel(range)); 496 } 497 break; 498 case PageFilter: 499 this.value = Long.toString(((PageFilter) filter).getPageSize()); 500 break; 501 case PrefixFilter: 502 this.value = 503 Bytes.toString(Base64.getEncoder().encode(((PrefixFilter) filter).getPrefix())); 504 break; 505 case FamilyFilter: 506 case QualifierFilter: 507 case RowFilter: 508 case ValueFilter: 509 this.op = ((CompareFilter) filter).getCompareOperator().toString(); 510 this.comparator = new ByteArrayComparableModel(((CompareFilter) filter).getComparator()); 511 break; 512 case RandomRowFilter: 513 this.chance = ((RandomRowFilter) filter).getChance(); 514 break; 515 case SingleColumnValueExcludeFilter: 516 case SingleColumnValueFilter: { 517 SingleColumnValueFilter scvf = (SingleColumnValueFilter) filter; 518 this.family = Bytes.toString(Base64.getEncoder().encode(scvf.getFamily())); 519 byte[] qualifier = scvf.getQualifier(); 520 if (qualifier != null) { 521 this.qualifier = Bytes.toString(Base64.getEncoder().encode(qualifier)); 522 } 523 this.op = scvf.getCompareOperator().toString(); 524 this.comparator = new ByteArrayComparableModel(scvf.getComparator()); 525 if (scvf.getFilterIfMissing()) { 526 this.ifMissing = true; 527 } 528 if (scvf.getLatestVersionOnly()) { 529 this.latestVersion = true; 530 } 531 } 532 break; 533 case SkipFilter: 534 this.filters = new ArrayList<>(); 535 this.filters.add(new FilterModel(((SkipFilter) filter).getFilter())); 536 break; 537 case TimestampsFilter: 538 this.timestamps = ((TimestampsFilter) filter).getTimestamps(); 539 break; 540 case WhileMatchFilter: 541 this.filters = new ArrayList<>(); 542 this.filters.add(new FilterModel(((WhileMatchFilter) filter).getFilter())); 543 break; 544 case FuzzyRowFilter: 545 this.fuzzyKeys = new ArrayList<>(((FuzzyRowFilter) filter).getFuzzyKeys().size()); 546 for (Pair<byte[], byte[]> keyWithMask : ((FuzzyRowFilter) filter).getFuzzyKeys()) { 547 this.fuzzyKeys.add(new FuzzyKeyModel(keyWithMask)); 548 } 549 break; 550 default: 551 throw new RuntimeException("unhandled filter type " + type); 552 } 553 } 554 555 public Filter build() { 556 Filter filter; 557 switch (FilterType.valueOf(type)) { 558 case ColumnCountGetFilter: 559 filter = new ColumnCountGetFilter(limit); 560 break; 561 case ColumnPaginationFilter: 562 filter = new ColumnPaginationFilter(limit, offset); 563 break; 564 case ColumnPrefixFilter: 565 filter = new ColumnPrefixFilter(Base64.getDecoder().decode(value)); 566 break; 567 case ColumnRangeFilter: 568 filter = new ColumnRangeFilter(Base64.getDecoder().decode(minColumn), minColumnInclusive, 569 Base64.getDecoder().decode(maxColumn), maxColumnInclusive); 570 break; 571 case DependentColumnFilter: 572 filter = new DependentColumnFilter(Base64.getDecoder().decode(family), 573 qualifier != null ? Base64.getDecoder().decode(qualifier) : null, dropDependentColumn, 574 CompareOperator.valueOf(op), comparator.build()); 575 break; 576 case FamilyFilter: 577 filter = new FamilyFilter(CompareOperator.valueOf(op), comparator.build()); 578 break; 579 case FilterList: { 580 List<Filter> list = new ArrayList<>(filters.size()); 581 for (FilterModel model : filters) { 582 list.add(model.build()); 583 } 584 filter = new FilterList(FilterList.Operator.valueOf(op), list); 585 } 586 break; 587 case FirstKeyOnlyFilter: 588 filter = new FirstKeyOnlyFilter(); 589 break; 590 case InclusiveStopFilter: 591 filter = new InclusiveStopFilter(Base64.getDecoder().decode(value)); 592 break; 593 case KeyOnlyFilter: 594 filter = new KeyOnlyFilter(); 595 break; 596 case MultipleColumnPrefixFilter: { 597 byte[][] values = new byte[prefixes.size()][]; 598 for (int i = 0; i < prefixes.size(); i++) { 599 values[i] = Base64.getDecoder().decode(prefixes.get(i)); 600 } 601 filter = new MultipleColumnPrefixFilter(values); 602 } 603 break; 604 case MultiRowRangeFilter: { 605 ArrayList<MultiRowRangeFilter.RowRange> rowRanges = new ArrayList<>(ranges.size()); 606 for (RowRangeModel rangeModel : ranges) { 607 rowRanges.add(rangeModel.build()); 608 } 609 filter = new MultiRowRangeFilter(rowRanges); 610 } 611 break; 612 case PageFilter: 613 filter = new PageFilter(Long.parseLong(value)); 614 break; 615 case PrefixFilter: 616 filter = new PrefixFilter(Base64.getDecoder().decode(value)); 617 break; 618 case QualifierFilter: 619 filter = new QualifierFilter(CompareOperator.valueOf(op), comparator.build()); 620 break; 621 case RandomRowFilter: 622 filter = new RandomRowFilter(chance); 623 break; 624 case RowFilter: 625 filter = new RowFilter(CompareOperator.valueOf(op), comparator.build()); 626 break; 627 case SingleColumnValueFilter: 628 filter = new SingleColumnValueFilter(Base64.getDecoder().decode(family), 629 qualifier != null ? Base64.getDecoder().decode(qualifier) : null, 630 CompareOperator.valueOf(op), comparator.build()); 631 if (ifMissing != null) { 632 ((SingleColumnValueFilter) filter).setFilterIfMissing(ifMissing); 633 } 634 if (latestVersion != null) { 635 ((SingleColumnValueFilter) filter).setLatestVersionOnly(latestVersion); 636 } 637 break; 638 case SingleColumnValueExcludeFilter: 639 filter = new SingleColumnValueExcludeFilter(Base64.getDecoder().decode(family), 640 qualifier != null ? Base64.getDecoder().decode(qualifier) : null, 641 CompareOperator.valueOf(op), comparator.build()); 642 if (ifMissing != null) { 643 ((SingleColumnValueExcludeFilter) filter).setFilterIfMissing(ifMissing); 644 } 645 if (latestVersion != null) { 646 ((SingleColumnValueExcludeFilter) filter).setLatestVersionOnly(latestVersion); 647 } 648 break; 649 case SkipFilter: 650 filter = new SkipFilter(filters.get(0).build()); 651 break; 652 case TimestampsFilter: 653 filter = new TimestampsFilter(timestamps); 654 break; 655 case ValueFilter: 656 filter = new ValueFilter(CompareOperator.valueOf(op), comparator.build()); 657 break; 658 case WhileMatchFilter: 659 filter = new WhileMatchFilter(filters.get(0).build()); 660 break; 661 case FuzzyRowFilter: { 662 ArrayList<Pair<byte[], byte[]>> fuzzyKeyArgs = new ArrayList<>(fuzzyKeys.size()); 663 for (FuzzyKeyModel keyModel : fuzzyKeys) { 664 fuzzyKeyArgs.add(keyModel.build()); 665 } 666 filter = new FuzzyRowFilter(fuzzyKeyArgs); 667 } 668 break; 669 default: 670 throw new RuntimeException("unhandled filter type: " + type); 671 } 672 return filter; 673 } 674 675 } 676 677 /** 678 * Get the <code>JacksonJaxbJsonProvider</code> instance; 679 * @return A <code>JacksonJaxbJsonProvider</code>. 680 */ 681 private static JacksonJaxbJsonProvider getJasonProvider() { 682 return JaxbJsonProviderHolder.INSTANCE; 683 } 684 685 /** 686 * @param s the JSON representation of the filter 687 * @return the filter 688 */ 689 public static Filter buildFilter(String s) throws Exception { 690 FilterModel model = 691 getJasonProvider().locateMapper(FilterModel.class, MediaType.APPLICATION_JSON_TYPE) 692 .readValue(s, FilterModel.class); 693 return model.build(); 694 } 695 696 /** 697 * @param filter the filter 698 * @return the JSON representation of the filter 699 */ 700 public static String stringifyFilter(final Filter filter) throws Exception { 701 return getJasonProvider().locateMapper(FilterModel.class, MediaType.APPLICATION_JSON_TYPE) 702 .writeValueAsString(new FilterModel(filter)); 703 } 704 705 private static final byte[] COLUMN_DIVIDER = Bytes.toBytes(":"); 706 707 /** 708 * @param scan the scan specification 709 */ 710 public static ScannerModel fromScan(Scan scan) throws Exception { 711 ScannerModel model = new ScannerModel(); 712 model.setStartRow(scan.getStartRow()); 713 model.setEndRow(scan.getStopRow()); 714 Map<byte[], NavigableSet<byte[]>> families = scan.getFamilyMap(); 715 if (families != null) { 716 for (Map.Entry<byte[], NavigableSet<byte[]>> entry : families.entrySet()) { 717 if (entry.getValue() != null) { 718 for (byte[] qualifier : entry.getValue()) { 719 model.addColumn(Bytes.add(entry.getKey(), COLUMN_DIVIDER, qualifier)); 720 } 721 } else { 722 model.addColumn(entry.getKey()); 723 } 724 } 725 } 726 model.setStartTime(scan.getTimeRange().getMin()); 727 model.setEndTime(scan.getTimeRange().getMax()); 728 int caching = scan.getCaching(); 729 if (caching > 0) { 730 model.setCaching(caching); 731 } 732 int batch = scan.getBatch(); 733 if (batch > 0) { 734 model.setBatch(batch); 735 } 736 int maxVersions = scan.getMaxVersions(); 737 if (maxVersions > 0) { 738 model.setMaxVersions(maxVersions); 739 } 740 if (scan.getLimit() > 0) { 741 model.setLimit(scan.getLimit()); 742 } 743 Filter filter = scan.getFilter(); 744 if (filter != null) { 745 model.setFilter(stringifyFilter(filter)); 746 } 747 // Add the visbility labels if found in the attributes 748 Authorizations authorizations = scan.getAuthorizations(); 749 if (authorizations != null) { 750 List<String> labels = authorizations.getLabels(); 751 for (String label : labels) { 752 model.addLabel(label); 753 } 754 } 755 model.setIncludeStartRow(scan.includeStartRow()); 756 model.setIncludeStopRow(scan.includeStopRow()); 757 return model; 758 } 759 760 /** 761 * Default constructor 762 */ 763 public ScannerModel() { 764 } 765 766 /** 767 * Constructor 768 * @param startRow the start key of the row-range 769 * @param endRow the end key of the row-range 770 * @param columns the columns to scan 771 * @param batch the number of values to return in batch 772 * @param caching the number of rows that the scanner will fetch at once 773 * @param endTime the upper bound on timestamps of values of interest 774 * @param maxVersions the maximum number of versions to return 775 * @param filter a filter specification (values with timestamps later than this are excluded) 776 */ 777 public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns, int batch, int caching, 778 long endTime, int maxVersions, String filter) { 779 super(); 780 this.startRow = startRow; 781 this.endRow = endRow; 782 this.columns = columns; 783 this.batch = batch; 784 this.caching = caching; 785 this.endTime = endTime; 786 this.maxVersions = maxVersions; 787 this.filter = filter; 788 } 789 790 /** 791 * Constructor 792 * @param startRow the start key of the row-range 793 * @param endRow the end key of the row-range 794 * @param columns the columns to scan 795 * @param batch the number of values to return in batch 796 * @param caching the number of rows that the scanner will fetch at once 797 * @param startTime the lower bound on timestamps of values of interest (values with timestamps 798 * earlier than this are excluded) 799 * @param endTime the upper bound on timestamps of values of interest (values with timestamps 800 * later than this are excluded) 801 * @param filter a filter specification 802 */ 803 public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns, int batch, int caching, 804 long startTime, long endTime, String filter) { 805 super(); 806 this.startRow = startRow; 807 this.endRow = endRow; 808 this.columns = columns; 809 this.batch = batch; 810 this.caching = caching; 811 this.startTime = startTime; 812 this.endTime = endTime; 813 this.filter = filter; 814 } 815 816 /** 817 * Add a column to the column set 818 * @param column the column name, as <column>(:<qualifier>)? 819 */ 820 public void addColumn(byte[] column) { 821 columns.add(column); 822 } 823 824 /** 825 * Add a visibility label to the scan 826 */ 827 public void addLabel(String label) { 828 labels.add(label); 829 } 830 831 /** Returns true if a start row was specified */ 832 public boolean hasStartRow() { 833 return !Bytes.equals(startRow, HConstants.EMPTY_START_ROW); 834 } 835 836 /** Returns start row */ 837 @XmlAttribute 838 public byte[] getStartRow() { 839 return startRow; 840 } 841 842 /** Returns true if an end row was specified */ 843 public boolean hasEndRow() { 844 return !Bytes.equals(endRow, HConstants.EMPTY_END_ROW); 845 } 846 847 /** Returns end row */ 848 @XmlAttribute 849 public byte[] getEndRow() { 850 return endRow; 851 } 852 853 /** Returns list of columns of interest in column:qualifier format, or empty for all */ 854 @XmlElement(name = "column") 855 public List<byte[]> getColumns() { 856 return columns; 857 } 858 859 @XmlElement(name = "labels") 860 public List<String> getLabels() { 861 return labels; 862 } 863 864 /** Returns the number of cells to return in batch */ 865 @XmlAttribute 866 public int getBatch() { 867 return batch; 868 } 869 870 /** Returns the number of rows that the scanner to fetch at once */ 871 @XmlAttribute 872 public int getCaching() { 873 return caching; 874 } 875 876 /** Returns the limit specification */ 877 @XmlAttribute 878 public int getLimit() { 879 return limit; 880 } 881 882 /** Returns true if HFile blocks should be cached on the servers for this scan, false otherwise */ 883 @XmlAttribute 884 public boolean getCacheBlocks() { 885 return cacheBlocks; 886 } 887 888 /** Returns the lower bound on timestamps of items of interest */ 889 @XmlAttribute 890 public long getStartTime() { 891 return startTime; 892 } 893 894 /** Returns the upper bound on timestamps of items of interest */ 895 @XmlAttribute 896 public long getEndTime() { 897 return endTime; 898 } 899 900 /** Returns maximum number of versions to return */ 901 @XmlAttribute 902 public int getMaxVersions() { 903 return maxVersions; 904 } 905 906 /** Returns the filter specification */ 907 @XmlElement 908 public String getFilter() { 909 return filter; 910 } 911 912 /** 913 * @param startRow start row 914 */ 915 public void setStartRow(byte[] startRow) { 916 this.startRow = startRow; 917 } 918 919 /** 920 * @param endRow end row 921 */ 922 public void setEndRow(byte[] endRow) { 923 this.endRow = endRow; 924 } 925 926 /** 927 * @param columns list of columns of interest in column:qualifier format, or empty for all 928 */ 929 public void setColumns(List<byte[]> columns) { 930 this.columns = columns; 931 } 932 933 /** 934 * @param batch the number of cells to return in batch 935 */ 936 public void setBatch(int batch) { 937 this.batch = batch; 938 } 939 940 /** 941 * @param caching the number of rows to fetch at once 942 */ 943 public void setCaching(int caching) { 944 this.caching = caching; 945 } 946 947 /** 948 * @param value true if HFile blocks should be cached on the servers for this scan, false 949 * otherwise 950 */ 951 public void setCacheBlocks(boolean value) { 952 this.cacheBlocks = value; 953 } 954 955 /** 956 * @param limit the number of rows can fetch of each scanner at lifetime 957 */ 958 public void setLimit(int limit) { 959 this.limit = limit; 960 } 961 962 /** 963 * @param maxVersions maximum number of versions to return 964 */ 965 public void setMaxVersions(int maxVersions) { 966 this.maxVersions = maxVersions; 967 } 968 969 /** 970 * @param startTime the lower bound on timestamps of values of interest 971 */ 972 public void setStartTime(long startTime) { 973 this.startTime = startTime; 974 } 975 976 /** 977 * @param endTime the upper bound on timestamps of values of interest 978 */ 979 public void setEndTime(long endTime) { 980 this.endTime = endTime; 981 } 982 983 /** 984 * @param filter the filter specification 985 */ 986 public void setFilter(String filter) { 987 this.filter = filter; 988 } 989 990 @Override 991 public Message messageFromObject() { 992 Scanner.Builder builder = Scanner.newBuilder(); 993 if (!Bytes.equals(startRow, HConstants.EMPTY_START_ROW)) { 994 builder.setStartRow(UnsafeByteOperations.unsafeWrap(startRow)); 995 } 996 if (!Bytes.equals(endRow, HConstants.EMPTY_START_ROW)) { 997 builder.setEndRow(UnsafeByteOperations.unsafeWrap(endRow)); 998 } 999 for (byte[] column : columns) { 1000 builder.addColumns(UnsafeByteOperations.unsafeWrap(column)); 1001 } 1002 if (startTime != 0) { 1003 builder.setStartTime(startTime); 1004 } 1005 if (endTime != 0) { 1006 builder.setEndTime(endTime); 1007 } 1008 builder.setBatch(getBatch()); 1009 if (caching > 0) { 1010 builder.setCaching(caching); 1011 } 1012 if (limit > 0) { 1013 builder.setLimit(limit); 1014 } 1015 builder.setMaxVersions(maxVersions); 1016 if (filter != null) { 1017 builder.setFilter(filter); 1018 } 1019 if (labels != null && labels.size() > 0) { 1020 for (String label : labels) 1021 builder.addLabels(label); 1022 } 1023 builder.setCacheBlocks(cacheBlocks); 1024 builder.setIncludeStartRow(includeStartRow); 1025 builder.setIncludeStopRow(includeStopRow); 1026 return builder.build(); 1027 } 1028 1029 @Override 1030 public ProtobufMessageHandler getObjectFromMessage(CodedInputStream cis) throws IOException { 1031 Scanner.Builder builder = Scanner.newBuilder(); 1032 RestUtil.mergeFrom(builder, cis); 1033 if (builder.hasStartRow()) { 1034 startRow = builder.getStartRow().toByteArray(); 1035 } 1036 if (builder.hasEndRow()) { 1037 endRow = builder.getEndRow().toByteArray(); 1038 } 1039 for (ByteString column : builder.getColumnsList()) { 1040 addColumn(column.toByteArray()); 1041 } 1042 if (builder.hasBatch()) { 1043 batch = builder.getBatch(); 1044 } 1045 if (builder.hasCaching()) { 1046 caching = builder.getCaching(); 1047 } 1048 if (builder.hasLimit()) { 1049 limit = builder.getLimit(); 1050 } 1051 if (builder.hasStartTime()) { 1052 startTime = builder.getStartTime(); 1053 } 1054 if (builder.hasEndTime()) { 1055 endTime = builder.getEndTime(); 1056 } 1057 if (builder.hasMaxVersions()) { 1058 maxVersions = builder.getMaxVersions(); 1059 } 1060 if (builder.hasFilter()) { 1061 filter = builder.getFilter(); 1062 } 1063 if (builder.getLabelsList() != null) { 1064 List<String> labels = builder.getLabelsList(); 1065 for (String label : labels) { 1066 addLabel(label); 1067 } 1068 } 1069 if (builder.hasCacheBlocks()) { 1070 this.cacheBlocks = builder.getCacheBlocks(); 1071 } 1072 if (builder.hasIncludeStartRow()) { 1073 this.includeStartRow = builder.getIncludeStartRow(); 1074 } 1075 if (builder.hasIncludeStopRow()) { 1076 this.includeStopRow = builder.getIncludeStopRow(); 1077 } 1078 return this; 1079 } 1080 1081}