Skip to content

Commit a73c1fa

Browse files
committed
HBASE-28626 MultiRowRangeFilter deserialization fails in org.apache.hadoop.hbase.rest.model.ScannerModel (#5951)
Signed-off-by: Ankit Singhal <ankit@apache.org> (cherry picked from commit cd0b29c)
1 parent 2332a30 commit a73c1fa

File tree

2 files changed

+97
-4
lines changed

2 files changed

+97
-4
lines changed

hbase-rest/src/main/java/org/apache/hadoop/hbase/rest/model/ScannerModel.java

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
import java.io.IOException;
2424
import java.io.Serializable;
2525
import java.util.ArrayList;
26+
import java.util.Arrays;
2627
import java.util.Base64;
2728
import java.util.List;
2829
import java.util.Map;
2930
import java.util.NavigableSet;
31+
import java.util.Objects;
3032
import javax.xml.bind.annotation.XmlAttribute;
3133
import javax.xml.bind.annotation.XmlElement;
3234
import javax.xml.bind.annotation.XmlRootElement;
@@ -201,6 +203,79 @@ public ByteArrayComparable build() {
201203

202204
}
203205

206+
/**
207+
* This DTO omits the pseudo-getters in MultiRowRangeFilter.RowRange which break Jackson
208+
* deserialization. It also avoids adding those as dummy JSON elements.
209+
*/
210+
static class RowRangeModel {
211+
212+
protected byte[] startRow;
213+
214+
protected boolean startRowInclusive = true;
215+
216+
protected byte[] stopRow;
217+
218+
protected boolean stopRowInclusive = false;
219+
220+
public RowRangeModel() {
221+
}
222+
223+
public RowRangeModel(MultiRowRangeFilter.RowRange rr) {
224+
this.startRow = rr.getStartRow();
225+
this.startRowInclusive = rr.isStartRowInclusive();
226+
this.stopRow = rr.getStopRow();
227+
this.stopRowInclusive = rr.isStopRowInclusive();
228+
}
229+
230+
public MultiRowRangeFilter.RowRange build() {
231+
return new MultiRowRangeFilter.RowRange(startRow, startRowInclusive, stopRow,
232+
stopRowInclusive);
233+
}
234+
235+
public byte[] getStartRow() {
236+
return startRow;
237+
}
238+
239+
public byte[] getStopRow() {
240+
return stopRow;
241+
}
242+
243+
/** Returns if start row is inclusive. */
244+
public boolean isStartRowInclusive() {
245+
return startRowInclusive;
246+
}
247+
248+
/** Returns if stop row is inclusive. */
249+
public boolean isStopRowInclusive() {
250+
return stopRowInclusive;
251+
}
252+
253+
@Override
254+
public int hashCode() {
255+
final int prime = 31;
256+
int result = 1;
257+
result = prime * result + Arrays.hashCode(startRow);
258+
result = prime * result + Arrays.hashCode(stopRow);
259+
result = prime * result + Objects.hash(startRowInclusive, stopRowInclusive);
260+
return result;
261+
}
262+
263+
@Override
264+
public boolean equals(Object obj) {
265+
if (this == obj) {
266+
return true;
267+
}
268+
if (!(obj instanceof RowRangeModel)) {
269+
return false;
270+
}
271+
RowRangeModel other = (RowRangeModel) obj;
272+
return Arrays.equals(startRow, other.startRow)
273+
&& startRowInclusive == other.startRowInclusive && Arrays.equals(stopRow, other.stopRow)
274+
&& stopRowInclusive == other.stopRowInclusive;
275+
}
276+
277+
}
278+
204279
// A grab bag of fields, would have been a union if this were C.
205280
// These are null by default and will only be serialized if set (non null).
206281
@XmlAttribute
@@ -240,7 +315,7 @@ public ByteArrayComparable build() {
240315
@XmlElement
241316
public List<String> prefixes;
242317
@XmlElement
243-
private List<RowRange> ranges;
318+
private List<RowRangeModel> ranges;
244319
@XmlElement
245320
public List<Long> timestamps;
246321

@@ -331,8 +406,7 @@ public FilterModel(Filter filter) {
331406
case MultiRowRangeFilter:
332407
this.ranges = new ArrayList<>();
333408
for (RowRange range : ((MultiRowRangeFilter) filter).getRowRanges()) {
334-
this.ranges.add(new RowRange(range.getStartRow(), range.isStartRowInclusive(),
335-
range.getStopRow(), range.isStopRowInclusive()));
409+
this.ranges.add(new RowRangeModel(range));
336410
}
337411
break;
338412
case PageFilter:
@@ -436,7 +510,11 @@ public Filter build() {
436510
}
437511
break;
438512
case MultiRowRangeFilter: {
439-
filter = new MultiRowRangeFilter(ranges);
513+
ArrayList<MultiRowRangeFilter.RowRange> rowRanges = new ArrayList<>(ranges.size());
514+
for (RowRangeModel rangeModel : ranges) {
515+
rowRanges.add(rangeModel.build());
516+
}
517+
filter = new MultiRowRangeFilter(rowRanges);
440518
}
441519
break;
442520
case PageFilter:

hbase-rest/src/test/java/org/apache/hadoop/hbase/rest/TestScannersWithFilters.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.apache.hadoop.hbase.filter.FilterList.Operator;
5252
import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
5353
import org.apache.hadoop.hbase.filter.InclusiveStopFilter;
54+
import org.apache.hadoop.hbase.filter.MultiRowRangeFilter;
5455
import org.apache.hadoop.hbase.filter.PageFilter;
5556
import org.apache.hadoop.hbase.filter.PrefixFilter;
5657
import org.apache.hadoop.hbase.filter.QualifierFilter;
@@ -957,4 +958,18 @@ public void testFirstKeyOnlyFilter() throws Exception {
957958
new KeyValue(ROWS_TWO[3], FAMILIES[0], QUALIFIERS_TWO[0], VALUES[1]) };
958959
verifyScanFull(s, kvs);
959960
}
961+
962+
@Test
963+
public void testMultiRowRangeFilter() throws Exception {
964+
long expectedRows = 2;
965+
long expectedKeys = colsPerRow;
966+
List<MultiRowRangeFilter.RowRange> ranges = new ArrayList<>();
967+
// Both return only the third element, as the second one is deleted during initialization.
968+
ranges.add(new MultiRowRangeFilter.RowRange(ROWS_ONE[1], true, ROWS_ONE[2], true));
969+
ranges.add(new MultiRowRangeFilter.RowRange(ROWS_TWO[0], false, ROWS_TWO[3], false));
970+
971+
Scan s = new Scan();
972+
s.setFilter(new MultiRowRangeFilter(ranges));
973+
verifyScan(s, expectedRows, expectedKeys);
974+
}
960975
}

0 commit comments

Comments
 (0)