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