package mil.nga.giat.geowave.datastore.hbase.metadata; import java.nio.ByteBuffer; import java.util.Iterator; import org.apache.commons.lang.NotImplementedException; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.store.CloseableIterator; 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.datastore.hbase.operations.BasicHBaseOperations; import mil.nga.giat.geowave.datastore.hbase.util.HBaseUtils; public class HBaseDataStatisticsStore extends AbstractHBasePersistence<DataStatistics<?>> implements DataStatisticsStore { protected static final String STATISTICS_CF = "STATS"; private final static Logger LOGGER = LoggerFactory.getLogger(HBaseDataStatisticsStore.class); public HBaseDataStatisticsStore( final BasicHBaseOperations operations ) { super( operations); } @Override public void setStatistics( final DataStatistics<?> statistics ) { removeStatistics( statistics.getDataAdapterId(), statistics.getStatisticsId()); addObject(statistics); } @Override public void incorporateStatistics( final DataStatistics<?> statistics ) { addObject(statistics); } @Override public CloseableIterator<DataStatistics<?>> getDataStatistics( final ByteArrayId adapterId, final String... authorizations ) { return getAllObjectsWithSecondaryId( adapterId, authorizations); } @Override public CloseableIterator<DataStatistics<?>> getAllDataStatistics( final String... authorizations ) { return getObjects(authorizations); } @Override public DataStatistics<?> getDataStatistics( final ByteArrayId adapterId, final ByteArrayId statisticsId, final String... authorizations ) { return getObject( statisticsId, adapterId, authorizations); } @Override public boolean removeStatistics( final ByteArrayId adapterId, final ByteArrayId statisticsId, final String... authorizations ) { if (statisticsId == null) { LOGGER.error("No statistics id specified for removeStatistics, ignoring request!"); return false; } return deleteObject( statisticsId, adapterId, authorizations); } @Override protected ByteArrayId getPrimaryId( final DataStatistics<?> persistedObject ) { return persistedObject.getStatisticsId(); } @Override protected String getPersistenceTypeName() { return STATISTICS_CF; } @Override protected ByteArrayId getSecondaryId( final DataStatistics<?> persistedObject ) { return persistedObject.getDataAdapterId(); } @Override protected DataStatistics<?> entryToValue( final Cell entry ) { final DataStatistics<?> stats = super.entryToValue(entry); if (stats != null) { stats.setDataAdapterId(new ByteArrayId( CellUtil.cloneQualifier(entry))); } return stats; } @Override public void removeAllStatistics( final ByteArrayId adapterId, final String... authorizations ) { deleteObjects( null, adapterId, authorizations); } @Override public void transformVisibility( final ByteArrayId adapterId, final String transformingRegex, final String replacement, final String... authorizations ) { // TODO Unimplemented LOGGER.error("This method transformVisibility is not yet coded. Need to fix it"); } /** * This function is used to change the scan object created in the superclass * to enable prefixing. */ @Override protected Scan applyScannerSettings( final Scan scanner, final ByteArrayId primaryId, final ByteArrayId secondaryId ) { final Scan scan = super.applyScannerSettings( scanner, primaryId, secondaryId); if (primaryId != null) { final ByteBuffer buf = ByteBuffer.allocate(primaryId.getBytes().length + 1); buf.put(primaryId.getBytes()); buf.put(new byte[] { 0 }); // So this will set the stop row to just after all the possible // suffixes to this primaryId. scan.setStopRow(HBaseUtils.getNextPrefix(buf.array())); } return scan; } /** * This function converts results and merges data statistic elements * together that have the same id. */ @Override protected Iterator<DataStatistics<?>> getNativeIteratorWrapper( final Iterator<Result> resultIterator ) { return new StatisticsNativeIteratorWrapper( resultIterator); } /** * A special version of NativeIteratorWrapper (defined in the parent) which * will combine records that have the same dataid & statsId */ private class StatisticsNativeIteratorWrapper implements Iterator<DataStatistics<?>> { final private Iterator<Result> it; private DataStatistics<?> nextVal = null; public StatisticsNativeIteratorWrapper( final Iterator<Result> resultIterator ) { it = resultIterator; } @Override public boolean hasNext() { return (nextVal != null) || it.hasNext(); } @Override public DataStatistics<?> next() { DataStatistics<?> currentStatistics = nextVal; nextVal = null; while (it.hasNext()) { final Cell cell = it.next().listCells().get( 0); // This entryToValue function has the side effect of adding the // object to the cache. // We need to make sure to add the merged version of the stat at // the end of this // function, before it is returned. final DataStatistics<?> statEntry = entryToValue(cell); if (currentStatistics == null) { currentStatistics = statEntry; } else { if (statEntry.getStatisticsId().equals( currentStatistics.getStatisticsId()) && statEntry.getDataAdapterId().equals( currentStatistics.getDataAdapterId())) { currentStatistics.merge(statEntry); } else { nextVal = statEntry; break; } } } // Add this entry to cache (see comment above) addObjectToCache( getPrimaryId(currentStatistics), getSecondaryId(currentStatistics), currentStatistics); return currentStatistics; } @Override public void remove() { throw new NotImplementedException( "Transforming iterator cannot use remove()"); } } /** * This function will append a UUID to the record that's inserted into the * database. * * @param object * @return */ @Override protected ByteArrayId getRowId( final DataStatistics<?> object ) { final byte[] parentRecord = super.getRowId( object).getBytes(); return HBaseUtils.ensureUniqueId( parentRecord, false); } }