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