-
-
Save DonDebonair/7abb733a695db2a7bad3b6ae1ac4b725 to your computer and use it in GitHub Desktop.
MockHBaseTable & MockHBaseConnection
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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[][] { | |
* { "<rowid>", "<column>", "<value>" }, | |
* { "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(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Updated to modern Table interface