Skip to content

Instantly share code, notes, and snippets.

@DonDebonair
Forked from agaoglu/MockHTable.java
Last active September 6, 2020 06:12
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save DonDebonair/7abb733a695db2a7bad3b6ae1ac4b725 to your computer and use it in GitHub Desktop.
Save DonDebonair/7abb733a695db2a7bad3b6ae1ac4b725 to your computer and use it in GitHub Desktop.
MockHBaseTable & MockHBaseConnection
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
/**
* MockHBaseTable.
*
* original MockHTable (by agaoglu) : https://gist.github.com/agaoglu/613217
* fork used (by odd-poet): https://gist.github.com/odd-poet/bbc2aa93e41de6dd5475
*
* The original was overhauled because HTableInterface was deprecated.
* This version is based on the Table interface.
* MockHBaseConnection was added, that is able to keep track of multiple tables
*
*/
public class MockHBaseConnection implements Connection, Serializable {
private Map<String, MockHBaseTable> tables = new HashMap<>();
@Override
public Configuration getConfiguration() {
return null;
}
@Override
public MockHBaseTable getTable(TableName tableName) {
String name = tableName.getNameAsString();
return tables.computeIfAbsent(name, MockHBaseTable::new);
}
@Override
public MockHBaseTable getTable(TableName tableName, ExecutorService pool) {
return getTable(tableName);
}
public MockHBaseTable getTable(String tableName) {
return tables.get(tableName);
}
public Map<String, MockHBaseTable> getTables() {
return tables;
}
@Override
public BufferedMutator getBufferedMutator(TableName tableName) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public BufferedMutator getBufferedMutator(BufferedMutatorParams params) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public RegionLocator getRegionLocator(TableName tableName) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public Admin getAdmin() {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public void close() {
}
@Override
public boolean isClosed() {
return false;
}
@Override
public void abort(String why, Throwable e) {
}
@Override
public boolean isAborted() {
return false;
}
}
import com.google.protobuf.Descriptors;
import com.google.protobuf.Message;
import com.google.protobuf.Service;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.client.coprocessor.Batch;
import org.apache.hadoop.hbase.filter.CompareFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.*;
/**
* MockHBaseTable.
*
* original MockHTable (by agaoglu) : https://gist.github.com/agaoglu/613217
* fork used (by odd-poet): https://gist.github.com/odd-poet/bbc2aa93e41de6dd5475
*
* The original was overhauled because HTableInterface was deprecated.
* This version is based on the Table interface.
* MockHBaseConnection was added, that is able to keep track of multiple tables
*
*/
public class MockHBaseTable implements Table {
private final String tableName;
private final List<String> columnFamilies = new ArrayList<>();
private NavigableMap<byte[], NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>> data
= new TreeMap<>(Bytes.BYTES_COMPARATOR);
private static List<Cell> toCell(byte[] row, NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> rowdata, int maxVersions) {
return toCell(row, rowdata, 0, Long.MAX_VALUE, maxVersions);
}
public MockHBaseTable(String tableName) {
this.tableName = tableName;
}
public MockHBaseTable(String tableName, String... columnFamilies) {
this.tableName = tableName;
this.columnFamilies.addAll(Arrays.asList(columnFamilies));
}
public void addColumnFamily(String columnFamily) {
this.columnFamilies.add(columnFamily);
}
public NavigableMap<byte[], NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>> getData() {
return data;
}
/**
* {@inheritDoc}
*/
@Override
public TableName getName() {
return TableName.valueOf(tableName);
}
/**
* {@inheritDoc}
*/
@Override
public Configuration getConfiguration() {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
/**
* {@inheritDoc}
*/
@Override
public HTableDescriptor getTableDescriptor() {
HTableDescriptor table = new HTableDescriptor(getName());
for (String columnFamily : columnFamilies) {
table.addFamily(new HColumnDescriptor(columnFamily));
}
return table;
}
/**
* {@inheritDoc}
*/
@Override
public void mutateRow(RowMutations rm) {
// currently only support Put and Delete
for (Mutation mutation : rm.getMutations()) {
if (mutation instanceof Put) {
put((Put) mutation);
} else if (mutation instanceof Delete) {
delete((Delete) mutation);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public Result append(Append append) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
private static List<Cell> toCell(byte[] row, NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> rowdata, long timestampStart, long timestampEnd, int maxVersions) {
List<Cell> ret = new ArrayList<>();
byte putType = KeyValue.Type.Put.getCode();
for (byte[] family : rowdata.keySet())
for (byte[] qualifier : rowdata.get(family).keySet()) {
int versionsAdded = 0;
for (Map.Entry<Long, byte[]> tsToVal : rowdata.get(family).get(qualifier).descendingMap().entrySet()) {
if (versionsAdded++ == maxVersions)
break;
Long timestamp = tsToVal.getKey();
if (timestamp < timestampStart)
continue;
if (timestamp > timestampEnd)
continue;
byte[] value = tsToVal.getValue();
ret.add(CellUtil.createCell(row, family, qualifier, timestamp, putType, value));
}
}
return ret;
}
/**
* {@inheritDoc}
*/
@Override
public boolean exists(Get get) throws IOException {
Result result = get(get);
return result != null && !result.isEmpty();
}
@Override
public boolean[] existsAll(List<Get> gets) throws IOException {
boolean[] result = new boolean[gets.size()];
for(int i = 0;i < gets.size();i++) {
result[i] = exists(gets.get(i));
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void batch(List<? extends Row> actions, Object[] results) throws IOException {
batch(actions);
}
/**
* {@inheritDoc}
*/
@Override
public Object[] batch(List<? extends Row> actions) throws IOException {
Object[] results = new Object[actions.size()]; // same size.
for (int i = 0; i < actions.size(); i++) {
Row r = actions.get(i);
if (r instanceof Delete) {
delete((Delete) r);
results[i] = new Result();
}
if (r instanceof Put) {
put((Put) r);
results[i] = new Result();
}
if (r instanceof Get) {
Result result = get((Get) r);
results[i] = result;
}
if (r instanceof Increment) {
Result result = increment((Increment) r);
results[i] = result;
}
if (r instanceof Append) {
Result result = append((Append) r);
results[i] = result;
}
}
return results;
}
@Override
public <R> void batchCallback(List<? extends Row> actions, Object[] results, Batch.Callback<R> callback) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public <R> Object[] batchCallback(List<? extends Row> actions, Batch.Callback<R> callback) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
/**
* {@inheritDoc}
*/
@Override
public Result get(Get get) throws IOException {
if (!data.containsKey(get.getRow()))
return new Result();
byte[] row = get.getRow();
List<Cell> cells = new ArrayList<>();
if (!get.hasFamilies()) {
cells = toCell(row, data.get(row), get.getMaxVersions());
} else {
for (byte[] family : get.getFamilyMap().keySet()) {
if (data.get(row).get(family) == null)
continue;
NavigableSet<byte[]> qualifiers = get.getFamilyMap().get(family);
if (qualifiers == null || qualifiers.isEmpty())
qualifiers = data.get(row).get(family).navigableKeySet();
for (byte[] qualifier : qualifiers) {
if (qualifier == null)
qualifier = "".getBytes();
if (!data.get(row).containsKey(family) ||
!data.get(row).get(family).containsKey(qualifier) ||
data.get(row).get(family).get(qualifier).isEmpty())
continue;
Map.Entry<Long, byte[]> timestampAndValue = data.get(row).get(family).get(qualifier).lastEntry();
cells.add(new KeyValue(row, family, qualifier, timestampAndValue.getKey(), timestampAndValue.getValue()));
}
}
}
Filter filter = get.getFilter();
if (filter != null) {
cells = filter(filter, cells);
}
cells.sort(new CellComparator());
return Result.create(cells);
}
/**
* {@inheritDoc}
*/
@Override
public Result[] get(List<Get> gets) throws IOException {
List<Result> results = new ArrayList<>();
for (Get g : gets) {
results.add(get(g));
}
return results.toArray(new Result[0]);
}
/**
* {@inheritDoc}
*/
@Override
public ResultScanner getScanner(Scan scan) throws IOException {
final List<Result> ret = new ArrayList<>();
byte[] st = scan.getStartRow();
byte[] sp = scan.getStopRow();
Filter filter = scan.getFilter();
for (byte[] row : data.keySet()) {
// if row is equal to startRow emit it. When startRow (inclusive) and
// stopRow (exclusive) is the same, it should not be excluded which would
// happen w/o this control.
if (st != null && st.length > 0 &&
Bytes.BYTES_COMPARATOR.compare(st, row) != 0) {
// if row is before startRow do not emit, pass to next row
if (Bytes.BYTES_COMPARATOR.compare(st, row) > 0)
continue;
// if row is equal to stopRow or after it do not emit, stop iteration
if (sp != null && sp.length > 0 &&
Bytes.BYTES_COMPARATOR.compare(sp, row) <= 0)
break;
}
List<Cell> kvs;
if (!scan.hasFamilies()) {
kvs = toCell(row, data.get(row), scan.getTimeRange().getMin(), scan.getTimeRange().getMax(), scan.getMaxVersions());
} else {
kvs = new ArrayList<>();
for (byte[] family : scan.getFamilyMap().keySet()) {
if (data.get(row).get(family) == null)
continue;
NavigableSet<byte[]> qualifiers = scan.getFamilyMap().get(family);
if (qualifiers == null || qualifiers.isEmpty())
qualifiers = data.get(row).get(family).navigableKeySet();
for (byte[] qualifier : qualifiers) {
if (data.get(row).get(family).get(qualifier) == null)
continue;
for (Long timestamp : data.get(row).get(family).get(qualifier).descendingKeySet()) {
if (timestamp < scan.getTimeRange().getMin())
continue;
if (timestamp > scan.getTimeRange().getMax())
continue;
byte[] value = data.get(row).get(family).get(qualifier).get(timestamp);
kvs.add(new KeyValue(row, family, qualifier, timestamp, value));
if (kvs.size() == scan.getMaxVersions()) {
break;
}
}
}
}
}
if (filter != null) {
kvs = filter(filter, kvs);
// Check for early out optimization
if (filter.filterAllRemaining()) {
break;
}
}
if (!kvs.isEmpty()) {
kvs.sort(new CellComparator());
ret.add(Result.create(kvs));
}
}
return new ResultScanner() {
private final Iterator<Result> iterator = ret.iterator();
public Iterator<Result> iterator() {
return iterator;
}
public Result[] next(int nbRows) {
ArrayList<Result> resultSets = new ArrayList<>(nbRows);
for (int i = 0; i < nbRows; i++) {
Result next = next();
if (next != null) {
resultSets.add(next);
} else {
break;
}
}
return resultSets.toArray(new Result[0]);
}
public Result next() {
try {
return iterator().next();
} catch (NoSuchElementException e) {
return null;
}
}
public void close() {
}
};
}
/**
* Follows the logical flow through the filter methods for a single row.
*
* @param filter HBase filter.
* @param cells List of a row's Cells
* @return List of Cells that were not filtered.
*/
private List<Cell> filter(Filter filter, List<Cell> cells) throws IOException {
filter.reset();
List<Cell> tmp = new ArrayList<>(cells.size());
tmp.addAll(cells);
/*
* Note. Filter flow for a single row. Adapted from
* "HBase: The Definitive Guide" (p. 163) by Lars George, 2011.
* See Figure 4-2 on p. 163.
*/
boolean filteredOnRowKey = false;
List<Cell> nkvs = new ArrayList<>(tmp.size());
for (Cell cell : tmp) {
if (filter.filterRowKey(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength())) {
filteredOnRowKey = true;
break;
}
Filter.ReturnCode filterResult = filter.filterKeyValue(cell);
if (filterResult == Filter.ReturnCode.INCLUDE) {
nkvs.add(cell);
} else if (filterResult == Filter.ReturnCode.NEXT_ROW) {
break;
}
/*
* Ignoring next key hint which is a optimization to reduce file
* system IO
*/
}
if (filter.hasFilterRow() && !filteredOnRowKey) {
filter.filterRowCells(nkvs);
}
if (filter.filterRow() || filteredOnRowKey) {
nkvs.clear();
}
return nkvs;
}
/**
* {@inheritDoc}
*/
@Override
public ResultScanner getScanner(byte[] family) throws IOException {
Scan scan = new Scan();
scan.addFamily(family);
return getScanner(scan);
}
/**
* {@inheritDoc}
*/
@Override
public ResultScanner getScanner(byte[] family, byte[] qualifier) throws IOException {
Scan scan = new Scan();
scan.addColumn(family, qualifier);
return getScanner(scan);
}
private <K, V> V forceFind(NavigableMap<K, V> map, K key, V newObject) {
return map.computeIfAbsent(key, k -> newObject);
}
/**
* {@inheritDoc}
*/
@Override
public void put(Put put) {
System.out.println("Performing Put in " + this.getName().getNameAsString());
byte[] row = put.getRow();
NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> rowData = forceFind(data, row, new TreeMap<>(Bytes.BYTES_COMPARATOR));
for (byte[] family : put.getFamilyCellMap().keySet()) {
// if (!columnFamilies.contains(new String(family))) {
// throw new RuntimeException("Not Exists columnFamily : " + new String(family));
// }
NavigableMap<byte[], NavigableMap<Long, byte[]>> familyData = forceFind(rowData, family, new TreeMap<>(Bytes.BYTES_COMPARATOR));
for (Cell cell : put.getFamilyCellMap().get(family)) {
byte[] qualifier = CellUtil.cloneQualifier(cell);
NavigableMap<Long, byte[]> qualifierData = forceFind(familyData, qualifier, new TreeMap<>());
qualifierData.put(cell.getTimestamp(), CellUtil.cloneValue(cell));
}
}
System.out.println("Data in " + this.getName().getNameAsString() + ": " + this.toString());
}
/**
* {@inheritDoc}
*/
@Override
public void put(List<Put> puts) {
for (Put put : puts) {
put(put);
}
}
private boolean check(byte[] row, byte[] family, byte[] qualifier, byte[] value) {
if (value == null || value.length == 0)
return !data.containsKey(row) ||
!data.get(row).containsKey(family) ||
!data.get(row).get(family).containsKey(qualifier);
else
return data.containsKey(row) &&
data.get(row).containsKey(family) &&
data.get(row).get(family).containsKey(qualifier) &&
!data.get(row).get(family).get(qualifier).isEmpty() &&
Arrays.equals(data.get(row).get(family).get(qualifier).lastEntry().getValue(), value);
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, byte[] value, Put put) {
if (check(row, family, qualifier, value)) {
put(put);
return true;
}
return false;
}
@Override
public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value, Put put) {
return false;
}
/**
* {@inheritDoc}
*/
@Override
public void delete(Delete delete) {
byte[] row = delete.getRow();
NavigableMap<byte [], List<Cell>> familyCellMap = delete.getFamilyCellMap();
if (data.get(row) == null)
return;
if (familyCellMap.size() == 0) {
data.remove(row);
return;
}
for (byte[] family : familyCellMap.keySet()) {
if (data.get(row).get(family) == null)
continue;
if (familyCellMap.get(family).isEmpty()) {
data.get(row).remove(family);
continue;
}
for (Cell cell : familyCellMap.get(family)) {
data.get(row).get(CellUtil.cloneFamily(cell)).remove(CellUtil.cloneQualifier(cell));
}
if (data.get(row).get(family).isEmpty()) {
data.get(row).remove(family);
}
}
if (data.get(row).isEmpty()) {
data.remove(row);
}
}
/**
* {@inheritDoc}
*/
@Override
public void delete(List<Delete> deletes) {
for (Delete delete : deletes) {
delete(delete);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, byte[] value, Delete delete) {
if (check(row, family, qualifier, value)) {
delete(delete);
return true;
}
return false;
}
// TODO: Implement?
@Override
public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value, Delete delete) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
/**
* {@inheritDoc}
*/
@Override
public Result increment(Increment increment) {
List<Cell> cells = new ArrayList<>();
Map<byte[], NavigableMap<byte[], Long>> famToVal = increment.getFamilyMapOfLongs();
for (Map.Entry<byte[], NavigableMap<byte[], Long>> ef : famToVal.entrySet()) {
byte[] family = ef.getKey();
NavigableMap<byte[], Long> qToVal = ef.getValue();
for (Map.Entry<byte[], Long> eq : qToVal.entrySet()) {
long newValue = incrementColumnValue(increment.getRow(), family, eq.getKey(), eq.getValue());
cells.add(CellUtil.createCell(increment.getRow(), family, eq.getKey(), System.currentTimeMillis(), KeyValue.Type.Put.getCode(), Bytes.toBytes(newValue)));
}
}
return Result.create(cells);
}
// TODO: Implement?
/**
* {@inheritDoc}
*/
@Override
public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount) {
return incrementColumnValue(row, family, qualifier, amount, Durability.USE_DEFAULT);
}
// TODO: Implement?
@Override
public long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount, Durability durability) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
/**
* {@inheritDoc}
*/
@Override
public void close() {
}
@Override
public CoprocessorRpcChannel coprocessorService(byte[] row) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public <T extends Service, R> Map<byte[], R> coprocessorService(Class<T> service, byte[] startKey, byte[] endKey, Batch.Call<T, R> callable) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public <T extends Service, R> void coprocessorService(Class<T> service, byte[] startKey, byte[] endKey, Batch.Call<T, R> callable, Batch.Callback<R> callback) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
/**
* {@inheritDoc}
*/
@Override
public long getWriteBufferSize() {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
/**
* {@inheritDoc}
*/
@Override
public void setWriteBufferSize(long writeBufferSize) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public <R extends Message> Map<byte[], R> batchCoprocessorService(Descriptors.MethodDescriptor methodDescriptor, Message request, byte[] startKey, byte[] endKey, R responsePrototype) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public <R extends Message> void batchCoprocessorService(Descriptors.MethodDescriptor methodDescriptor, Message request, byte[] startKey, byte[] endKey, R responsePrototype, Batch.Callback<R> callback) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
// TODO: Implement?
@Override
public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier, CompareFilter.CompareOp compareOp, byte[] value, RowMutations mutation) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public void setOperationTimeout(int operationTimeout) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public int getOperationTimeout() {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public void setRpcTimeout(int rpcTimeout) {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
@Override
public int getRpcTimeout() {
throw new RuntimeException(this.getClass() + " does NOT implement this method.");
}
private static void indent(int level, StringBuilder sb) {
String indent = new String(new char[level]).replace("\0", " ");
sb.append(indent);
}
/**
* Create a MockHBaseTable with some pre-loaded data. Parameter should be a map of
* column-to-data mappings of rows. It can be created with data like:
*
* <pre>
* rowid:
* family1:qualifier1: value1
* family2:qualifier2: value2
* </pre>
*
* @param name name of the new table
* @param data data to initialize the table with
* @return a new MockHBaseTable loaded with given data
*/
public static MockHBaseTable with(String name, Map<String, Map<String, String>> data){
MockHBaseTable table = new MockHBaseTable(name);
for (String row : data.keySet()){
for (String column : data.get(row).keySet()){
String val = data.get(row).get(column);
put(table, row, column, val);
}
}
return table;
}
/**
* Create a MockHBaseTable with some pre-loaded data. Parameter should be an array
* of string arrays which define every column value individually.
*
* <pre>
* new String[][] {
* { "&lt;rowid&gt;", "&lt;column&gt;", "&lt;value&gt;" },
* { "id", "family:qualifier1", "data1" },
* { "id", "family:qualifier2", "data2" }
* });
* </pre>
*
* @param name name of the new table
* @param data data to initialize the table with
* @return a new MockHBaseTable loaded with given data
*/
public static MockHBaseTable with(String name, String[][] data) {
MockHBaseTable ret = new MockHBaseTable(name);
for(String[] row : data){
put(ret, row[0], row[1], row[2]);
}
return ret;
}
/**
* Helper method of pre-loaders, adds parameters to data.
*
* @param table table to load data into
* @param row row id
* @param column family:qualifier encoded value
* @param val value
*/
private static void put(MockHBaseTable table, String row, String column, String val) {
String[] fq = split(column);
byte[] family = Bytes.toBytes(fq[0]);
byte[] qualifier = Bytes.toBytes(fq[1]);
NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> families = table.forceFind(table.data, Bytes.toBytes(row), new TreeMap<>(Bytes.BYTES_COMPARATOR));
NavigableMap<byte[], NavigableMap<Long, byte[]>> qualifiers = table.forceFind(families, family, new TreeMap<>(Bytes.BYTES_COMPARATOR));
NavigableMap<Long, byte[]> values = table.forceFind(qualifiers, qualifier, new TreeMap<>());
values.put(System.currentTimeMillis(), Bytes.toBytes(val));
}
/**
* Column identification helper
*
* @param column
* column name in the format family:qualifier
* @return <code>{"family", "qualifier"}</code>
*/
private static String[] split(String column) {
return new String[]{
column.substring(0, column.indexOf(':')),
column.substring(column.indexOf(':') + 1)
};
}
/**
* Read a value saved in the object. Useful for making assertions in tests.
*
* @param rowid
* rowid of the data to read
* @param column
* family:qualifier of the data to read
* @return value or null if row or column of the row does not exist
*/
public byte[] read(String rowid, String column){
NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> row = data.get(Bytes.toBytes(rowid));
if (row == null)
return null;
String[] fq = split(column);
byte[] family = Bytes.toBytes(fq[0]);
byte[] qualifier = Bytes.toBytes(fq[1]);
if (!row.containsKey(family))
return null;
if (!row.get(family).containsKey(qualifier))
return null;
return row.get(family).get(qualifier).lastEntry().getValue();
}
@Override
public String toString() {
String nl = System.getProperty("line.separator");
int i = 1;
StringBuilder sb = new StringBuilder();
sb.append("{");
sb.append(nl);
for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>>> row: getData().entrySet()) {
indent(i, sb);
sb.append(Bytes.toString(row.getKey()));
sb.append(":");
sb.append(nl);
i++;
for(Map.Entry<byte[], NavigableMap<byte[], NavigableMap<Long, byte[]>>> family: row.getValue().entrySet()) {
indent(i, sb);
sb.append(Bytes.toString(family.getKey()));
sb.append(":");
sb.append(nl);
i++;
for(Map.Entry<byte[], NavigableMap<Long, byte[]>> column: family.getValue().entrySet()) {
indent(i, sb);
sb.append(Bytes.toString(column.getKey()));
sb.append(": ");
sb.append(Bytes.toString(column.getValue().lastEntry().getValue()));
sb.append(nl);
}
i--;
}
i--;
}
sb.append(nl);
sb.append("}");
return sb.toString();
}
}
@DonDebonair
Copy link
Author

Updated to modern Table interface

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment