HBASE-13273 Make Result.EMPTY_RESULT read-only; currently it can be modified
Signed-off-by: Sean Busbey <busbey@apache.org> Conflicts: hbase-client/src/main/java/org/apache/hadoop/hbase/client/Result.java hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestResult.java
This commit is contained in:
parent
c305b44362
commit
ac6fd86d26
|
@ -100,7 +100,7 @@ public class Result implements CellScannable, CellScanner {
|
||||||
|
|
||||||
private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>();
|
private static ThreadLocal<byte[]> localBuffer = new ThreadLocal<byte[]>();
|
||||||
private static final int PAD_WIDTH = 128;
|
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;
|
private final static int INITIAL_CELLSCANNER_INDEX = -1;
|
||||||
|
|
||||||
|
@ -110,6 +110,8 @@ public class Result implements CellScannable, CellScanner {
|
||||||
private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
|
private int cellScannerIndex = INITIAL_CELLSCANNER_INDEX;
|
||||||
private ClientProtos.RegionLoadStats stats;
|
private ClientProtos.RegionLoadStats stats;
|
||||||
|
|
||||||
|
private final boolean readonly;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
|
* Creates an empty Result w/ no KeyValue payload; returns null if you call {@link #rawCells()}.
|
||||||
* Use this to represent no results if <code>null</code> won't do or in old 'mapred' as oppposed to 'mapreduce' package
|
* Use this to represent no results if <code>null</code> won't do or in old 'mapred' as oppposed to 'mapreduce' package
|
||||||
|
@ -117,7 +119,16 @@ public class Result implements CellScannable, CellScanner {
|
||||||
* instance with a {@link #copyFrom(Result)} call.
|
* instance with a {@link #copyFrom(Result)} call.
|
||||||
*/
|
*/
|
||||||
public Result() {
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,7 +136,7 @@ public class Result implements CellScannable, CellScanner {
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public Result(KeyValue [] cells) {
|
public Result(KeyValue [] cells) {
|
||||||
this.cells = cells;
|
this(cells, null, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -187,6 +198,7 @@ public class Result implements CellScannable, CellScanner {
|
||||||
this.exists = exists;
|
this.exists = exists;
|
||||||
this.stale = stale;
|
this.stale = stale;
|
||||||
this.partial = partial;
|
this.partial = partial;
|
||||||
|
this.readonly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -909,9 +921,12 @@ public class Result implements CellScannable, CellScanner {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy another Result into this one. Needed for the old Mapred framework
|
* 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
|
* @param other
|
||||||
*/
|
*/
|
||||||
public void copyFrom(Result other) {
|
public void copyFrom(Result other) {
|
||||||
|
checkReadonly();
|
||||||
this.row = null;
|
this.row = null;
|
||||||
this.familyMap = null;
|
this.familyMap = null;
|
||||||
this.cells = other.cells;
|
this.cells = other.cells;
|
||||||
|
@ -941,6 +956,7 @@ public class Result implements CellScannable, CellScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setExists(Boolean exists) {
|
public void setExists(Boolean exists) {
|
||||||
|
checkReadonly();
|
||||||
this.exists = exists;
|
this.exists = exists;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -966,8 +982,11 @@ public class Result implements CellScannable, CellScanner {
|
||||||
/**
|
/**
|
||||||
* Add load information about the region to the information about the result
|
* Add load information about the region to the information about the result
|
||||||
* @param loadStats statistics about the current region from which this was returned
|
* @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) {
|
public void addResults(ClientProtos.RegionLoadStats loadStats) {
|
||||||
|
checkReadonly();
|
||||||
this.stats = loadStats;
|
this.stats = loadStats;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,4 +997,14 @@ public class Result implements CellScannable, CellScanner {
|
||||||
public ClientProtos.RegionLoadStats getStats() {
|
public ClientProtos.RegionLoadStats getStats() {
|
||||||
return stats;
|
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.CellScanner;
|
||||||
import org.apache.hadoop.hbase.CellUtil;
|
import org.apache.hadoop.hbase.CellUtil;
|
||||||
import org.apache.hadoop.hbase.KeyValue;
|
import org.apache.hadoop.hbase.KeyValue;
|
||||||
|
import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
|
||||||
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
import org.apache.hadoop.hbase.testclassification.SmallTests;
|
||||||
import org.apache.hadoop.hbase.util.Bytes;
|
import org.apache.hadoop.hbase.util.Bytes;
|
||||||
import org.junit.experimental.categories.Category;
|
import org.junit.experimental.categories.Category;
|
||||||
|
@ -228,6 +229,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.
|
* Microbenchmark that compares {@link Result#getValue} and {@link Result#loadValue} performance.
|
||||||
|
|
Loading…
Reference in New Issue