HBASE-13273 Make Result.EMPTY_RESULT read-only; currently it can be modified
Signed-off-by: Sean Busbey <busbey@cloudera.com>
This commit is contained in:
parent
fcc09dcd38
commit
8cb4f89c01
|
@ -102,7 +102,7 @@ public class Result implements CellScannable, CellScanner {
|
|||
|
||||
private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>();
|
||||
private static final int PAD_WIDTH = 128;
|
||||
public static final Result EMPTY_RESULT = new Result();
|
||||
public static final Result EMPTY_RESULT = new Result(true);
|
||||
|
||||
private final static int INITIAL_CELLSCANNER_INDEX = -1;
|
||||
|
||||
|
@ -112,6 +112,8 @@ public class Result implements CellScannable, CellScanner {
|
|||
private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
|
||||
private ClientProtos.RegionLoadStats stats;
|
||||
|
||||
private final boolean readonly;
|
||||
|
||||
/**
|
||||
* Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
|
||||
* Use this to represent no results if {@code null} won't do or in old 'mapred' as opposed
|
||||
|
@ -119,7 +121,16 @@ public class Result implements CellScannable, CellScanner {
|
|||
* {@link #copyFrom(Result)} call.
|
||||
*/
|
||||
public Result() {
|
||||
super();
|
||||
this(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to construct special purpose immutable Result objects,
|
||||
* such as EMPTY_RESULT.
|
||||
* @param readonly whether this Result instance is readonly
|
||||
*/
|
||||
private Result(boolean readonly) {
|
||||
this.readonly = readonly;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,6 +183,7 @@ public class Result implements CellScannable, CellScanner {
|
|||
this.exists = exists;
|
||||
this.stale = stale;
|
||||
this.partial = partial;
|
||||
this.readonly = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -834,9 +846,12 @@ public class Result implements CellScannable, CellScanner {
|
|||
|
||||
/**
|
||||
* Copy another Result into this one. Needed for the old Mapred framework
|
||||
* @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
|
||||
* (which is supposed to be immutable).
|
||||
* @param other
|
||||
*/
|
||||
public void copyFrom(Result other) {
|
||||
checkReadonly();
|
||||
this.row = null;
|
||||
this.familyMap = null;
|
||||
this.cells = other.cells;
|
||||
|
@ -866,6 +881,7 @@ public class Result implements CellScannable, CellScanner {
|
|||
}
|
||||
|
||||
public void setExists(Boolean exists) {
|
||||
checkReadonly();
|
||||
this.exists = exists;
|
||||
}
|
||||
|
||||
|
@ -891,8 +907,11 @@ public class Result implements CellScannable, CellScanner {
|
|||
/**
|
||||
* Add load information about the region to the information about the result
|
||||
* @param loadStats statistics about the current region from which this was returned
|
||||
* @throws UnsupportedOperationException if invoked on instance of EMPTY_RESULT
|
||||
* (which is supposed to be immutable).
|
||||
*/
|
||||
public void addResults(ClientProtos.RegionLoadStats loadStats) {
|
||||
checkReadonly();
|
||||
this.stats = loadStats;
|
||||
}
|
||||
|
||||
|
@ -903,4 +922,14 @@ public class Result implements CellScannable, CellScanner {
|
|||
public ClientProtos.RegionLoadStats getStats() {
|
||||
return stats;
|
||||
}
|
||||
|
||||
/**
|
||||
* All methods modifying state of Result object must call this method
|
||||
* to ensure that special purpose immutable Results can't be accidentally modified.
|
||||
*/
|
||||
private void checkReadonly() {
|
||||
if (readonly == true) {
|
||||
throw new UnsupportedOperationException("Attempting to modify readonly EMPTY_RESULT!");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -34,6 +34,7 @@ import org.apache.hadoop.hbase.Cell;
|
|||
import org.apache.hadoop.hbase.CellScanner;
|
||||
import org.apache.hadoop.hbase.CellUtil;
|
||||
import org.apache.hadoop.hbase.KeyValue;
|
||||
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
|
||||
import org.apache.hadoop.hbase.testclassification.ClientTests;
|
||||
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
|
@ -229,6 +230,32 @@ public class TestResult extends TestCase {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that one can't modify instance of EMPTY_RESULT.
|
||||
*/
|
||||
public void testEmptyResultIsReadonly() {
|
||||
Result emptyResult = Result.EMPTY_RESULT;
|
||||
Result otherResult = new Result();
|
||||
|
||||
try {
|
||||
emptyResult.copyFrom(otherResult);
|
||||
fail("UnsupportedOperationException should have been thrown!");
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.debug("As expected: " + ex.getMessage());
|
||||
}
|
||||
try {
|
||||
emptyResult.addResults(ClientProtos.RegionLoadStats.getDefaultInstance());
|
||||
fail("UnsupportedOperationException should have been thrown!");
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.debug("As expected: " + ex.getMessage());
|
||||
}
|
||||
try {
|
||||
emptyResult.setExists(true);
|
||||
fail("UnsupportedOperationException should have been thrown!");
|
||||
} catch (UnsupportedOperationException ex) {
|
||||
LOG.debug("As expected: " + ex.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance.
|
||||
|
|
Loading…
Reference in New Issue