View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.rest.model;
21  
22  import java.io.IOException;
23  import java.io.Serializable;
24  import java.io.StringReader;
25  import java.io.StringWriter;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.NavigableSet;
30  
31  import javax.xml.bind.annotation.XmlAttribute;
32  import javax.xml.bind.annotation.XmlElement;
33  import javax.xml.bind.annotation.XmlRootElement;
34  
35  import com.google.protobuf.HBaseZeroCopyByteString;
36  import org.apache.hadoop.classification.InterfaceAudience;
37  import org.apache.hadoop.hbase.HConstants;
38  import org.apache.hadoop.hbase.client.Scan;
39  import org.apache.hadoop.hbase.filter.BinaryComparator;
40  import org.apache.hadoop.hbase.filter.BinaryPrefixComparator;
41  import org.apache.hadoop.hbase.filter.BitComparator;
42  import org.apache.hadoop.hbase.filter.ByteArrayComparable;
43  import org.apache.hadoop.hbase.filter.ColumnCountGetFilter;
44  import org.apache.hadoop.hbase.filter.ColumnPaginationFilter;
45  import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
46  import org.apache.hadoop.hbase.filter.ColumnRangeFilter;
47  import org.apache.hadoop.hbase.filter.CompareFilter;
48  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
49  import org.apache.hadoop.hbase.filter.DependentColumnFilter;
50  import org.apache.hadoop.hbase.filter.FamilyFilter;
51  import org.apache.hadoop.hbase.filter.Filter;
52  import org.apache.hadoop.hbase.filter.FilterList;
53  import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
54  import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
55  import org.apache.hadoop.hbase.filter.KeyOnlyFilter;
56  import org.apache.hadoop.hbase.filter.MultipleColumnPrefixFilter;
57  import org.apache.hadoop.hbase.filter.NullComparator;
58  import org.apache.hadoop.hbase.filter.PageFilter;
59  import org.apache.hadoop.hbase.filter.PrefixFilter;
60  import org.apache.hadoop.hbase.filter.QualifierFilter;
61  import org.apache.hadoop.hbase.filter.RandomRowFilter;
62  import org.apache.hadoop.hbase.filter.RegexStringComparator;
63  import org.apache.hadoop.hbase.filter.RowFilter;
64  import org.apache.hadoop.hbase.filter.SingleColumnValueExcludeFilter;
65  import org.apache.hadoop.hbase.filter.SingleColumnValueFilter;
66  import org.apache.hadoop.hbase.filter.SkipFilter;
67  import org.apache.hadoop.hbase.filter.SubstringComparator;
68  import org.apache.hadoop.hbase.filter.TimestampsFilter;
69  import org.apache.hadoop.hbase.filter.ValueFilter;
70  import org.apache.hadoop.hbase.filter.WhileMatchFilter;
71  import org.apache.hadoop.hbase.rest.ProtobufMessageHandler;
72  import org.apache.hadoop.hbase.rest.protobuf.generated.ScannerMessage.Scanner;
73  import org.apache.hadoop.hbase.security.visibility.Authorizations;
74  import org.apache.hadoop.hbase.util.Base64;
75  import org.apache.hadoop.hbase.util.Bytes;
76  
77  import com.google.protobuf.ByteString;
78  import com.sun.jersey.api.json.JSONConfiguration;
79  import com.sun.jersey.api.json.JSONJAXBContext;
80  import com.sun.jersey.api.json.JSONMarshaller;
81  import com.sun.jersey.api.json.JSONUnmarshaller;
82  
83  /**
84   * A representation of Scanner parameters.
85   * 
86   * <pre>
87   * &lt;complexType name="Scanner"&gt;
88   *   &lt;sequence>
89   *     &lt;element name="column" type="base64Binary" minOccurs="0" maxOccurs="unbounded"/&gt;
90   *     &lt;element name="filter" type="string" minOccurs="0" maxOccurs="1"&gt;&lt;/element&gt;
91   *   &lt;/sequence&gt;
92   *   &lt;attribute name="startRow" type="base64Binary"&gt;&lt;/attribute&gt;
93   *   &lt;attribute name="endRow" type="base64Binary"&gt;&lt;/attribute&gt;
94   *   &lt;attribute name="batch" type="int"&gt;&lt;/attribute&gt;
95   *   &lt;attribute name="caching" type="int"&gt;&lt;/attribute&gt;
96   *   &lt;attribute name="startTime" type="int"&gt;&lt;/attribute&gt;
97   *   &lt;attribute name="endTime" type="int"&gt;&lt;/attribute&gt;
98   *   &lt;attribute name="maxVersions" type="int"&gt;&lt;/attribute&gt;
99   * &lt;/complexType&gt;
100  * </pre>
101  */
102 @XmlRootElement(name="Scanner")
103 @InterfaceAudience.Private
104 public class ScannerModel implements ProtobufMessageHandler, Serializable {
105 
106   private static final long serialVersionUID = 1L;
107 
108   private byte[] startRow = HConstants.EMPTY_START_ROW;
109   private byte[] endRow = HConstants.EMPTY_END_ROW;;
110   private List<byte[]> columns = new ArrayList<byte[]>();
111   private int batch = Integer.MAX_VALUE;
112   private long startTime = 0;
113   private long endTime = Long.MAX_VALUE;
114   private String filter = null;
115   private int maxVersions = Integer.MAX_VALUE;
116   private int caching = -1;
117   private List<String> labels = new ArrayList<String>();
118   
119   @XmlRootElement
120   static class FilterModel {
121     
122     @XmlRootElement
123     static class ByteArrayComparableModel {
124       @XmlAttribute public String type;
125       @XmlAttribute public String value;
126       @XmlAttribute public String op;
127 
128       static enum ComparatorType {
129         BinaryComparator,
130         BinaryPrefixComparator,
131         BitComparator,
132         NullComparator,
133         RegexStringComparator,
134         SubstringComparator    
135       }
136 
137       public ByteArrayComparableModel() { }
138 
139       public ByteArrayComparableModel(
140           ByteArrayComparable comparator) {
141         String typeName = comparator.getClass().getSimpleName();
142         ComparatorType type = ComparatorType.valueOf(typeName);
143         this.type = typeName;
144         switch (type) {
145           case BinaryComparator:
146           case BinaryPrefixComparator:
147             this.value = Base64.encodeBytes(comparator.getValue());
148             break;
149           case BitComparator:
150             this.value = Base64.encodeBytes(comparator.getValue());
151             this.op = ((BitComparator)comparator).getOperator().toString();
152             break;
153           case NullComparator:
154             break;
155           case RegexStringComparator:
156           case SubstringComparator:
157             this.value = Bytes.toString(comparator.getValue());
158             break;
159           default:
160             throw new RuntimeException("unhandled filter type: " + type);
161         }
162       }
163 
164       public ByteArrayComparable build() {
165         ByteArrayComparable comparator;
166         switch (ComparatorType.valueOf(type)) {
167           case BinaryComparator:
168             comparator = new BinaryComparator(Base64.decode(value));
169             break;
170           case BinaryPrefixComparator:
171             comparator = new BinaryPrefixComparator(Base64.decode(value));
172             break;
173           case BitComparator:
174             comparator = new BitComparator(Base64.decode(value),
175                 BitComparator.BitwiseOp.valueOf(op));
176             break;
177           case NullComparator:
178             comparator = new NullComparator();
179             break;
180           case RegexStringComparator:
181             comparator = new RegexStringComparator(value);
182             break;
183           case SubstringComparator:
184             comparator = new SubstringComparator(value);
185             break;
186           default:
187             throw new RuntimeException("unhandled comparator type: " + type);
188         }
189         return comparator;
190       }
191 
192     }
193 
194     // A grab bag of fields, would have been a union if this were C.
195     // These are null by default and will only be serialized if set (non null).
196     @XmlAttribute public String type;
197     @XmlAttribute public String op;
198     @XmlElement ByteArrayComparableModel comparator;
199     @XmlAttribute public String value;
200     @XmlElement public List<FilterModel> filters;
201     @XmlAttribute public Integer limit;
202     @XmlAttribute public Integer offset;
203     @XmlAttribute public String family;
204     @XmlAttribute public String qualifier;
205     @XmlAttribute public Boolean ifMissing;
206     @XmlAttribute public Boolean latestVersion;
207     @XmlAttribute public String minColumn;
208     @XmlAttribute public Boolean minColumnInclusive;
209     @XmlAttribute public String maxColumn;
210     @XmlAttribute public Boolean maxColumnInclusive;
211     @XmlAttribute public Boolean dropDependentColumn;
212     @XmlAttribute public Float chance;
213     @XmlElement public List<String> prefixes;
214     @XmlElement public List<Long> timestamps;
215 
216     static enum FilterType {
217       ColumnCountGetFilter,
218       ColumnPaginationFilter,
219       ColumnPrefixFilter,
220       ColumnRangeFilter,
221       DependentColumnFilter,
222       FamilyFilter,
223       FilterList,
224       FirstKeyOnlyFilter,
225       InclusiveStopFilter,
226       KeyOnlyFilter,
227       MultipleColumnPrefixFilter,
228       PageFilter,
229       PrefixFilter,
230       QualifierFilter,
231       RandomRowFilter,
232       RowFilter,
233       SingleColumnValueExcludeFilter,
234       SingleColumnValueFilter,
235       SkipFilter,
236       TimestampsFilter,
237       ValueFilter,
238       WhileMatchFilter    
239     }
240 
241     public FilterModel() { }
242     
243     public FilterModel(Filter filter) { 
244       String typeName = filter.getClass().getSimpleName();
245       FilterType type = FilterType.valueOf(typeName);
246       this.type = typeName;
247       switch (type) {
248         case ColumnCountGetFilter:
249           this.limit = ((ColumnCountGetFilter)filter).getLimit();
250           break;
251         case ColumnPaginationFilter:
252           this.limit = ((ColumnPaginationFilter)filter).getLimit();
253           this.offset = ((ColumnPaginationFilter)filter).getOffset();
254           break;
255         case ColumnPrefixFilter:
256           this.value = Base64.encodeBytes(((ColumnPrefixFilter)filter).getPrefix());
257           break;
258         case ColumnRangeFilter:
259           this.minColumn = Base64.encodeBytes(((ColumnRangeFilter)filter).getMinColumn());
260           this.minColumnInclusive = ((ColumnRangeFilter)filter).getMinColumnInclusive();
261           this.maxColumn = Base64.encodeBytes(((ColumnRangeFilter)filter).getMaxColumn());
262           this.maxColumnInclusive = ((ColumnRangeFilter)filter).getMaxColumnInclusive();
263           break;
264         case DependentColumnFilter: {
265           DependentColumnFilter dcf = (DependentColumnFilter)filter;
266           this.family = Base64.encodeBytes(dcf.getFamily());
267           byte[] qualifier = dcf.getQualifier();
268           if (qualifier != null) {
269             this.qualifier = Base64.encodeBytes(qualifier);
270           }
271           this.op = dcf.getOperator().toString();
272           this.comparator = new ByteArrayComparableModel(dcf.getComparator());
273           this.dropDependentColumn = dcf.dropDependentColumn();
274         } break;
275         case FilterList:
276           this.op = ((FilterList)filter).getOperator().toString();
277           this.filters = new ArrayList<FilterModel>();
278           for (Filter child: ((FilterList)filter).getFilters()) {
279             this.filters.add(new FilterModel(child));
280           }
281           break;
282         case FirstKeyOnlyFilter:
283         case KeyOnlyFilter:
284           break;
285         case InclusiveStopFilter:
286           this.value = 
287             Base64.encodeBytes(((InclusiveStopFilter)filter).getStopRowKey());
288           break;
289         case MultipleColumnPrefixFilter:
290           this.prefixes = new ArrayList<String>();
291           for (byte[] prefix: ((MultipleColumnPrefixFilter)filter).getPrefix()) {
292             this.prefixes.add(Base64.encodeBytes(prefix));
293           }
294           break;
295         case PageFilter:
296           this.value = Long.toString(((PageFilter)filter).getPageSize());
297           break;
298         case PrefixFilter:
299           this.value = Base64.encodeBytes(((PrefixFilter)filter).getPrefix());
300           break;
301         case FamilyFilter:
302         case QualifierFilter:
303         case RowFilter:
304         case ValueFilter:
305           this.op = ((CompareFilter)filter).getOperator().toString();
306           this.comparator = 
307             new ByteArrayComparableModel(
308               ((CompareFilter)filter).getComparator());
309           break;
310         case RandomRowFilter:
311           this.chance = ((RandomRowFilter)filter).getChance();
312           break;
313         case SingleColumnValueExcludeFilter:
314         case SingleColumnValueFilter: {
315           SingleColumnValueFilter scvf = (SingleColumnValueFilter) filter;
316           this.family = Base64.encodeBytes(scvf.getFamily());
317           byte[] qualifier = scvf.getQualifier();
318           if (qualifier != null) {
319             this.qualifier = Base64.encodeBytes(qualifier);
320           }
321           this.op = scvf.getOperator().toString();
322           this.comparator = 
323             new ByteArrayComparableModel(scvf.getComparator());
324           if (scvf.getFilterIfMissing()) {
325             this.ifMissing = true;
326           }
327           if (scvf.getLatestVersionOnly()) {
328             this.latestVersion = true;
329           }
330         } break;
331         case SkipFilter:
332           this.filters = new ArrayList<FilterModel>();
333           this.filters.add(new FilterModel(((SkipFilter)filter).getFilter()));
334           break;
335         case TimestampsFilter:
336           this.timestamps = ((TimestampsFilter)filter).getTimestamps();
337           break;
338         case WhileMatchFilter:
339           this.filters = new ArrayList<FilterModel>();
340           this.filters.add(
341             new FilterModel(((WhileMatchFilter)filter).getFilter()));
342           break;
343         default:
344           throw new RuntimeException("unhandled filter type " + type);
345       }
346     }
347 
348     public Filter build() {
349       Filter filter;
350       switch (FilterType.valueOf(type)) {
351       case ColumnCountGetFilter:
352         filter = new ColumnCountGetFilter(limit);
353         break;
354       case ColumnPaginationFilter:
355         filter = new ColumnPaginationFilter(limit, offset);
356         break;
357       case ColumnPrefixFilter:
358         filter = new ColumnPrefixFilter(Base64.decode(value));
359         break;
360       case ColumnRangeFilter:
361         filter = new ColumnRangeFilter(Base64.decode(minColumn),
362             minColumnInclusive, Base64.decode(maxColumn),
363             maxColumnInclusive);
364         break;
365       case DependentColumnFilter:
366         filter = new DependentColumnFilter(Base64.decode(family),
367             qualifier != null ? Base64.decode(qualifier) : null,
368             dropDependentColumn, CompareOp.valueOf(op), comparator.build());
369         break;
370       case FamilyFilter:
371         filter = new FamilyFilter(CompareOp.valueOf(op), comparator.build());
372         break;
373       case FilterList: {
374         List<Filter> list = new ArrayList<Filter>();
375         for (FilterModel model: filters) {
376           list.add(model.build());
377         }
378         filter = new FilterList(FilterList.Operator.valueOf(op), list);
379       } break;
380       case FirstKeyOnlyFilter:
381         filter = new FirstKeyOnlyFilter();
382         break;
383       case InclusiveStopFilter:
384         filter = new InclusiveStopFilter(Base64.decode(value));
385         break;
386       case KeyOnlyFilter:
387         filter = new KeyOnlyFilter();
388         break;
389       case MultipleColumnPrefixFilter: {
390         byte[][] values = new byte[prefixes.size()][];
391         for (int i = 0; i < prefixes.size(); i++) {
392           values[i] = Base64.decode(prefixes.get(i));
393         }
394         filter = new MultipleColumnPrefixFilter(values);
395       } break;
396       case PageFilter:
397         filter = new PageFilter(Long.valueOf(value));
398         break;
399       case PrefixFilter:
400         filter = new PrefixFilter(Base64.decode(value));
401         break;
402       case QualifierFilter:
403         filter = new QualifierFilter(CompareOp.valueOf(op), comparator.build());
404         break;
405       case RandomRowFilter:
406         filter = new RandomRowFilter(chance);
407         break;
408       case RowFilter:
409         filter = new RowFilter(CompareOp.valueOf(op), comparator.build());
410         break;
411       case SingleColumnValueFilter:
412         filter = new SingleColumnValueFilter(Base64.decode(family),
413           qualifier != null ? Base64.decode(qualifier) : null,
414           CompareOp.valueOf(op), comparator.build());
415         if (ifMissing != null) {
416           ((SingleColumnValueFilter)filter).setFilterIfMissing(ifMissing);
417         }
418         if (latestVersion != null) {
419           ((SingleColumnValueFilter)filter).setLatestVersionOnly(latestVersion);
420         }
421         break;
422       case SingleColumnValueExcludeFilter:
423         filter = new SingleColumnValueExcludeFilter(Base64.decode(family),
424           qualifier != null ? Base64.decode(qualifier) : null,
425           CompareOp.valueOf(op), comparator.build());
426         if (ifMissing != null) {
427           ((SingleColumnValueExcludeFilter)filter).setFilterIfMissing(ifMissing);
428         }
429         if (latestVersion != null) {
430           ((SingleColumnValueExcludeFilter)filter).setLatestVersionOnly(latestVersion);
431         }
432         break;
433       case SkipFilter:
434         filter = new SkipFilter(filters.get(0).build());
435         break;
436       case TimestampsFilter:
437         filter = new TimestampsFilter(timestamps);
438         break;
439       case ValueFilter:
440         filter = new ValueFilter(CompareOp.valueOf(op), comparator.build());
441         break;
442       case WhileMatchFilter:
443         filter = new WhileMatchFilter(filters.get(0).build());
444         break;
445       default:
446         throw new RuntimeException("unhandled filter type: " + type);
447       }
448       return filter;
449     }
450 
451   }
452 
453   /**
454    * @param s the JSON representation of the filter
455    * @return the filter
456    * @throws Exception
457    */
458   public static Filter buildFilter(String s) throws Exception {
459     JSONJAXBContext context =
460       new JSONJAXBContext(JSONConfiguration.natural().build(),
461         FilterModel.class);
462     JSONUnmarshaller unmarshaller = context.createJSONUnmarshaller();
463     FilterModel model = unmarshaller.unmarshalFromJSON(new StringReader(s),
464       FilterModel.class);
465     return model.build();
466   }
467 
468   /**
469    * @param filter the filter
470    * @return the JSON representation of the filter
471    * @throws Exception 
472    */
473   public static String stringifyFilter(final Filter filter) throws Exception {
474     JSONJAXBContext context =
475       new JSONJAXBContext(JSONConfiguration.natural().build(),
476         FilterModel.class);
477     JSONMarshaller marshaller = context.createJSONMarshaller();
478     StringWriter writer = new StringWriter();
479     marshaller.marshallToJSON(new FilterModel(filter), writer);
480     return writer.toString();
481   }
482 
483   private static final byte[] COLUMN_DIVIDER = Bytes.toBytes(":");
484 
485   /**
486    * @param scan the scan specification
487    * @throws Exception 
488    */
489   public static ScannerModel fromScan(Scan scan) throws Exception {
490     ScannerModel model = new ScannerModel();
491     model.setStartRow(scan.getStartRow());
492     model.setEndRow(scan.getStopRow());
493     Map<byte [], NavigableSet<byte []>> families = scan.getFamilyMap();
494     if (families != null) {
495       for (Map.Entry<byte [], NavigableSet<byte []>> entry : families.entrySet()) {
496         if (entry.getValue() != null) {
497           for (byte[] qualifier: entry.getValue()) {
498             model.addColumn(Bytes.add(entry.getKey(), COLUMN_DIVIDER, qualifier));
499           }
500         } else {
501           model.addColumn(entry.getKey());
502         }
503       }
504     }
505     model.setStartTime(scan.getTimeRange().getMin());
506     model.setEndTime(scan.getTimeRange().getMax());
507     int caching = scan.getCaching();
508     if (caching > 0) {
509       model.setCaching(caching);
510     }
511     int batch = scan.getBatch();
512     if (batch > 0) {
513       model.setBatch(batch);
514     }
515     int maxVersions = scan.getMaxVersions();
516     if (maxVersions > 0) {
517       model.setMaxVersions(maxVersions);
518     }
519     Filter filter = scan.getFilter();
520     if (filter != null) {
521       model.setFilter(stringifyFilter(filter));
522     }
523     // Add the visbility labels if found in the attributes
524     Authorizations authorizations = scan.getAuthorizations();
525     if (authorizations != null) {
526       List<String> labels = authorizations.getLabels();
527       for (String label : labels) {
528         model.addLabel(label);
529       }
530     }
531     return model;
532   }
533 
534   /**
535    * Default constructor
536    */
537   public ScannerModel() {}
538 
539   /**
540    * Constructor
541    * @param startRow the start key of the row-range
542    * @param endRow the end key of the row-range
543    * @param columns the columns to scan
544    * @param batch the number of values to return in batch
545    * @param caching the number of rows that the scanner will fetch at once
546    * @param endTime the upper bound on timestamps of values of interest
547    * @param maxVersions the maximum number of versions to return
548    * @param filter a filter specification
549    * (values with timestamps later than this are excluded)
550    */
551   public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns,
552       int batch, int caching, long endTime, int maxVersions, String filter) {
553     super();
554     this.startRow = startRow;
555     this.endRow = endRow;
556     this.columns = columns;
557     this.batch = batch;
558     this.caching = caching;
559     this.endTime = endTime;
560     this.maxVersions = maxVersions;
561     this.filter = filter;
562   }
563 
564   /**
565    * Constructor 
566    * @param startRow the start key of the row-range
567    * @param endRow the end key of the row-range
568    * @param columns the columns to scan
569    * @param batch the number of values to return in batch
570    * @param caching the number of rows that the scanner will fetch at once
571    * @param startTime the lower bound on timestamps of values of interest
572    * (values with timestamps earlier than this are excluded)
573    * @param endTime the upper bound on timestamps of values of interest
574    * (values with timestamps later than this are excluded)
575    * @param filter a filter specification
576    */
577   public ScannerModel(byte[] startRow, byte[] endRow, List<byte[]> columns,
578       int batch, int caching, long startTime, long endTime, String filter) {
579     super();
580     this.startRow = startRow;
581     this.endRow = endRow;
582     this.columns = columns;
583     this.batch = batch;
584     this.caching = caching;
585     this.startTime = startTime;
586     this.endTime = endTime;
587     this.filter = filter;
588   }
589 
590   /**
591    * Add a column to the column set
592    * @param column the column name, as &lt;column&gt;(:&lt;qualifier&gt;)?
593    */
594   public void addColumn(byte[] column) {
595     columns.add(column);
596   }
597   
598   /**
599    * Add a visibility label to the scan
600    */
601   public void addLabel(String label) {
602     labels.add(label);
603   }
604   /**
605    * @return true if a start row was specified
606    */
607   public boolean hasStartRow() {
608     return !Bytes.equals(startRow, HConstants.EMPTY_START_ROW);
609   }
610 
611   /**
612    * @return start row
613    */
614   @XmlAttribute
615   public byte[] getStartRow() {
616     return startRow;
617   }
618 
619   /**
620    * @return true if an end row was specified
621    */
622   public boolean hasEndRow() {
623     return !Bytes.equals(endRow, HConstants.EMPTY_END_ROW);
624   }
625 
626   /**
627    * @return end row
628    */
629   @XmlAttribute
630   public byte[] getEndRow() {
631     return endRow;
632   }
633 
634   /**
635    * @return list of columns of interest in column:qualifier format, or empty for all
636    */
637   @XmlElement(name="column")
638   public List<byte[]> getColumns() {
639     return columns;
640   }
641   
642   @XmlElement(name="label")
643   public List<String> getLabels() {
644     return labels;
645   }
646 
647   /**
648    * @return the number of cells to return in batch
649    */
650   @XmlAttribute
651   public int getBatch() {
652     return batch;
653   }
654 
655   /**
656    * @return the number of rows that the scanner to fetch at once
657    */
658   @XmlAttribute
659   public int getCaching() {
660     return caching;
661   }
662 
663   /**
664    * @return the lower bound on timestamps of items of interest
665    */
666   @XmlAttribute
667   public long getStartTime() {
668     return startTime;
669   }
670 
671   /**
672    * @return the upper bound on timestamps of items of interest
673    */
674   @XmlAttribute
675   public long getEndTime() {
676     return endTime;
677   }
678 
679   /**
680    * @return maximum number of versions to return
681    */
682   @XmlAttribute
683   public int getMaxVersions() {
684     return maxVersions;
685   }
686 
687   /**
688    * @return the filter specification
689    */
690   @XmlElement
691   public String getFilter() {
692     return filter;
693   }
694 
695   /**
696    * @param startRow start row
697    */
698   public void setStartRow(byte[] startRow) {
699     this.startRow = startRow;
700   }
701 
702   /**
703    * @param endRow end row
704    */
705   public void setEndRow(byte[] endRow) {
706     this.endRow = endRow;
707   }
708 
709   /**
710    * @param columns list of columns of interest in column:qualifier format, or empty for all
711    */
712   public void setColumns(List<byte[]> columns) {
713     this.columns = columns;
714   }
715 
716   /**
717    * @param batch the number of cells to return in batch
718    */
719   public void setBatch(int batch) {
720     this.batch = batch;
721   }
722 
723   /**
724    * @param caching the number of rows to fetch at once
725    */
726   public void setCaching(int caching) {
727     this.caching = caching;
728   }
729 
730   /**
731    * @param maxVersions maximum number of versions to return
732    */
733   public void setMaxVersions(int maxVersions) {
734     this.maxVersions = maxVersions;
735   }
736 
737   /**
738    * @param startTime the lower bound on timestamps of values of interest
739    */
740   public void setStartTime(long startTime) {
741     this.startTime = startTime;
742   }
743 
744   /**
745    * @param endTime the upper bound on timestamps of values of interest
746    */
747   public void setEndTime(long endTime) {
748     this.endTime = endTime;
749   }
750 
751   /**
752    * @param filter the filter specification
753    */
754   public void setFilter(String filter) {
755     this.filter = filter;
756   }
757 
758   @Override
759   public byte[] createProtobufOutput() {
760     Scanner.Builder builder = Scanner.newBuilder();
761     if (!Bytes.equals(startRow, HConstants.EMPTY_START_ROW)) {
762       builder.setStartRow(HBaseZeroCopyByteString.wrap(startRow));
763     }
764     if (!Bytes.equals(endRow, HConstants.EMPTY_START_ROW)) {
765       builder.setEndRow(HBaseZeroCopyByteString.wrap(endRow));
766     }
767     for (byte[] column: columns) {
768       builder.addColumns(HBaseZeroCopyByteString.wrap(column));
769     }
770     if (startTime != 0) {
771       builder.setStartTime(startTime);
772     }
773     if (endTime != 0) {
774       builder.setEndTime(endTime);
775     }
776     builder.setBatch(getBatch());
777     if (caching > 0) {
778       builder.setCaching(caching);
779     }
780     builder.setMaxVersions(maxVersions);
781     if (filter != null) {
782       builder.setFilter(filter);
783     }
784     if (labels != null && labels.size() > 0) {
785       for (String label : labels)
786         builder.addLabels(label);
787     }
788     return builder.build().toByteArray();
789   }
790 
791   @Override
792   public ProtobufMessageHandler getObjectFromMessage(byte[] message)
793       throws IOException {
794     Scanner.Builder builder = Scanner.newBuilder();
795     builder.mergeFrom(message);
796     if (builder.hasStartRow()) {
797       startRow = builder.getStartRow().toByteArray();
798     }
799     if (builder.hasEndRow()) {
800       endRow = builder.getEndRow().toByteArray();
801     }
802     for (ByteString column: builder.getColumnsList()) {
803       addColumn(column.toByteArray());
804     }
805     if (builder.hasBatch()) {
806       batch = builder.getBatch();
807     }
808     if (builder.hasCaching()) {
809       caching = builder.getCaching();
810     }
811     if (builder.hasStartTime()) {
812       startTime = builder.getStartTime();
813     }
814     if (builder.hasEndTime()) {
815       endTime = builder.getEndTime();
816     }
817     if (builder.hasMaxVersions()) {
818       maxVersions = builder.getMaxVersions();
819     }
820     if (builder.hasFilter()) {
821       filter = builder.getFilter();
822     }
823     if(builder.getLabelsList() != null) {
824       List<String> labels = builder.getLabelsList();
825       for(String label :  labels) {
826         addLabel(label);
827       }
828     }
829     return this;
830   }
831 
832 }