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