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