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