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