package mil.nga.giat.geowave.datastore.accumulo.util; import java.io.IOException; import java.math.BigDecimal; import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import org.apache.accumulo.core.client.AccumuloException; import org.apache.accumulo.core.client.AccumuloSecurityException; import org.apache.accumulo.core.client.BatchScanner; import org.apache.accumulo.core.client.Connector; import org.apache.accumulo.core.client.IteratorSetting; import org.apache.accumulo.core.client.ScannerBase; import org.apache.accumulo.core.client.TableNotFoundException; import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Mutation; import org.apache.accumulo.core.data.Range; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.IteratorUtil.IteratorScope; import org.apache.accumulo.core.iterators.user.WholeRowIterator; import org.apache.accumulo.core.security.ColumnVisibility; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.apache.hadoop.io.Text; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.ByteArrayRange; import mil.nga.giat.geowave.core.index.StringUtils; import mil.nga.giat.geowave.core.index.simple.RoundRobinKeyIndexStrategy; import mil.nga.giat.geowave.core.store.CloseableIterator; import mil.nga.giat.geowave.core.store.CloseableIteratorWrapper; import mil.nga.giat.geowave.core.store.adapter.AdapterPersistenceEncoding; 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.IndexedAdapterPersistenceEncoding; import mil.nga.giat.geowave.core.store.adapter.RowMergingDataAdapter; import mil.nga.giat.geowave.core.store.adapter.RowMergingDataAdapter.RowTransform; import mil.nga.giat.geowave.core.store.base.DataStoreEntryInfo; import mil.nga.giat.geowave.core.store.base.Writer; import mil.nga.giat.geowave.core.store.base.DataStoreEntryInfo.FieldInfo; import mil.nga.giat.geowave.core.store.callback.ScanCallback; import mil.nga.giat.geowave.core.store.adapter.WritableDataAdapter; import mil.nga.giat.geowave.core.store.data.PersistentDataset; import mil.nga.giat.geowave.core.store.data.PersistentValue; import mil.nga.giat.geowave.core.store.data.VisibilityWriter; import mil.nga.giat.geowave.core.store.data.field.FieldReader; import mil.nga.giat.geowave.core.store.entities.GeowaveRowId; import mil.nga.giat.geowave.core.store.filter.DedupeFilter; import mil.nga.giat.geowave.core.store.filter.FilterList; import mil.nga.giat.geowave.core.store.filter.QueryFilter; import mil.nga.giat.geowave.core.store.flatten.BitmaskUtils; import mil.nga.giat.geowave.core.store.flatten.FlattenedDataSet; import mil.nga.giat.geowave.core.store.flatten.FlattenedFieldInfo; import mil.nga.giat.geowave.core.store.flatten.FlattenedUnreadDataSingleRow; 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.IndexStore; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.metadata.AbstractGeowavePersistence; import mil.nga.giat.geowave.core.store.util.DataStoreUtils; import mil.nga.giat.geowave.datastore.accumulo.AccumuloOperations; import mil.nga.giat.geowave.datastore.accumulo.AccumuloRowId; import mil.nga.giat.geowave.datastore.accumulo.BasicAccumuloOperations; import mil.nga.giat.geowave.datastore.accumulo.IteratorConfig; import mil.nga.giat.geowave.datastore.accumulo.IteratorConfig.OptionProvider; import mil.nga.giat.geowave.datastore.accumulo.RowMergingAdapterOptionProvider; import mil.nga.giat.geowave.datastore.accumulo.RowMergingCombiner; import mil.nga.giat.geowave.datastore.accumulo.RowMergingVisibilityCombiner; import mil.nga.giat.geowave.datastore.accumulo.metadata.AbstractAccumuloPersistence; import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloAdapterStore; import mil.nga.giat.geowave.datastore.accumulo.metadata.AccumuloIndexStore; import mil.nga.giat.geowave.datastore.accumulo.operations.config.AccumuloOptions; import mil.nga.giat.geowave.datastore.accumulo.query.AccumuloConstraintsQuery; /** * A set of convenience methods for common operations on Accumulo within * GeoWave, such as conversions between GeoWave objects and corresponding * Accumulo objects. * */ public class AccumuloUtils { private final static Logger LOGGER = LoggerFactory.getLogger(AccumuloUtils.class); private static final String ROW_MERGING_SUFFIX = "_COMBINER"; private static final String ROW_MERGING_VISIBILITY_SUFFIX = "_VISIBILITY_COMBINER"; public static Range byteArrayRangeToAccumuloRange( final ByteArrayRange byteArrayRange ) { if (byteArrayRange.isSingleValue()) { return Range.exact(new Text( byteArrayRange.getStart().getBytes())); } final Text start = new Text( byteArrayRange.getStart().getBytes()); final Text end = new Text( byteArrayRange.getEnd().getBytes()); if (start.compareTo(end) > 0) { return null; } return new Range( new Text( byteArrayRange.getStart().getBytes()), true, Range.followingPrefix(new Text( byteArrayRange.getEnd().getBytes())), false); } public static TreeSet<Range> byteArrayRangesToAccumuloRanges( final List<ByteArrayRange> byteArrayRanges ) { if (byteArrayRanges == null) { final TreeSet<Range> range = new TreeSet<Range>(); range.add(new Range()); return range; } final TreeSet<Range> accumuloRanges = new TreeSet<Range>(); for (final ByteArrayRange byteArrayRange : byteArrayRanges) { final Range range = byteArrayRangeToAccumuloRange(byteArrayRange); if (range == null) { continue; } accumuloRanges.add(range); } if (accumuloRanges.isEmpty()) { // implies full table scan accumuloRanges.add(new Range()); } return accumuloRanges; } public static String getQualifiedTableName( final String tableNamespace, final String unqualifiedTableName ) { return ((tableNamespace == null) || tableNamespace.isEmpty()) ? unqualifiedTableName : tableNamespace + "_" + unqualifiedTableName; } @SuppressWarnings("unchecked") public static <T> T decodeRow( final Key key, final Value value, final boolean wholeRowEncoding, final AdapterStore adapterStore, final QueryFilter clientFilter, final PrimaryIndex index, final ScanCallback<T> scanCallback ) { final GeowaveRowId rowId = new GeowaveRowId( key.getRow().copyBytes()); return (T) decodeRowObj( key, value, wholeRowEncoding, rowId, null, adapterStore, clientFilter, index, scanCallback); } public static Object decodeRow( final Key key, final Value value, final boolean wholeRowEncoding, final GeowaveRowId rowId, final AdapterStore adapterStore, final QueryFilter clientFilter, final PrimaryIndex index ) { return decodeRowObj( key, value, wholeRowEncoding, rowId, null, adapterStore, clientFilter, index, null); } private static <T> Object decodeRowObj( final Key key, final Value value, final boolean wholeRowEncoding, final GeowaveRowId rowId, final DataAdapter<T> dataAdapter, final AdapterStore adapterStore, final QueryFilter clientFilter, final PrimaryIndex index, final ScanCallback<T> scanCallback ) { final Pair<T, DataStoreEntryInfo> pair = decodeRow( key, value, wholeRowEncoding, rowId, dataAdapter, adapterStore, clientFilter, index, scanCallback); return pair != null ? pair.getLeft() : null; } @SuppressWarnings("unchecked") public static <T> Pair<T, DataStoreEntryInfo> decodeRow( final Key k, final Value v, final boolean wholeRowEncoding, final GeowaveRowId rowId, final DataAdapter<T> dataAdapter, final AdapterStore adapterStore, final QueryFilter clientFilter, final PrimaryIndex index, final ScanCallback<T> scanCallback ) { if ((dataAdapter == null) && (adapterStore == null)) { LOGGER.error("Could not decode row from iterator. Either adapter or adapter store must be non-null."); return null; } DataAdapter<T> adapter = dataAdapter; Map<Key, Value> rowMapping; if (wholeRowEncoding) { try { rowMapping = WholeRowIterator.decodeRow( k, v); } catch (final IOException e) { LOGGER.error( "Could not decode row from iterator. Ensure whole row iterators are being used.", e); return null; } } else { rowMapping = new HashMap<Key, Value>(); rowMapping.put( k, v); } // build a persistence encoding object first, pass it through the // client filters and if its accepted, use the data adapter to // decode the persistence model into the native data type final PersistentDataset<CommonIndexValue> indexData = new PersistentDataset<CommonIndexValue>(); final PersistentDataset<Object> extendedData = new PersistentDataset<Object>(); final PersistentDataset<byte[]> unknownData = new PersistentDataset<byte[]>(); // for now we are assuming all entries in a row are of the same type // and use the same adapter boolean adapterMatchVerified; ByteArrayId adapterId; if (adapter != null) { adapterId = adapter.getAdapterId(); adapterMatchVerified = false; } else { adapterMatchVerified = true; adapterId = null; } final List<FieldInfo<?>> fieldInfoList = new ArrayList<FieldInfo<?>>( rowMapping.size()); for (final Entry<Key, Value> entry : rowMapping.entrySet()) { // the column family is the data element's type ID if (adapterId == null) { adapterId = new ByteArrayId( entry.getKey().getColumnFamilyData().getBackingArray()); } if (adapter == null) { adapter = (DataAdapter<T>) adapterStore.getAdapter(adapterId); if (adapter == null) { LOGGER.error("DataAdapter does not exist"); return null; } } if (!adapterMatchVerified) { if (!adapterId.equals(adapter.getAdapterId())) { return null; } adapterMatchVerified = true; } final CommonIndexModel indexModel = index.getIndexModel(); final byte[] byteValue = entry.getValue().get(); DataStoreUtils.readFieldInfo( fieldInfoList, indexData, extendedData, unknownData, entry.getKey().getColumnQualifierData().getBackingArray(), entry.getKey().getColumnVisibilityData().getBackingArray(), byteValue, adapter, indexModel); } final IndexedAdapterPersistenceEncoding encodedRow = new IndexedAdapterPersistenceEncoding( adapterId, new ByteArrayId( rowId.getDataId()), new ByteArrayId( rowId.getInsertionId()), rowId.getNumberOfDuplicates(), indexData, unknownData, extendedData); if ((clientFilter == null) || clientFilter.accept( index.getIndexModel(), encodedRow)) { // cannot get here unless adapter is found (not null) if (adapter == null) { LOGGER.error("Error, adapter was null when it should not be"); } else { final Pair<T, DataStoreEntryInfo> pair = Pair.of( adapter.decode( encodedRow, index), new DataStoreEntryInfo( rowId.getDataId(), Arrays.asList(new ByteArrayId( rowId.getInsertionId())), Arrays.asList(new ByteArrayId( k.getRowData().getBackingArray())), fieldInfoList)); if (scanCallback != null) { scanCallback.entryScanned( pair.getRight(), pair.getLeft()); } return pair; } } return null; } public static <T> DataStoreEntryInfo write( final WritableDataAdapter<T> writableAdapter, final PrimaryIndex index, final T entry, final Writer writer, final AccumuloOperations operations, final VisibilityWriter<T> customFieldVisibilityWriter ) { // we need to make sure at least this user has authorization // on the visibility that is being written try { final DataStoreEntryInfo ingestInfo = DataStoreUtils.getIngestInfo( writableAdapter, index, entry, customFieldVisibilityWriter); if (customFieldVisibilityWriter != DataStoreUtils.UNCONSTRAINED_VISIBILITY) { for (final FieldInfo field : ingestInfo.getFieldInfo()) { if ((field.getVisibility() != null) && (field.getVisibility().length > 0)) { operations.insureAuthorization( null, StringUtils.stringFromBinary(field.getVisibility())); } } } final List<Mutation> mutations = buildMutations( writableAdapter.getAdapterId().getBytes(), ingestInfo, index, writableAdapter); writer.write(mutations); return ingestInfo; } catch (AccumuloException | AccumuloSecurityException e) { LOGGER.warn( "Unable to add user authorization", e); } return null; } private static <T> List<Mutation> buildMutations( final byte[] adapterId, final DataStoreEntryInfo ingestInfo, final PrimaryIndex index, final WritableDataAdapter<T> writableAdapter ) { final List<Mutation> mutations = new ArrayList<Mutation>(); final List<FieldInfo<?>> fieldInfoList = DataStoreUtils.composeFlattenedFields( ingestInfo.getFieldInfo(), index.getIndexModel(), writableAdapter); for (final ByteArrayId rowId : ingestInfo.getRowIds()) { final Mutation mutation = new Mutation( new Text( rowId.getBytes())); for (final FieldInfo<?> fieldInfo : fieldInfoList) { if (fieldInfo.getVisibility() != null && fieldInfo.getVisibility().length > 0) { mutation.put( new Text( adapterId), new Text( fieldInfo.getDataValue().getId().getBytes()), new ColumnVisibility( fieldInfo.getVisibility()), new Value( fieldInfo.getWrittenValue())); } else { mutation.put( new Text( adapterId), new Text( fieldInfo.getDataValue().getId().getBytes()), new Value( fieldInfo.getWrittenValue())); } } mutations.add(mutation); } return mutations; } /** * * @param dataWriter * @param index * @param entry * @return List of zero or more matches */ public static <T> List<ByteArrayId> getRowIds( final WritableDataAdapter<T> dataWriter, final PrimaryIndex index, final T entry ) { final CommonIndexModel indexModel = index.getIndexModel(); final AdapterPersistenceEncoding encodedData = dataWriter.encode( entry, indexModel); final List<ByteArrayId> insertionIds = encodedData.getInsertionIds(index); final List<ByteArrayId> rowIds = new ArrayList<ByteArrayId>( insertionIds.size()); DataStoreUtils.addToRowIds( rowIds, insertionIds, dataWriter.getDataId( entry).getBytes(), dataWriter.getAdapterId().getBytes(), encodedData.isDeduplicationEnabled()); return rowIds; } /** * Get Namespaces * * @param connector */ public static List<String> getNamespaces( final Connector connector ) { final List<String> namespaces = new ArrayList<String>(); for (final String table : connector.tableOperations().list()) { final int idx = table.indexOf(AbstractGeowavePersistence.METADATA_TABLE) - 1; if (idx > 0) { namespaces.add(table.substring( 0, idx)); } } return namespaces; } /** * Get list of data adapters associated with the given namespace * * @param connector * @param namespace */ public static List<DataAdapter<?>> getDataAdapters( final Connector connector, final String namespace ) { final List<DataAdapter<?>> adapters = new ArrayList<DataAdapter<?>>(); final AdapterStore adapterStore = new AccumuloAdapterStore( new BasicAccumuloOperations( connector, namespace)); try (final CloseableIterator<DataAdapter<?>> itr = adapterStore.getAdapters()) { while (itr.hasNext()) { adapters.add(itr.next()); } } catch (final IOException e) { LOGGER.error( "Unable to close iterator", e); } return adapters; } /** * Get list of indices associated with the given namespace * * @param connector * @param namespace */ public static List<Index<?, ?>> getIndices( final Connector connector, final String namespace ) { final List<Index<?, ?>> indices = new ArrayList<Index<?, ?>>(); final IndexStore indexStore = new AccumuloIndexStore( new BasicAccumuloOperations( connector, namespace)); try (final CloseableIterator<Index<?, ?>> itr = indexStore.getIndices()) { while (itr.hasNext()) { indices.add(itr.next()); } } catch (final IOException e) { LOGGER.error( "Unable to close iterator", e); } return indices; } /** * Set splits on a table based on a partition ID * * @param namespace * @param index * @param randomParitions * number of partition IDs * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException * @throws TableNotFoundException */ public static void setSplitsByRandomPartitions( final Connector connector, final String namespace, final PrimaryIndex index, final int randomPartitions ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { final AccumuloOperations operations = new BasicAccumuloOperations( connector, namespace); final RoundRobinKeyIndexStrategy partitions = new RoundRobinKeyIndexStrategy( randomPartitions); operations.createTable( index.getId().getString(), true, true, partitions.getNaturalSplits()); } /** * Set splits on a table based on quantile distribution and fixed number of * splits * * @param namespace * @param index * @param quantile * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException * @throws TableNotFoundException */ public static void setSplitsByQuantile( final Connector connector, final String namespace, final PrimaryIndex index, final int quantile ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { final long count = getEntries( connector, namespace, index); try (final CloseableIterator<Entry<Key, Value>> iterator = getIterator( connector, namespace, index)) { if (iterator == null) { LOGGER.error("Could not get iterator instance, getIterator returned null"); throw new IOException( "Could not get iterator instance, getIterator returned null"); } long ii = 0; final long splitInterval = (long) Math.ceil((double) count / (double) quantile); final SortedSet<Text> splits = new TreeSet<Text>(); while (iterator.hasNext()) { final Entry<Key, Value> entry = iterator.next(); ii++; if (ii >= splitInterval) { ii = 0; splits.add(entry.getKey().getRow()); } } final String tableName = AccumuloUtils.getQualifiedTableName( namespace, index.getId().getString()); connector.tableOperations().addSplits( tableName, splits); connector.tableOperations().compact( tableName, null, null, true, true); } } /** * Set splits on table based on equal interval distribution and fixed number * of splits. * * @param namespace * @param index * @param numberSplits * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException * @throws TableNotFoundException */ public static void setSplitsByNumSplits( final Connector connector, final String namespace, final PrimaryIndex index, final int numSplits ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { final SortedSet<Text> splits = new TreeSet<Text>(); try (final CloseableIterator<Entry<Key, Value>> iterator = getIterator( connector, namespace, index)) { if (iterator == null) { LOGGER.error("could not get iterator instance, getIterator returned null"); throw new IOException( "could not get iterator instance, getIterator returned null"); } final int numberSplits = numSplits - 1; BigInteger min = null; BigInteger max = null; while (iterator.hasNext()) { final Entry<Key, Value> entry = iterator.next(); final byte[] bytes = entry.getKey().getRow().getBytes(); final BigInteger value = new BigInteger( bytes); if ((min == null) || (max == null)) { min = value; max = value; } min = min.min(value); max = max.max(value); } if ((min != null) && (max != null)) { final BigDecimal dMax = new BigDecimal( max); final BigDecimal dMin = new BigDecimal( min); BigDecimal delta = dMax.subtract(dMin); delta = delta.divideToIntegralValue(new BigDecimal( numSplits)); for (int ii = 1; ii <= numberSplits; ii++) { final BigDecimal temp = delta.multiply(BigDecimal.valueOf(ii)); final BigInteger value = min.add(temp.toBigInteger()); final Text split = new Text( value.toByteArray()); splits.add(split); } } final String tableName = AccumuloUtils.getQualifiedTableName( namespace, StringUtils.stringFromBinary(index.getId().getBytes())); connector.tableOperations().addSplits( tableName, splits); connector.tableOperations().compact( tableName, null, null, true, true); } } /** * Set splits on table based on fixed number of rows per split. * * @param namespace * @param index * @param numberRows * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException * @throws TableNotFoundException */ public static void setSplitsByNumRows( final Connector connector, final String namespace, final PrimaryIndex index, final long numberRows ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { try (final CloseableIterator<Entry<Key, Value>> iterator = getIterator( connector, namespace, index)) { if (iterator == null) { LOGGER.error("Unable to get iterator instance, getIterator returned null"); throw new IOException( "Unable to get iterator instance, getIterator returned null"); } long ii = 0; final SortedSet<Text> splits = new TreeSet<Text>(); while (iterator.hasNext()) { final Entry<Key, Value> entry = iterator.next(); ii++; if (ii >= numberRows) { ii = 0; splits.add(entry.getKey().getRow()); } } final String tableName = AccumuloUtils.getQualifiedTableName( namespace, StringUtils.stringFromBinary(index.getId().getBytes())); connector.tableOperations().addSplits( tableName, splits); connector.tableOperations().compact( tableName, null, null, true, true); } } /** * Check if locality group is set. * * @param namespace * @param index * @param adapter * @return * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException * @throws TableNotFoundException */ public static boolean isLocalityGroupSet( final Connector connector, final String namespace, final PrimaryIndex index, final DataAdapter<?> adapter ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { final AccumuloOperations operations = new BasicAccumuloOperations( connector, namespace); // get unqualified table name final String tableName = StringUtils.stringFromBinary(index.getId().getBytes()); return operations.localityGroupExists( tableName, adapter.getAdapterId().getBytes()); } /** * Set locality group. * * @param namespace * @param index * @param adapter * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException * @throws TableNotFoundException */ public static void setLocalityGroup( final Connector connector, final String namespace, final PrimaryIndex index, final DataAdapter<?> adapter ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { final AccumuloOperations operations = new BasicAccumuloOperations( connector, namespace); // get unqualified table name final String tableName = StringUtils.stringFromBinary(index.getId().getBytes()); operations.addLocalityGroup( tableName, adapter.getAdapterId().getBytes()); } /** * Get number of entries for a data adapter in an index. * * @param namespace * @param index * @param adapter * @return * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException */ public static long getEntries( final Connector connector, final String namespace, final PrimaryIndex index, final DataAdapter<?> adapter ) throws AccumuloException, AccumuloSecurityException, IOException { long counter = 0L; final AccumuloOperations operations = new BasicAccumuloOperations( connector, namespace); final AccumuloIndexStore indexStore = new AccumuloIndexStore( operations); final AccumuloAdapterStore adapterStore = new AccumuloAdapterStore( operations); if (indexStore.indexExists(index.getId()) && adapterStore.adapterExists(adapter.getAdapterId())) { final List<ByteArrayId> adapterIds = new ArrayList<>(); adapterIds.add(adapter.getAdapterId()); final AccumuloConstraintsQuery accumuloQuery = new AccumuloConstraintsQuery( adapterIds, index, null, null, null, null, null, null, null, null, new String[0]); final CloseableIterator<?> iterator = accumuloQuery.query( operations, new AccumuloAdapterStore( operations), null, null); while (iterator.hasNext()) { counter++; iterator.next(); } iterator.close(); } return counter; } /** * * Get number of entries per index. * * @param namespace * @param index * @return * @throws AccumuloException * @throws AccumuloSecurityException * @throws IOException */ public static long getEntries( final Connector connector, final String namespace, final PrimaryIndex index ) throws AccumuloException, AccumuloSecurityException, IOException { long counter = 0L; final AccumuloOperations operations = new BasicAccumuloOperations( connector, namespace); final AccumuloIndexStore indexStore = new AccumuloIndexStore( operations); if (indexStore.indexExists(index.getId())) { final AccumuloConstraintsQuery accumuloQuery = new AccumuloConstraintsQuery( null, index, null, null, null, null, null, null, null, null, new String[0]); final CloseableIterator<?> iterator = accumuloQuery.query( operations, new AccumuloAdapterStore( operations), null, null); while (iterator.hasNext()) { counter++; iterator.next(); } iterator.close(); } return counter; } public static void attachRowMergingIterators( final RowMergingDataAdapter<?, ?> adapter, final AccumuloOperations operations, final AccumuloOptions options, final Set<ByteArrayId> splits, final String tableName ) throws TableNotFoundException { final RowTransform rowTransform = adapter.getTransform(); if (rowTransform != null) { final EnumSet<IteratorScope> visibilityCombinerScope = EnumSet.of(IteratorScope.scan); final OptionProvider optionProvider = new RowMergingAdapterOptionProvider( adapter); final IteratorConfig rowMergingCombinerConfig = new IteratorConfig( EnumSet.complementOf(visibilityCombinerScope), rowTransform.getBaseTransformPriority(), rowTransform.getTransformName() + ROW_MERGING_SUFFIX, RowMergingCombiner.class.getName(), optionProvider); final IteratorConfig rowMergingVisibilityCombinerConfig = new IteratorConfig( visibilityCombinerScope, rowTransform.getBaseTransformPriority() + 1, rowTransform.getTransformName() + ROW_MERGING_VISIBILITY_SUFFIX, RowMergingVisibilityCombiner.class.getName(), optionProvider); operations.attachIterators( tableName, options.isCreateTable(), true, options.isEnableBlockCache(), splits, rowMergingCombinerConfig, rowMergingVisibilityCombinerConfig); } } private static CloseableIterator<Entry<Key, Value>> getIterator( final Connector connector, final String namespace, final PrimaryIndex index ) throws AccumuloException, AccumuloSecurityException, IOException, TableNotFoundException { CloseableIterator<Entry<Key, Value>> iterator = null; final AccumuloOperations operations = new BasicAccumuloOperations( connector, namespace); final AccumuloIndexStore indexStore = new AccumuloIndexStore( operations); final AccumuloAdapterStore adapterStore = new AccumuloAdapterStore( operations); if (indexStore.indexExists(index.getId())) { final ScannerBase scanner = operations.createBatchScanner(index.getId().getString()); ((BatchScanner) scanner).setRanges(AccumuloUtils.byteArrayRangesToAccumuloRanges(null)); final IteratorSetting iteratorSettings = new IteratorSetting( 10, "GEOWAVE_WHOLE_ROW_ITERATOR", WholeRowIterator.class); scanner.addScanIterator(iteratorSettings); final Iterator<Entry<Key, Value>> it = new IteratorWrapper( adapterStore, index, scanner.iterator(), new DedupeFilter()); iterator = new CloseableIteratorWrapper<Entry<Key, Value>>( new ScannerClosableWrapper( scanner), it); } return iterator; } private static class IteratorWrapper implements Iterator<Entry<Key, Value>> { private final Iterator<Entry<Key, Value>> scannerIt; private final AdapterStore adapterStore; private final PrimaryIndex index; private final QueryFilter clientFilter; private Entry<Key, Value> nextValue; public IteratorWrapper( final AdapterStore adapterStore, final PrimaryIndex index, final Iterator<Entry<Key, Value>> scannerIt, final QueryFilter clientFilter ) { this.adapterStore = adapterStore; this.index = index; this.scannerIt = scannerIt; this.clientFilter = clientFilter; findNext(); } private void findNext() { while (scannerIt.hasNext()) { final Entry<Key, Value> row = scannerIt.next(); final Object decodedValue = decodeRow( row, clientFilter, index); if (decodedValue != null) { nextValue = row; return; } } nextValue = null; } private Object decodeRow( final Entry<Key, Value> row, final QueryFilter clientFilter, final PrimaryIndex index ) { return AccumuloUtils.decodeRow( row.getKey(), row.getValue(), true, new AccumuloRowId( row.getKey()), // need to pass this, otherwise null // value for rowId gets dereferenced // later adapterStore, clientFilter, index); } @Override public boolean hasNext() { return nextValue != null; } @Override public Entry<Key, Value> next() { final Entry<Key, Value> previousNext = nextValue; findNext(); return previousNext; } @Override public void remove() {} } }