package mil.nga.giat.geowave.datastore.accumulo.mapreduce;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyMap;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.apache.accumulo.core.client.AccumuloException;
import org.apache.accumulo.core.client.AccumuloSecurityException;
import org.apache.accumulo.core.client.Connector;
import org.apache.accumulo.core.client.TableNotFoundException;
import org.apache.accumulo.core.client.impl.TabletLocator;
import org.apache.accumulo.core.client.mock.MockInstance;
import org.apache.accumulo.core.client.security.tokens.PasswordToken;
import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Range;
import org.apache.accumulo.core.data.thrift.TKey;
import org.apache.accumulo.core.data.thrift.TRange;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.InputSplit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.junit.Before;
import org.junit.Test;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import mil.nga.giat.geowave.core.geotime.ingest.SpatialDimensionalityTypeProvider;
import mil.nga.giat.geowave.core.geotime.store.dimension.GeometryWrapper;
import mil.nga.giat.geowave.core.geotime.store.query.SpatialQuery;
import mil.nga.giat.geowave.core.geotime.store.statistics.BoundingBoxDataStatistics;
import mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.store.DataStoreOperations;
import mil.nga.giat.geowave.core.store.EntryVisibilityHandler;
import mil.nga.giat.geowave.core.store.adapter.AbstractDataAdapter;
import mil.nga.giat.geowave.core.store.adapter.AdapterIndexMappingStore;
import mil.nga.giat.geowave.core.store.adapter.AdapterStore;
import mil.nga.giat.geowave.core.store.adapter.DataAdapter;
import mil.nga.giat.geowave.core.store.adapter.NativeFieldHandler;
import mil.nga.giat.geowave.core.store.adapter.NativeFieldHandler.RowBuilder;
import mil.nga.giat.geowave.core.store.adapter.PersistentIndexFieldHandler;
import mil.nga.giat.geowave.core.store.adapter.WritableDataAdapter;
import mil.nga.giat.geowave.core.store.adapter.statistics.CountDataStatistics;
import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics;
import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatisticsStore;
import mil.nga.giat.geowave.core.store.adapter.statistics.FieldTypeStatisticVisibility;
import mil.nga.giat.geowave.core.store.adapter.statistics.RowRangeHistogramStatistics;
import mil.nga.giat.geowave.core.store.adapter.statistics.StatisticsProvider;
import mil.nga.giat.geowave.core.store.data.PersistentValue;
import mil.nga.giat.geowave.core.store.data.field.FieldReader;
import mil.nga.giat.geowave.core.store.data.field.FieldUtils;
import mil.nga.giat.geowave.core.store.data.field.FieldWriter;
import mil.nga.giat.geowave.core.store.dimension.NumericDimensionField;
import mil.nga.giat.geowave.core.store.index.CommonIndexModel;
import mil.nga.giat.geowave.core.store.index.CommonIndexValue;
import mil.nga.giat.geowave.core.store.index.Index;
import mil.nga.giat.geowave.core.store.index.PrimaryIndex;
import mil.nga.giat.geowave.core.store.query.DistributableQuery;
import mil.nga.giat.geowave.core.store.query.QueryOptions;
import mil.nga.giat.geowave.datastore.accumulo.AccumuloDataStore;
import mil.nga.giat.geowave.datastore.accumulo.AccumuloOperations;
import mil.nga.giat.geowave.datastore.accumulo.BasicAccumuloOperations;
import mil.nga.giat.geowave.datastore.accumulo.index.secondary.AccumuloSecondaryIndexDataStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloAdapterIndexMappingStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloAdapterStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloDataStatisticsStore;
import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloIndexStore;
import mil.nga.giat.geowave.datastore.accumulo.operations.config.AccumuloOptions;
import mil.nga.giat.geowave.mapreduce.splits.GeoWaveInputSplit;
import mil.nga.giat.geowave.mapreduce.splits.GeoWaveRowRange;
import mil.nga.giat.geowave.mapreduce.splits.IntermediateSplitInfo;
import mil.nga.giat.geowave.mapreduce.splits.SplitsProvider;
//@formatter:off
/*if[accumulo.api=1.6]
import org.apache.accumulo.core.security.Credentials;
import org.apache.accumulo.core.data.KeyExtent;
else[accumulo.api=1.6]*/
import org.apache.accumulo.core.client.impl.ClientContext;
import org.apache.accumulo.core.data.impl.KeyExtent;
/*end[accumulo.api=1.6]*/
//@formatter:on
public class AccumuloSplitsProviderTest
{
private final static Logger LOGGER = LoggerFactory.getLogger(AccumuloSplitsProviderTest.class);
final AccumuloOptions accumuloOptions = new AccumuloOptions();
final GeometryFactory factory = new GeometryFactory();
AccumuloOperations accumuloOperations;
AccumuloIndexStore indexStore;
AccumuloAdapterStore adapterStore;
AccumuloDataStatisticsStore statsStore;
AccumuloDataStore mockDataStore;
AccumuloSecondaryIndexDataStore secondaryIndexDataStore;
AdapterIndexMappingStore adapterIndexMappingStore;
TabletLocator tabletLocator;
PrimaryIndex index;
WritableDataAdapter<TestGeometry> adapter;
Geometry testGeoFilter;
Map<PrimaryIndex, RowRangeHistogramStatistics<?>> statsCache;
/**
* public List<InputSplit> getSplits(
*
* final DistributableQuery query, final QueryOptions queryOptions,
*
* final IndexStore indexStore,
*
* final AdapterIndexMappingStore adapterIndexMappingStore, final Integer
* minSplits, final Integer maxSplits )
*/
@Before
public void setUp() {
final MockInstance mockInstance = new MockInstance();
Connector mockConnector = null;
try {
mockConnector = mockInstance.getConnector(
"root",
new PasswordToken(
new byte[0]));
}
catch (AccumuloException | AccumuloSecurityException e) {
LOGGER.error(
"Failed to create mock accumulo connection",
e);
}
accumuloOperations = new BasicAccumuloOperations(
mockConnector);
indexStore = new AccumuloIndexStore(
accumuloOperations);
adapterStore = new AccumuloAdapterStore(
accumuloOperations);
statsStore = new AccumuloDataStatisticsStore(
accumuloOperations);
secondaryIndexDataStore = new AccumuloSecondaryIndexDataStore(
accumuloOperations,
new AccumuloOptions());
adapterIndexMappingStore = new AccumuloAdapterIndexMappingStore(
accumuloOperations);
mockDataStore = new AccumuloDataStore(
indexStore,
adapterStore,
statsStore,
secondaryIndexDataStore,
adapterIndexMappingStore,
accumuloOperations,
accumuloOptions);
index = new SpatialDimensionalityTypeProvider().createPrimaryIndex();
adapter = new TestGeometryAdapter();
tabletLocator = mock(TabletLocator.class);
testGeoFilter = factory.createPolygon(new Coordinate[] {
new Coordinate(
24,
33),
new Coordinate(
28,
33),
new Coordinate(
28,
31),
new Coordinate(
24,
31),
new Coordinate(
24,
33)
});
statsCache = new HashMap<PrimaryIndex, RowRangeHistogramStatistics<?>>();
}
@Test
public void testPopulateIntermediateSplits_EmptyRange() {
final SpatialQuery query = new SpatialQuery(
testGeoFilter);
final SplitsProvider splitsProvider = new MockAccumuloSplitsProvider(
tabletLocator) {
@Override
public void addMocks() {
doNothing().when(
tabletLocator).invalidateCache();
}
};
try {
List<InputSplit> splits = splitsProvider.getSplits(
accumuloOperations,
query,
new QueryOptions(
adapter,
index,
-1,
null,
new String[] {
"aaa",
"bbb"
}),
adapterStore,
statsStore,
indexStore,
adapterIndexMappingStore,
1,
5);
verify(
tabletLocator).invalidateCache();
assertThat(
splits.isEmpty(),
is(true));
}
catch (IOException | InterruptedException e) {
e.printStackTrace();
assertFalse(
"Not expecting an error",
true);
}
}
/**
* Used to simulate what happens if an HBase operations for instance gets
* passed in
*
* @author akash_000
*
*/
private static class MockOperations implements
DataStoreOperations
{
public boolean tableExists(
String altIdxTableName )
throws IOException {
return false;
}
public void deleteAll()
throws Exception {}
public String getTableNameSpace() {
return null;
}
@Override
public boolean mergeData(
PrimaryIndex index,
AdapterStore adapterStore,
AdapterIndexMappingStore adapterIndexMappingStore ) {
// TODO Auto-generated method stub
return false;
}
}
@Test
public void testPopulateIntermediateSplits_MismatchedOperations() {
final SpatialQuery query = new SpatialQuery(
testGeoFilter);
final SplitsProvider splitsProvider = new MockAccumuloSplitsProvider(
tabletLocator) {
@Override
public void addMocks() {
// no mocks
}
};
try {
List<InputSplit> splits = splitsProvider.getSplits(
new MockOperations(),
query,
new QueryOptions(
adapter,
index,
-1,
null,
new String[] {
"aaa",
"bbb"
}),
adapterStore,
statsStore,
indexStore,
adapterIndexMappingStore,
1,
5);
assertThat(
splits.isEmpty(),
is(true));
// no need to verify mock here, no actions taken on it
}
catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void testPopulateIntermediateSplits_UnsupportedQuery() {
final SpatialQuery query = new SpatialQuery(
testGeoFilter) {
@Override
public boolean isSupported(
final Index<?, ?> index ) {
return false;
}
};
final SplitsProvider splitsProvider = new MockAccumuloSplitsProvider(
tabletLocator) {
@Override
public void addMocks() {
// no mocks
}
};
try {
List<InputSplit> splits = splitsProvider.getSplits(
accumuloOperations,
query,
new QueryOptions(
adapter,
index,
-1,
null,
new String[] {
"aaa",
"bbb"
}),
adapterStore,
statsStore,
indexStore,
adapterIndexMappingStore,
1,
5);
// if query is unsupported, return an empty split, with no error
assertThat(
splits.isEmpty(),
is(true));
// no need to verify mock here, no actions taken on it
}
catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
@Test
public void testPopulateIntermediateSplits_PrebuiltRange() {
final SpatialQuery query = new SpatialQuery(
testGeoFilter);
final SplitsProvider splitsProvider = new MockAccumuloSplitsProvider(
tabletLocator,
new AccumuloRowRange(
new Range(
"aa",
"bb"))) {
@Override
public void addMocks() {
doNothing().when(
tabletLocator).invalidateCache();
try {
when(
tabletLocator.binRanges(
isA(
//@formatter:off
/*if[accumulo.api=1.6]
Credentials.class
else[accumulo.api=1.6]*/
ClientContext.class
/*end[accumulo.api=1.6]*/
//@formatter:on
),
anyList(),
anyMap())).thenReturn(
new ArrayList<Range>());
}
catch (AccumuloException | AccumuloSecurityException | TableNotFoundException e) {
e.printStackTrace();
}
}
@Override
/**
* Build our own simple Binned Range Structure
* Here is what it will look like:
*
* Initial level: Map of String (tablet locations) to another Map
* Second level: Map of KeyExtents (the difference between our previous range and current range) to Range lists
* Third level: List of the range lists
*
* This implementation will have two tablet locations (assumed to be two nodes), 4 KeyExtents per tablet location (3, 3, 3, 4) with the total range spanning a to z
* @return
*/
public Map<String, Map<KeyExtent, List<Range>>> getBinnedRangesStructure() {
final Map<String, Map<KeyExtent, List<Range>>> tserverBinnedRanges = new HashMap<String, Map<KeyExtent, List<Range>>>();
// first tablet locator
tserverBinnedRanges.put(
"127.0.0.1:tabletLocator1",
new HashMap<KeyExtent, List<Range>>());
// second tablet locator
tserverBinnedRanges.put(
"127.0.0.2:tabletLocator2",
new HashMap<KeyExtent, List<Range>>());
final Text table = new Text(
"GEOWAVE_METADATA");
// key extents for first tablet locator
tserverBinnedRanges.get(
"127.0.0.1:tabletLocator1").put(
new KeyExtent(
table,
new Text(
"ac"),
new Text(
"")),
Arrays.asList(
new Range(
"aa",
"ab"),
new Range(
"ab",
"ac")));
tserverBinnedRanges.get(
"127.0.0.1:tabletLocator1").put(
new KeyExtent(
table,
new Text(
"af"),
new Text(
"ac")),
Arrays.asList(
new Range(
"ac",
"ad"),
new Range(
"ad",
"ae"),
new Range(
"ae",
"af")));
tserverBinnedRanges.get(
"127.0.0.1:tabletLocator1").put(
new KeyExtent(
table,
new Text(
"ai"),
new Text(
"af")),
Arrays.asList(
new Range(
"af",
"ag"),
new Range(
"ag",
"ah"),
new Range(
"ah",
"ai")));
tserverBinnedRanges.get(
"127.0.0.1:tabletLocator1").put(
new KeyExtent(
table,
new Text(
"am"),
new Text(
"ai")),
Arrays.asList(
new Range(
"ai",
"aj"),
new Range(
"aj",
"ak"),
new Range(
"ak",
"al"),
new Range(
"al",
"am")));
// key extents for second tablet locator
tserverBinnedRanges.get(
"127.0.0.2:tabletLocator2").put(
new KeyExtent(
table,
new Text(
"ao"),
new Text(
"am")),
Arrays.asList(
new Range(
"am",
"an"),
new Range(
"an",
"ao")));
tserverBinnedRanges.get(
"127.0.0.2:tabletLocator2").put(
new KeyExtent(
table,
new Text(
"ar"),
new Text(
"ao")),
Arrays.asList(
new Range(
"ao",
"ap"),
new Range(
"ap",
"aq"),
new Range(
"aq",
"ar")));
tserverBinnedRanges.get(
"127.0.0.2:tabletLocator2").put(
new KeyExtent(
table,
new Text(
"au"),
new Text(
"ar")),
Arrays.asList(
new Range(
"ar",
"as"),
new Range(
"as",
"at"),
new Range(
"at",
"au")));
tserverBinnedRanges.get(
"127.0.0.2:tabletLocator2").put(
new KeyExtent(
table,
new Text(
"ay"),
new Text(
"au")),
Arrays.asList(
new Range(
"au",
"av"),
new Range(
"av",
"aw"),
new Range(
"aw",
"ax"),
new Range(
"ax",
"ay")));
tserverBinnedRanges.get(
"127.0.0.2:tabletLocator2").put(
new KeyExtent(
table,
new Text(
"bb"),
new Text(
"ay")),
Arrays.asList(
new Range(
"ay",
"az"),
new Range(
"az",
"ba"),
new Range(
"ba",
"bb")));
return tserverBinnedRanges;
}
/**
* Build our own host name cache, to avoid an unsuccessful lookup
* Expect two tablets, just use default localhost ip
*/
@Override
public HashMap<String, String> getHostNameCache() {
final HashMap<String, String> hostNameCache = new HashMap<String, String>();
hostNameCache.put(
"127.0.0.1",
"tabletLocator1");
hostNameCache.put(
"127.0.0.2",
"tabletLocator2");
return hostNameCache;
}
};
try {
QueryOptions queryOptions = new QueryOptions(
adapter,
index,
-1,
null,
new String[] {
"aaa",
"bbb"
});
Pair<PrimaryIndex, List<DataAdapter<Object>>> indexAdapterPair = queryOptions
.getAdaptersWithMinimalSetOfIndices(
adapterStore,
adapterIndexMappingStore,
indexStore)
.get(
0);
TreeSet<IntermediateSplitInfo> splitsInput = new TreeSet<IntermediateSplitInfo>();
TreeSet<IntermediateSplitInfo> splitsOutput;
splitsOutput = ((MockAccumuloSplitsProvider) splitsProvider).populateIntermediateSplits(
splitsInput,
accumuloOperations,
indexAdapterPair.getLeft(),
indexAdapterPair.getValue(),
statsCache,
adapterStore,
statsStore,
5,
query,
queryOptions.getAuthorizations());
verify(
tabletLocator).invalidateCache();
verify(
tabletLocator).binRanges(
isA(
//@formatter:off
/*if[accumulo.api=1.6]
Credentials.class
else[accumulo.api=1.6]*/
ClientContext.class
/*end[accumulo.api=1.6]*/
//@formatter:on
),
anyList(),
anyMap());
// if query is unsupported, return an empty split, with no error
assertThat(
splitsOutput.isEmpty(),
is(false));
assertThat(
splitsOutput.size(),
is(9));
IntermediateSplitInfo splitTest;
int countTablet1 = 0;
int countTablet2 = 0;
splitTest = splitsOutput.pollFirst();
// can't verify order of splits; I was getting different order
// depending on whether I ran or debugged
// instead, verify size of splits
while (splitTest != null) {
GeoWaveInputSplit finalSplitTest = splitTest.toFinalSplit();
if (finalSplitTest.getLocations()[0] == "tabletLocator1") {
countTablet1++;
}
else if (finalSplitTest.getLocations()[0] == "tabletLocator2") {
countTablet2++;
}
splitTest = splitsOutput.pollFirst();
}
assertThat(
countTablet1,
is(4));
assertThat(
countTablet2,
is(5));
}
catch (IOException e) {
e.printStackTrace();
}
catch (AccumuloException e) {
e.printStackTrace();
}
catch (AccumuloSecurityException e) {
e.printStackTrace();
}
catch (TableNotFoundException e) {
e.printStackTrace();
}
}
@Test
public void testWrapRange() {
final ByteBuffer startRow = ByteBuffer.allocate(8);
startRow.put("aaaaaaaa".getBytes());
startRow.rewind();
final ByteBuffer endRow = ByteBuffer.allocate(8);
endRow.put("bbbbbbbb".getBytes());
endRow.rewind();
final ByteBuffer colFamily = ByteBuffer.allocate(8);
colFamily.put("testing".getBytes());
colFamily.rewind();
final ByteBuffer colQualifier = ByteBuffer.allocate(8);
colQualifier.put("testing".getBytes());
colQualifier.rewind();
final ByteBuffer colVisibility = ByteBuffer.allocate(8);
colVisibility.put("testing".getBytes());
colVisibility.rewind();
TRange tRange = new TRange(
new TKey(
startRow,
colFamily,
colQualifier,
colVisibility,
new Date().getTime()),
new TKey(
endRow,
colFamily,
colQualifier,
colVisibility,
new Date().getTime()),
true,
false,
false,
false);
Map<String, Range> rangesToTest = new HashMap<String, Range>();
rangesToTest.put(
"emptyRange",
new Range());
rangesToTest.put(
"charSequenceRange1",
new Range(
"The quick fox jumps over the lazy dog"));
rangesToTest.put(
"textRange",
new Range(
new Text(
"The quick fox jumps over the lazy dog")));
rangesToTest.put(
"thriftRange",
new Range(
tRange));
rangesToTest.put(
"charSequenceRange2",
new Range(
"The quick fox jumps over the lazy dog",
"quick fox jumps over the lazy dog"));
rangesToTest.put(
"keyRange",
new Range(
new Key(
new Text(
"alpha")),
new Key(
new Text(
"epsilon"))));
for (String key : rangesToTest.keySet()) {
GeoWaveRowRange wrappedRange = AccumuloSplitsProvider.wrapRange(rangesToTest.get(key));
String wrappedStartKey = wrappedRange.getStartKey() != null ? new String(
wrappedRange.getStartKey()) : null;
String originalStartKey = rangesToTest.get(
key).getStartKey() != null ? rangesToTest.get(
key).getStartKey().getRow().toString() : null;
assertThat(
"StartKey test case failed: " + key,
wrappedStartKey,
is(originalStartKey));
String wrappedEndKey = wrappedRange.getEndKey() != null ? new String(
wrappedRange.getEndKey()) : null;
String originalEndKey = rangesToTest.get(
key).getEndKey() != null ? rangesToTest.get(
key).getEndKey().getRow().toString() : null;
assertThat(
"StartKey test case failed: " + key,
wrappedEndKey,
is(originalEndKey));
}
}
@Test
public void testUnwrapRanges() {
final ByteBuffer startRow = ByteBuffer.allocate(8);
startRow.put("aaaaaaaa".getBytes());
startRow.rewind();
final ByteBuffer endRow = ByteBuffer.allocate(8);
endRow.put("bbbbbbbb".getBytes());
endRow.rewind();
final ByteBuffer colFamily = ByteBuffer.allocate(8);
colFamily.put("testing".getBytes());
colFamily.rewind();
final ByteBuffer colQualifier = ByteBuffer.allocate(8);
colQualifier.put("testing".getBytes());
colQualifier.rewind();
final ByteBuffer colVisibility = ByteBuffer.allocate(8);
colVisibility.put("testing".getBytes());
colVisibility.rewind();
TRange tRange = new TRange(
new TKey(
startRow,
colFamily,
colQualifier,
colVisibility,
new Date().getTime()),
new TKey(
endRow,
colFamily,
colQualifier,
colVisibility,
new Date().getTime()),
true,
false,
false,
false);
Map<String, Range> rangesToTest = new HashMap<String, Range>();
rangesToTest.put(
"emptyRange",
new Range());
rangesToTest.put(
"charSequenceRange1",
new Range(
"The quick fox jumps over the lazy dog"));
rangesToTest.put(
"textRange",
new Range(
new Text(
"The quick fox jumps over the lazy dog")));
rangesToTest.put(
"thriftRange",
new Range(
tRange));
rangesToTest.put(
"charSequenceRange2",
new Range(
"The quick fox jumps over the lazy dog",
"quick fox jumps over the lazy dog"));
rangesToTest.put(
"keyRange",
new Range(
new Key(
new Text(
"alpha")),
new Key(
new Text(
"epsilon"))));
// easiest way to test unwrap is by verifying an unwrapped wrap is equal
// to itself
for (String key : rangesToTest.keySet()) {
assertThat(
rangesToTest.get(key),
is(AccumuloSplitsProvider.unwrapRange(AccumuloSplitsProvider.wrapRange(rangesToTest.get(key)))));
}
}
protected static class TestGeometry
{
private final Geometry geom;
private final String id;
public TestGeometry(
final Geometry geom,
final String id ) {
this.geom = geom;
this.id = id;
}
}
protected static class TestGeometryAdapter extends
AbstractDataAdapter<TestGeometry> implements
StatisticsProvider<TestGeometry>
{
private static final ByteArrayId GEOM = new ByteArrayId(
"myGeo");
private static final ByteArrayId ID = new ByteArrayId(
"myId");
private static final PersistentIndexFieldHandler<TestGeometry, ? extends CommonIndexValue, Object> GEOM_FIELD_HANDLER = new PersistentIndexFieldHandler<TestGeometry, CommonIndexValue, Object>() {
@Override
public ByteArrayId[] getNativeFieldIds() {
return new ByteArrayId[] {
GEOM
};
}
@Override
public CommonIndexValue toIndexValue(
final TestGeometry row ) {
return new GeometryWrapper(
row.geom,
new byte[0]);
}
@SuppressWarnings("unchecked")
@Override
public PersistentValue<Object>[] toNativeValues(
final CommonIndexValue indexValue ) {
return new PersistentValue[] {
new PersistentValue<Object>(
GEOM,
((GeometryWrapper) indexValue).getGeometry())
};
}
@Override
public byte[] toBinary() {
return new byte[0];
}
@Override
public void fromBinary(
final byte[] bytes ) {
}
};
private final static EntryVisibilityHandler<TestGeometry> GEOMETRY_VISIBILITY_HANDLER = new FieldTypeStatisticVisibility<TestGeometry>(
GeometryWrapper.class);
private static final NativeFieldHandler<TestGeometry, Object> ID_FIELD_HANDLER = new NativeFieldHandler<TestGeometry, Object>() {
@Override
public ByteArrayId getFieldId() {
return ID;
}
@Override
public Object getFieldValue(
final TestGeometry row ) {
return row.id;
}
};
private static final List<NativeFieldHandler<TestGeometry, Object>> NATIVE_FIELD_HANDLER_LIST = new ArrayList<NativeFieldHandler<TestGeometry, Object>>();
private static final List<PersistentIndexFieldHandler<TestGeometry, ? extends CommonIndexValue, Object>> COMMON_FIELD_HANDLER_LIST = new ArrayList<PersistentIndexFieldHandler<TestGeometry, ? extends CommonIndexValue, Object>>();
static {
COMMON_FIELD_HANDLER_LIST.add(GEOM_FIELD_HANDLER);
NATIVE_FIELD_HANDLER_LIST.add(ID_FIELD_HANDLER);
}
public TestGeometryAdapter() {
super(
COMMON_FIELD_HANDLER_LIST,
NATIVE_FIELD_HANDLER_LIST);
}
@Override
public ByteArrayId getAdapterId() {
return new ByteArrayId(
"test");
}
@Override
public boolean isSupported(
final TestGeometry entry ) {
return true;
}
@Override
public ByteArrayId getDataId(
final TestGeometry entry ) {
return new ByteArrayId(
entry.id);
}
@SuppressWarnings("unchecked")
@Override
public FieldReader getReader(
final ByteArrayId fieldId ) {
if (fieldId.equals(GEOM)) {
return FieldUtils.getDefaultReaderForClass(Geometry.class);
}
else if (fieldId.equals(ID)) {
return FieldUtils.getDefaultReaderForClass(String.class);
}
return null;
}
@Override
public FieldWriter getWriter(
final ByteArrayId fieldId ) {
if (fieldId.equals(GEOM)) {
return FieldUtils.getDefaultWriterForClass(Geometry.class);
}
else if (fieldId.equals(ID)) {
return FieldUtils.getDefaultWriterForClass(String.class);
}
return null;
}
@Override
public DataStatistics<TestGeometry> createDataStatistics(
final ByteArrayId statisticsId ) {
if (BoundingBoxDataStatistics.STATS_TYPE.equals(statisticsId)) {
return new GeoBoundingBoxStatistics(
getAdapterId());
}
else if (CountDataStatistics.STATS_TYPE.equals(statisticsId)) {
return new CountDataStatistics<TestGeometry>(
getAdapterId());
}
LOGGER.warn("Unrecognized statistics ID " + statisticsId.getString() + " using count statistic");
return new CountDataStatistics<TestGeometry>(
getAdapterId(),
statisticsId);
}
@Override
public EntryVisibilityHandler<TestGeometry> getVisibilityHandler(
final ByteArrayId statisticsId ) {
return GEOMETRY_VISIBILITY_HANDLER;
}
@Override
protected RowBuilder newBuilder() {
return new RowBuilder<TestGeometry, Object>() {
private String id;
private Geometry geom;
@Override
public void setField(
final PersistentValue<Object> fieldValue ) {
if (fieldValue.getId().equals(
GEOM)) {
geom = (Geometry) fieldValue.getValue();
}
else if (fieldValue.getId().equals(
ID)) {
id = (String) fieldValue.getValue();
}
}
@Override
public TestGeometry buildRow(
final ByteArrayId dataId ) {
return new TestGeometry(
geom,
id);
}
};
}
@Override
public ByteArrayId[] getSupportedStatisticsTypes() {
return SUPPORTED_STATS_IDS;
}
@Override
public int getPositionOfOrderedField(
final CommonIndexModel model,
final ByteArrayId fieldId ) {
int i = 0;
for (final NumericDimensionField<? extends CommonIndexValue> dimensionField : model.getDimensions()) {
if (fieldId.equals(dimensionField.getFieldId())) {
return i;
}
i++;
}
if (fieldId.equals(GEOM)) {
return i;
}
else if (fieldId.equals(ID)) {
return i + 1;
}
return -1;
}
@Override
public ByteArrayId getFieldIdForPosition(
final CommonIndexModel model,
final int position ) {
if (position < model.getDimensions().length) {
int i = 0;
for (final NumericDimensionField<? extends CommonIndexValue> dimensionField : model.getDimensions()) {
if (i == position) {
return dimensionField.getFieldId();
}
i++;
}
}
else {
final int numDimensions = model.getDimensions().length;
if (position == numDimensions) {
return GEOM;
}
else if (position == (numDimensions + 1)) {
return ID;
}
}
return null;
}
}
private final static ByteArrayId[] SUPPORTED_STATS_IDS = new ByteArrayId[] {
BoundingBoxDataStatistics.STATS_TYPE,
CountDataStatistics.STATS_TYPE
};
private static class GeoBoundingBoxStatistics extends
BoundingBoxDataStatistics<TestGeometry>
{
@SuppressWarnings("unused")
protected GeoBoundingBoxStatistics() {
super();
}
public GeoBoundingBoxStatistics(
final ByteArrayId dataAdapterId ) {
super(
dataAdapterId);
}
@Override
protected Envelope getEnvelope(
final TestGeometry entry ) {
// incorporate the bounding box of the entry's envelope
final Geometry geometry = entry.geom;
if ((geometry != null) && !geometry.isEmpty()) {
return geometry.getEnvelopeInternal();
}
return null;
}
}
private abstract static class MockAccumuloSplitsProvider extends
AccumuloSplitsProvider
{
private GeoWaveRowRange rangeMax;
private TabletLocator mockTabletLocator;
public MockAccumuloSplitsProvider(
TabletLocator tabletLocator ) {
super();
this.mockTabletLocator = tabletLocator;
}
public MockAccumuloSplitsProvider(
TabletLocator tabletLocator,
GeoWaveRowRange rangeMax ) {
super();
this.rangeMax = rangeMax;
this.mockTabletLocator = tabletLocator;
}
public abstract void addMocks();
/**
* Return a mocked out TabletLocator to avoid having to look up a
* TabletLocator, which fails
*
*/
@Override
protected TabletLocator getTabletLocator(
final Object clientContextOrInstance,
final String tableId )
throws TableNotFoundException {
addMocks();
return mockTabletLocator;
}
/**
* Returns a rangeMax, instead of going through the statistics store to
* get it
*/
@Override
protected GeoWaveRowRange getRangeMax(
final Index<?, ?> index,
final AdapterStore adapterStore,
final DataStatisticsStore statsStore,
final String[] authorizations ) {
return this.rangeMax;
};
@Override
public TreeSet<IntermediateSplitInfo> populateIntermediateSplits(
final TreeSet<IntermediateSplitInfo> splits,
final DataStoreOperations operations,
final PrimaryIndex index,
final List<DataAdapter<Object>> adapters,
final Map<PrimaryIndex, RowRangeHistogramStatistics<?>> statsCache,
final AdapterStore adapterStore,
final DataStatisticsStore statsStore,
final Integer maxSplits,
final DistributableQuery query,
final String[] authorizations )
throws IOException {
return super.populateIntermediateSplits(
splits,
operations,
index,
adapters,
statsCache,
adapterStore,
statsStore,
maxSplits,
query,
authorizations);
}
}
}