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