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 com.google.protobuf.ByteString;
022import java.io.IOException;
023import java.io.Serializable;
024import java.util.ArrayList;
025import java.util.Base64;
026import java.util.List;
027import java.util.Map;
028import java.util.NavigableSet;
029import javax.xml.bind.annotation.XmlAttribute;
030import javax.xml.bind.annotation.XmlElement;
031import javax.xml.bind.annotation.XmlRootElement;
032import org.apache.hadoop.hbase.CompareOperator;
033import org.apache.hadoop.hbase.HConstants;
034import org.apache.hadoop.hbase.client.Scan;
035import org.apache.hadoop.hbase.filter.BinaryComparator;
036import org.apache.hadoop.hbase.filter.BinaryPrefixComparator;
037import org.apache.hadoop.hbase.filter.BitComparator;
038import org.apache.hadoop.hbase.filter.ByteArrayComparable;
039import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
040import org.apache.hadoop.hbase.filter.ColumnPaginationFilter;
041import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
042import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
043import org.apache.hadoop.hbase.filter.CompareFilter;
044import org.apache.hadoop.hbase.filter.DependentColumnFilter;
045import org.apache.hadoop.hbase.filter.FamilyFilter;
046import org.apache.hadoop.hbase.filter.Filter;
047import org.apache.hadoop.hbase.filter.FilterList;
048import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
049import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
050import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
051import org.apache.hadoop.hbase.filter.MultiRowRangeFilter;
052import org.apache.hadoop.hbase.filter.MultiRowRangeFilter.RowRange;
053import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
054import org.apache.hadoop.hbase.filter.NullComparator;
055import org.apache.hadoop.hbase.filter.PageFilter;
056import org.apache.hadoop.hbase.filter.PrefixFilter;
057import org.apache.hadoop.hbase.filter.QualifierFilter;
058import org.apache.hadoop.hbase.filter.RandomRowFilter;
059import org.apache.hadoop.hbase.filter.RegexStringComparator;
060import org.apache.hadoop.hbase.filter.RowFilter;
061import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
062import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
063import org.apache.hadoop.hbase.filter.SkipFilter;
064import org.apache.hadoop.hbase.filter.SubstringComparator;
065import org.apache.hadoop.hbase.filter.TimestampsFilter;
066import org.apache.hadoop.hbase.filter.ValueFilter;
067import org.apache.hadoop.hbase.filter.WhileMatchFilter;
068import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
069import org.apache.hadoop.hbase.rest.ProtobufMessageHandler;
070import org.apache.hadoop.hbase.rest.protobuf.generated.ScannerMessage.Scanner;
071import org.apache.hadoop.hbase.security.visibility.Authorizations;
072import org.apache.hadoop.hbase.util.ByteStringer;
073import org.apache.hadoop.hbase.util.Bytes;
074import org.apache.yetus.audience.InterfaceAudience;
075
076import org.apache.hbase.thirdparty.com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider;
077import org.apache.hbase.thirdparty.javax.ws.rs.core.MediaType;
078
079/**
080 * A representation of Scanner parameters.
081 *
082 * <pre>
083 * &lt;complexType name="Scanner"&gt;
084 *   &lt;sequence&gt;
085 *     &lt;element name="column" type="base64Binary" minOccurs="0" maxOccurs="unbounded"/&gt;
086 *     &lt;element name="filter" type="string" minOccurs="0" maxOccurs="1"&gt;&lt;/element&gt;
087 *   &lt;/sequence&gt;
088 *   &lt;attribute name="startRow" type="base64Binary"&gt;&lt;/attribute&gt;
089 *   &lt;attribute name="endRow" type="base64Binary"&gt;&lt;/attribute&gt;
090 *   &lt;attribute name="batch" type="int"&gt;&lt;/attribute&gt;
091 *   &lt;attribute name="caching" type="int"&gt;&lt;/attribute&gt;
092 *   &lt;attribute name="startTime" type="int"&gt;&lt;/attribute&gt;
093 *   &lt;attribute name="endTime" type="int"&gt;&lt;/attribute&gt;
094 *   &lt;attribute name="maxVersions" type="int"&gt;&lt;/attribute&gt;
095 * &lt;/complexType&gt;
096 * </pre>
097 */
098@XmlRootElement(name = "Scanner")
099@JsonInclude(JsonInclude.Include.NON_NULL)
100@InterfaceAudience.Private
101public class ScannerModel implements ProtobufMessageHandler, Serializable {
102
103  private static final long serialVersionUID = 1L;
104
105  private byte[] startRow = HConstants.EMPTY_START_ROW;
106  private byte[] endRow = HConstants.EMPTY_END_ROW;;
107  private List<byte[]> columns = new ArrayList<>();
108  private int batch = Integer.MAX_VALUE;
109  private long startTime = 0;
110  private long endTime = Long.MAX_VALUE;
111  private String filter = null;
112  private int maxVersions = Integer.MAX_VALUE;
113  private int caching = -1;
114  private List<String> labels = new ArrayList<>();
115  private boolean cacheBlocks = true;
116
117  /**
118   * Implement lazily-instantiated singleton as per recipe here:
119   * http://literatejava.com/jvm/fastest-threadsafe-singleton-jvm/
120   */
121  private static class JaxbJsonProviderHolder {
122    static final JacksonJaxbJsonProvider INSTANCE = new JacksonJaxbJsonProvider();
123  }
124
125  @XmlRootElement
126  static class FilterModel {
127
128    @XmlRootElement
129    static class ByteArrayComparableModel {
130      @XmlAttribute
131      public String type;
132      @XmlAttribute
133      public String value;
134      @XmlAttribute
135      public String op;
136
137      static enum ComparatorType {
138        BinaryComparator,
139        BinaryPrefixComparator,
140        BitComparator,
141        NullComparator,
142        RegexStringComparator,
143        SubstringComparator
144      }
145
146      public ByteArrayComparableModel() {
147      }
148
149      public ByteArrayComparableModel(ByteArrayComparable comparator) {
150        String typeName = comparator.getClass().getSimpleName();
151        ComparatorType type = ComparatorType.valueOf(typeName);
152        this.type = typeName;
153        switch (type) {
154          case BinaryComparator:
155          case BinaryPrefixComparator:
156            this.value = Bytes.toString(Base64.getEncoder().encode(comparator.getValue()));
157            break;
158          case BitComparator:
159            this.value = Bytes.toString(Base64.getEncoder().encode(comparator.getValue()));
160            this.op = ((BitComparator) comparator).getOperator().toString();
161            break;
162          case NullComparator:
163            break;
164          case RegexStringComparator:
165          case SubstringComparator:
166            this.value = Bytes.toString(comparator.getValue());
167            break;
168          default:
169            throw new RuntimeException("unhandled filter type: " + type);
170        }
171      }
172
173      public ByteArrayComparable build() {
174        ByteArrayComparable comparator;
175        switch (ComparatorType.valueOf(type)) {
176          case BinaryComparator:
177            comparator = new BinaryComparator(Base64.getDecoder().decode(value));
178            break;
179          case BinaryPrefixComparator:
180            comparator = new BinaryPrefixComparator(Base64.getDecoder().decode(value));
181            break;
182          case BitComparator:
183            comparator = new BitComparator(Base64.getDecoder().decode(value),
184              BitComparator.BitwiseOp.valueOf(op));
185            break;
186          case NullComparator:
187            comparator = new NullComparator();
188            break;
189          case RegexStringComparator:
190            comparator = new RegexStringComparator(value);
191            break;
192          case SubstringComparator:
193            comparator = new SubstringComparator(value);
194            break;
195          default:
196            throw new RuntimeException("unhandled comparator type: " + type);
197        }
198        return comparator;
199      }
200
201    }
202
203    // A grab bag of fields, would have been a union if this were C.
204    // These are null by default and will only be serialized if set (non null).
205    @XmlAttribute
206    public String type;
207    @XmlAttribute
208    public String op;
209    @XmlElement
210    ByteArrayComparableModel comparator;
211    @XmlAttribute
212    public String value;
213    @XmlElement
214    public List<FilterModel> filters;
215    @XmlAttribute
216    public Integer limit;
217    @XmlAttribute
218    public Integer offset;
219    @XmlAttribute
220    public String family;
221    @XmlAttribute
222    public String qualifier;
223    @XmlAttribute
224    public Boolean ifMissing;
225    @XmlAttribute
226    public Boolean latestVersion;
227    @XmlAttribute
228    public String minColumn;
229    @XmlAttribute
230    public Boolean minColumnInclusive;
231    @XmlAttribute
232    public String maxColumn;
233    @XmlAttribute
234    public Boolean maxColumnInclusive;
235    @XmlAttribute
236    public Boolean dropDependentColumn;
237    @XmlAttribute
238    public Float chance;
239    @XmlElement
240    public List<String> prefixes;
241    @XmlElement
242    private List<RowRange> ranges;
243    @XmlElement
244    public List<Long> timestamps;
245
246    static enum FilterType {
247      ColumnCountGetFilter,
248      ColumnPaginationFilter,
249      ColumnPrefixFilter,
250      ColumnRangeFilter,
251      DependentColumnFilter,
252      FamilyFilter,
253      FilterList,
254      FirstKeyOnlyFilter,
255      InclusiveStopFilter,
256      KeyOnlyFilter,
257      MultipleColumnPrefixFilter,
258      MultiRowRangeFilter,
259      PageFilter,
260      PrefixFilter,
261      QualifierFilter,
262      RandomRowFilter,
263      RowFilter,
264      SingleColumnValueExcludeFilter,
265      SingleColumnValueFilter,
266      SkipFilter,
267      TimestampsFilter,
268      ValueFilter,
269      WhileMatchFilter
270    }
271
272    public FilterModel() {
273    }
274
275    public FilterModel(Filter filter) {
276      String typeName = filter.getClass().getSimpleName();
277      FilterType type = FilterType.valueOf(typeName);
278      this.type = typeName;
279      switch (type) {
280        case ColumnCountGetFilter:
281          this.limit = ((ColumnCountGetFilter) filter).getLimit();
282          break;
283        case ColumnPaginationFilter:
284          this.limit = ((ColumnPaginationFilter) filter).getLimit();
285          this.offset = ((ColumnPaginationFilter) filter).getOffset();
286          break;
287        case ColumnPrefixFilter:
288          byte[] src = ((ColumnPrefixFilter) filter).getPrefix();
289          this.value = Bytes.toString(Base64.getEncoder().encode(src));
290          break;
291        case ColumnRangeFilter:
292          ColumnRangeFilter crf = (ColumnRangeFilter) filter;
293          this.minColumn = Bytes.toString(Base64.getEncoder().encode(crf.getMinColumn()));
294          this.minColumnInclusive = crf.getMinColumnInclusive();
295          this.maxColumn = Bytes.toString(Base64.getEncoder().encode(crf.getMaxColumn()));
296          this.maxColumnInclusive = crf.getMaxColumnInclusive();
297          break;
298        case DependentColumnFilter: {
299          DependentColumnFilter dcf = (DependentColumnFilter) filter;
300          this.family = Bytes.toString(Base64.getEncoder().encode(dcf.getFamily()));
301          byte[] qualifier = dcf.getQualifier();
302          if (qualifier != null) {
303            this.qualifier = Bytes.toString(Base64.getEncoder().encode(qualifier));
304          }
305          this.op = dcf.getOperator().toString();
306          this.comparator = new ByteArrayComparableModel(dcf.getComparator());
307          this.dropDependentColumn = dcf.dropDependentColumn();
308        }
309          break;
310        case FilterList:
311          this.op = ((FilterList) filter).getOperator().toString();
312          this.filters = new ArrayList<>();
313          for (Filter child : ((FilterList) filter).getFilters()) {
314            this.filters.add(new FilterModel(child));
315          }
316          break;
317        case FirstKeyOnlyFilter:
318        case KeyOnlyFilter:
319          break;
320        case InclusiveStopFilter:
321          this.value = Bytes
322            .toString(Base64.getEncoder().encode(((InclusiveStopFilter) filter).getStopRowKey()));
323          break;
324        case MultipleColumnPrefixFilter:
325          this.prefixes = new ArrayList<>();
326          for (byte[] prefix : ((MultipleColumnPrefixFilter) filter).getPrefix()) {
327            this.prefixes.add(Bytes.toString(Base64.getEncoder().encode(prefix)));
328          }
329          break;
330        case MultiRowRangeFilter:
331          this.ranges = new ArrayList<>();
332          for (RowRange range : ((MultiRowRangeFilter) filter).getRowRanges()) {
333            this.ranges.add(new RowRange(range.getStartRow(), range.isStartRowInclusive(),
334              range.getStopRow(), range.isStopRowInclusive()));
335          }
336          break;
337        case PageFilter:
338          this.value = Long.toString(((PageFilter) filter).getPageSize());
339          break;
340        case PrefixFilter:
341          this.value =
342            Bytes.toString(Base64.getEncoder().encode(((PrefixFilter) filter).getPrefix()));
343          break;
344        case FamilyFilter:
345        case QualifierFilter:
346        case RowFilter:
347        case ValueFilter:
348          this.op = ((CompareFilter) filter).getOperator().toString();
349          this.comparator = new ByteArrayComparableModel(((CompareFilter) filter).getComparator());
350          break;
351        case RandomRowFilter:
352          this.chance = ((RandomRowFilter) filter).getChance();
353          break;
354        case SingleColumnValueExcludeFilter:
355        case SingleColumnValueFilter: {
356          SingleColumnValueFilter scvf = (SingleColumnValueFilter) filter;
357          this.family = Bytes.toString(Base64.getEncoder().encode(scvf.getFamily()));
358          byte[] qualifier = scvf.getQualifier();
359          if (qualifier != null) {
360            this.qualifier = Bytes.toString(Base64.getEncoder().encode(qualifier));
361          }
362          this.op = scvf.getOperator().toString();
363          this.comparator = new ByteArrayComparableModel(scvf.getComparator());
364          if (scvf.getFilterIfMissing()) {
365            this.ifMissing = true;
366          }
367          if (scvf.getLatestVersionOnly()) {
368            this.latestVersion = true;
369          }
370        }
371          break;
372        case SkipFilter:
373          this.filters = new ArrayList<>();
374          this.filters.add(new FilterModel(((SkipFilter) filter).getFilter()));
375          break;
376        case TimestampsFilter:
377          this.timestamps = ((TimestampsFilter) filter).getTimestamps();
378          break;
379        case WhileMatchFilter:
380          this.filters = new ArrayList<>();
381          this.filters.add(new FilterModel(((WhileMatchFilter) filter).getFilter()));
382          break;
383        default:
384          throw new RuntimeException("unhandled filter type " + type);
385      }
386    }
387
388    public Filter build() {
389      Filter filter;
390      switch (FilterType.valueOf(type)) {
391        case ColumnCountGetFilter:
392          filter = new ColumnCountGetFilter(limit);
393          break;
394        case ColumnPaginationFilter:
395          filter = new ColumnPaginationFilter(limit, offset);
396          break;
397        case ColumnPrefixFilter:
398          filter = new ColumnPrefixFilter(Base64.getDecoder().decode(value));
399          break;
400        case ColumnRangeFilter:
401          filter = new ColumnRangeFilter(Base64.getDecoder().decode(minColumn), minColumnInclusive,
402            Base64.getDecoder().decode(maxColumn), maxColumnInclusive);
403          break;
404        case DependentColumnFilter:
405          filter = new DependentColumnFilter(Base64.getDecoder().decode(family),
406            qualifier != null ? Base64.getDecoder().decode(qualifier) : null, dropDependentColumn,
407            CompareOperator.valueOf(op), comparator.build());
408          break;
409        case FamilyFilter:
410          filter = new FamilyFilter(CompareOperator.valueOf(op), comparator.build());
411          break;
412        case FilterList: {
413          List<Filter> list = new ArrayList<>(filters.size());
414          for (FilterModel model : filters) {
415            list.add(model.build());
416          }
417          filter = new FilterList(FilterList.Operator.valueOf(op), list);
418        }
419          break;
420        case FirstKeyOnlyFilter:
421          filter = new FirstKeyOnlyFilter();
422          break;
423        case InclusiveStopFilter:
424          filter = new InclusiveStopFilter(Base64.getDecoder().decode(value));
425          break;
426        case KeyOnlyFilter:
427          filter = new KeyOnlyFilter();
428          break;
429        case MultipleColumnPrefixFilter: {
430          byte[][] values = new byte[prefixes.size()][];
431          for (int i = 0; i < prefixes.size(); i++) {
432            values[i] = Base64.getDecoder().decode(prefixes.get(i));
433          }
434          filter = new MultipleColumnPrefixFilter(values);
435        }
436          break;
437        case MultiRowRangeFilter: {
438          filter = new MultiRowRangeFilter(ranges);
439        }
440          break;
441        case PageFilter:
442          filter = new PageFilter(Long.parseLong(value));
443          break;
444        case PrefixFilter:
445          filter = new PrefixFilter(Base64.getDecoder().decode(value));
446          break;
447        case QualifierFilter:
448          filter = new QualifierFilter(CompareOperator.valueOf(op), comparator.build());
449          break;
450        case RandomRowFilter:
451          filter = new RandomRowFilter(chance);
452          break;
453        case RowFilter:
454          filter = new RowFilter(CompareOperator.valueOf(op), comparator.build());
455          break;
456        case SingleColumnValueFilter:
457          filter = new SingleColumnValueFilter(Base64.getDecoder().decode(family),
458            qualifier != null ? Base64.getDecoder().decode(qualifier) : null,
459            CompareOperator.valueOf(op), comparator.build());
460          if (ifMissing != null) {
461            ((SingleColumnValueFilter) filter).setFilterIfMissing(ifMissing);
462          }
463          if (latestVersion != null) {
464            ((SingleColumnValueFilter) filter).setLatestVersionOnly(latestVersion);
465          }
466          break;
467        case SingleColumnValueExcludeFilter:
468          filter = new SingleColumnValueExcludeFilter(Base64.getDecoder().decode(family),
469            qualifier != null ? Base64.getDecoder().decode(qualifier) : null,
470            CompareOperator.valueOf(op), comparator.build());
471          if (ifMissing != null) {
472            ((SingleColumnValueExcludeFilter) filter).setFilterIfMissing(ifMissing);
473          }
474          if (latestVersion != null) {
475            ((SingleColumnValueExcludeFilter) filter).setLatestVersionOnly(latestVersion);
476          }
477          break;
478        case SkipFilter:
479          filter = new SkipFilter(filters.get(0).build());
480          break;
481        case TimestampsFilter:
482          filter = new TimestampsFilter(timestamps);
483          break;
484        case ValueFilter:
485          filter = new ValueFilter(CompareOperator.valueOf(op), comparator.build());
486          break;
487        case WhileMatchFilter:
488          filter = new WhileMatchFilter(filters.get(0).build());
489          break;
490        default:
491          throw new RuntimeException("unhandled filter type: " + type);
492      }
493      return filter;
494    }
495
496  }
497
498  /**
499   * Get the <code>JacksonJaxbJsonProvider</code> instance;
500   * @return A <code>JacksonJaxbJsonProvider</code>.
501   */
502  private static JacksonJaxbJsonProvider getJasonProvider() {
503    return JaxbJsonProviderHolder.INSTANCE;
504  }
505
506  /**
507   * @param s the JSON representation of the filter
508   * @return the filter
509   */
510  public static Filter buildFilter(String s) throws Exception {
511    FilterModel model =
512      getJasonProvider().locateMapper(FilterModel.class, MediaType.APPLICATION_JSON_TYPE)
513        .readValue(s, FilterModel.class);
514    return model.build();
515  }
516
517  /**
518   * @param filter the filter
519   * @return the JSON representation of the filter
520   */
521  public static String stringifyFilter(final Filter filter) throws Exception {
522    return getJasonProvider().locateMapper(FilterModel.class, MediaType.APPLICATION_JSON_TYPE)
523      .writeValueAsString(new FilterModel(filter));
524  }
525
526  private static final byte[] COLUMN_DIVIDER = Bytes.toBytes(":");
527
528  /**
529   * @param scan the scan specification
530   */
531  public static ScannerModel fromScan(Scan scan) throws Exception {
532    ScannerModel model = new ScannerModel();
533    model.setStartRow(scan.getStartRow());
534    model.setEndRow(scan.getStopRow());
535    Map<byte[], NavigableSet<byte[]>> families = scan.getFamilyMap();
536    if (families != null) {
537      for (Map.Entry<byte[], NavigableSet<byte[]>> entry : families.entrySet()) {
538        if (entry.getValue() != null) {
539          for (byte[] qualifier : entry.getValue()) {
540            model.addColumn(Bytes.add(entry.getKey(), COLUMN_DIVIDER, qualifier));
541          }
542        } else {
543          model.addColumn(entry.getKey());
544        }
545      }
546    }
547    model.setStartTime(scan.getTimeRange().getMin());
548    model.setEndTime(scan.getTimeRange().getMax());
549    int caching = scan.getCaching();
550    if (caching > 0) {
551      model.setCaching(caching);
552    }
553    int batch = scan.getBatch();
554    if (batch > 0) {
555      model.setBatch(batch);
556    }
557    int maxVersions = scan.getMaxVersions();
558    if (maxVersions > 0) {
559      model.setMaxVersions(maxVersions);
560    }
561    Filter filter = scan.getFilter();
562    if (filter != null) {
563      model.setFilter(stringifyFilter(filter));
564    }
565    // Add the visbility labels if found in the attributes
566    Authorizations authorizations = scan.getAuthorizations();
567    if (authorizations != null) {
568      List<String> labels = authorizations.getLabels();
569      for (String label : labels) {
570        model.addLabel(label);
571      }
572    }
573    return model;
574  }
575
576  /**
577   * Default constructor
578   */
579  public ScannerModel() {
580  }
581
582  /**
583   * Constructor
584   * @param startRow    the start key of the row-range
585   * @param endRow      the end key of the row-range
586   * @param columns     the columns to scan
587   * @param batch       the number of values to return in batch
588   * @param caching     the number of rows that the scanner will fetch at once
589   * @param endTime     the upper bound on timestamps of values of interest
590   * @param maxVersions the maximum number of versions to return
591   * @param filter      a filter specification (values with timestamps later than this are excluded)
592   */
593  public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns, int batch, int caching,
594    long endTime, int maxVersions, String filter) {
595    super();
596    this.startRow = startRow;
597    this.endRow = endRow;
598    this.columns = columns;
599    this.batch = batch;
600    this.caching = caching;
601    this.endTime = endTime;
602    this.maxVersions = maxVersions;
603    this.filter = filter;
604  }
605
606  /**
607   * Constructor
608   * @param startRow  the start key of the row-range
609   * @param endRow    the end key of the row-range
610   * @param columns   the columns to scan
611   * @param batch     the number of values to return in batch
612   * @param caching   the number of rows that the scanner will fetch at once
613   * @param startTime the lower bound on timestamps of values of interest (values with timestamps
614   *                  earlier than this are excluded)
615   * @param endTime   the upper bound on timestamps of values of interest (values with timestamps
616   *                  later than this are excluded)
617   * @param filter    a filter specification
618   */
619  public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns, int batch, int caching,
620    long startTime, long endTime, String filter) {
621    super();
622    this.startRow = startRow;
623    this.endRow = endRow;
624    this.columns = columns;
625    this.batch = batch;
626    this.caching = caching;
627    this.startTime = startTime;
628    this.endTime = endTime;
629    this.filter = filter;
630  }
631
632  /**
633   * Add a column to the column set
634   * @param column the column name, as &lt;column&gt;(:&lt;qualifier&gt;)?
635   */
636  public void addColumn(byte[] column) {
637    columns.add(column);
638  }
639
640  /**
641   * Add a visibility label to the scan
642   */
643  public void addLabel(String label) {
644    labels.add(label);
645  }
646
647  /** Returns true if a start row was specified */
648  public boolean hasStartRow() {
649    return !Bytes.equals(startRow, HConstants.EMPTY_START_ROW);
650  }
651
652  /** Returns start row */
653  @XmlAttribute
654  public byte[] getStartRow() {
655    return startRow;
656  }
657
658  /** Returns true if an end row was specified */
659  public boolean hasEndRow() {
660    return !Bytes.equals(endRow, HConstants.EMPTY_END_ROW);
661  }
662
663  /** Returns end row */
664  @XmlAttribute
665  public byte[] getEndRow() {
666    return endRow;
667  }
668
669  /** Returns list of columns of interest in column:qualifier format, or empty for all */
670  @XmlElement(name = "column")
671  public List<byte[]> getColumns() {
672    return columns;
673  }
674
675  @XmlElement(name = "labels")
676  public List<String> getLabels() {
677    return labels;
678  }
679
680  /** Returns the number of cells to return in batch */
681  @XmlAttribute
682  public int getBatch() {
683    return batch;
684  }
685
686  /** Returns the number of rows that the scanner to fetch at once */
687  @XmlAttribute
688  public int getCaching() {
689    return caching;
690  }
691
692  /** Returns true if HFile blocks should be cached on the servers for this scan, false otherwise */
693  @XmlAttribute
694  public boolean getCacheBlocks() {
695    return cacheBlocks;
696  }
697
698  /** Returns the lower bound on timestamps of items of interest */
699  @XmlAttribute
700  public long getStartTime() {
701    return startTime;
702  }
703
704  /** Returns the upper bound on timestamps of items of interest */
705  @XmlAttribute
706  public long getEndTime() {
707    return endTime;
708  }
709
710  /** Returns maximum number of versions to return */
711  @XmlAttribute
712  public int getMaxVersions() {
713    return maxVersions;
714  }
715
716  /** Returns the filter specification */
717  @XmlElement
718  public String getFilter() {
719    return filter;
720  }
721
722  /**
723   * @param startRow start row
724   */
725  public void setStartRow(byte[] startRow) {
726    this.startRow = startRow;
727  }
728
729  /**
730   * @param endRow end row
731   */
732  public void setEndRow(byte[] endRow) {
733    this.endRow = endRow;
734  }
735
736  /**
737   * @param columns list of columns of interest in column:qualifier format, or empty for all
738   */
739  public void setColumns(List<byte[]> columns) {
740    this.columns = columns;
741  }
742
743  /**
744   * @param batch the number of cells to return in batch
745   */
746  public void setBatch(int batch) {
747    this.batch = batch;
748  }
749
750  /**
751   * @param caching the number of rows to fetch at once
752   */
753  public void setCaching(int caching) {
754    this.caching = caching;
755  }
756
757  /**
758   * @param value true if HFile blocks should be cached on the servers for this scan, false
759   *              otherwise
760   */
761  public void setCacheBlocks(boolean value) {
762    this.cacheBlocks = value;
763  }
764
765  /**
766   * @param maxVersions maximum number of versions to return
767   */
768  public void setMaxVersions(int maxVersions) {
769    this.maxVersions = maxVersions;
770  }
771
772  /**
773   * @param startTime the lower bound on timestamps of values of interest
774   */
775  public void setStartTime(long startTime) {
776    this.startTime = startTime;
777  }
778
779  /**
780   * @param endTime the upper bound on timestamps of values of interest
781   */
782  public void setEndTime(long endTime) {
783    this.endTime = endTime;
784  }
785
786  /**
787   * @param filter the filter specification
788   */
789  public void setFilter(String filter) {
790    this.filter = filter;
791  }
792
793  @Override
794  public byte[] createProtobufOutput() {
795    Scanner.Builder builder = Scanner.newBuilder();
796    if (!Bytes.equals(startRow, HConstants.EMPTY_START_ROW)) {
797      builder.setStartRow(ByteStringer.wrap(startRow));
798    }
799    if (!Bytes.equals(endRow, HConstants.EMPTY_START_ROW)) {
800      builder.setEndRow(ByteStringer.wrap(endRow));
801    }
802    for (byte[] column : columns) {
803      builder.addColumns(ByteStringer.wrap(column));
804    }
805    if (startTime != 0) {
806      builder.setStartTime(startTime);
807    }
808    if (endTime != 0) {
809      builder.setEndTime(endTime);
810    }
811    builder.setBatch(getBatch());
812    if (caching > 0) {
813      builder.setCaching(caching);
814    }
815    builder.setMaxVersions(maxVersions);
816    if (filter != null) {
817      builder.setFilter(filter);
818    }
819    if (labels != null && labels.size() > 0) {
820      for (String label : labels)
821        builder.addLabels(label);
822    }
823    builder.setCacheBlocks(cacheBlocks);
824    return builder.build().toByteArray();
825  }
826
827  @Override
828  public ProtobufMessageHandler getObjectFromMessage(byte[] message) throws IOException {
829    Scanner.Builder builder = Scanner.newBuilder();
830    ProtobufUtil.mergeFrom(builder, message);
831    if (builder.hasStartRow()) {
832      startRow = builder.getStartRow().toByteArray();
833    }
834    if (builder.hasEndRow()) {
835      endRow = builder.getEndRow().toByteArray();
836    }
837    for (ByteString column : builder.getColumnsList()) {
838      addColumn(column.toByteArray());
839    }
840    if (builder.hasBatch()) {
841      batch = builder.getBatch();
842    }
843    if (builder.hasCaching()) {
844      caching = builder.getCaching();
845    }
846    if (builder.hasStartTime()) {
847      startTime = builder.getStartTime();
848    }
849    if (builder.hasEndTime()) {
850      endTime = builder.getEndTime();
851    }
852    if (builder.hasMaxVersions()) {
853      maxVersions = builder.getMaxVersions();
854    }
855    if (builder.hasFilter()) {
856      filter = builder.getFilter();
857    }
858    if (builder.getLabelsList() != null) {
859      List<String> labels = builder.getLabelsList();
860      for (String label : labels) {
861        addLabel(label);
862      }
863    }
864    if (builder.hasCacheBlocks()) {
865      this.cacheBlocks = builder.getCacheBlocks();
866    }
867    return this;
868  }
869
870}