package mil.nga.giat.geowave.datastore.hbase.metadata; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.TableNotFoundException; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.RowMutations; 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.index.Persistable; import mil.nga.giat.geowave.core.index.PersistenceUtils; import mil.nga.giat.geowave.core.store.CloseableIterator; import mil.nga.giat.geowave.core.store.CloseableIteratorWrapper; import mil.nga.giat.geowave.core.store.metadata.AbstractGeowavePersistence; import mil.nga.giat.geowave.datastore.hbase.io.HBaseWriter; import mil.nga.giat.geowave.datastore.hbase.operations.BasicHBaseOperations; import mil.nga.giat.geowave.datastore.hbase.util.HBaseUtils; import mil.nga.giat.geowave.datastore.hbase.util.HBaseUtils.ScannerClosableWrapper; public abstract class AbstractHBasePersistence<T extends Persistable> extends AbstractGeowavePersistence<T> { private final static Logger LOGGER = LoggerFactory.getLogger(AbstractHBasePersistence.class); protected static final String[] METADATA_CFS = new String[] { HBaseAdapterIndexMappingStore.ADAPTER_INDEX_CF, HBaseAdapterStore.ADAPTER_CF, HBaseDataStatisticsStore.STATISTICS_CF, HBaseIndexStore.INDEX_CF }; protected final BasicHBaseOperations operations; public AbstractHBasePersistence( final BasicHBaseOperations operations ) { super( operations); this.operations = operations; } @SuppressWarnings("unchecked") protected T getObject( final ByteArrayId primaryId, final ByteArrayId secondaryId, final String... authorizations ) { final Object cacheResult = getObjectFromCache( primaryId, secondaryId); if (cacheResult != null) { return (T) cacheResult; } final Scan scanner = getScanner( primaryId, secondaryId, authorizations); try { final Iterator<Result> it = operations.getScannedResults( scanner, getTablename(), authorizations).iterator(); final Iterator<T> iter = getNativeIteratorWrapper(it); if (!iter.hasNext()) { LOGGER.warn("Object '" + getCombinedId( primaryId, secondaryId).getString() + "' not found"); return null; } return iter.next(); } catch (final IOException e) { LOGGER.error( "Unable to find object '" + getCombinedId( primaryId, secondaryId).getString() + "'", e); } return null; } @SuppressWarnings("unchecked") protected T entryToValue( final Cell entry ) { final T result = (T) PersistenceUtils.fromBinary( CellUtil.cloneValue(entry), Persistable.class); if (result != null) { addObjectToCache( getPrimaryId(result), getSecondaryId(result), result); } return result; } protected Scan getScanner( final ByteArrayId primaryId, final ByteArrayId secondaryId, final String... authorizations ) { final Scan scanner = new Scan(); applyScannerSettings( scanner, primaryId, secondaryId); return scanner; } protected Iterator<T> getNativeIteratorWrapper( final Iterator<Result> resultIterator ) { return new NativeIteratorWrapper( resultIterator); } protected Scan applyScannerSettings( final Scan scanner, final ByteArrayId primaryId, final ByteArrayId secondaryId ) { final String columnFamily = getColumnFamily(); final String columnQualifier = getColumnQualifier(secondaryId); if (columnFamily != null) { if (columnQualifier != null) { scanner.addColumn( toBytes(columnFamily), toBytes(columnQualifier)); } else { scanner.addFamily(toBytes(columnFamily)); } } if (primaryId != null) { scanner.setStartRow(primaryId.getBytes()); scanner.setStopRow(primaryId.getBytes()); } return scanner; } protected CloseableIterator<T> getObjects( final String... authorizations ) { try { final Scan scanner = getFullScanner(authorizations); final ResultScanner rS = operations.getScannedResults( scanner, getTablename(), authorizations); final Iterator<Result> it = rS.iterator(); return new CloseableIteratorWrapper<T>( new ScannerClosableWrapper( rS), getNativeIteratorWrapper(it)); } catch (final IOException e) { LOGGER.warn( "Unable to find objects in HBase table.", e); } return new CloseableIterator.Empty<T>(); } private Scan getFullScanner( final String... authorizations ) throws TableNotFoundException { return getScanner( null, null, authorizations); } protected ByteArrayId getRowId( final T object ) { return getPrimaryId(object); } @Override protected void addObject( final ByteArrayId primaryId, final ByteArrayId secondaryId, final T object ) { final ByteArrayId id = getRowId(object); addObjectToCache( primaryId, secondaryId, object); try { final HBaseWriter writer = operations.createWriter( getTablename(), // create table with all possible column families initially // because it is known METADATA_CFS, true); final RowMutations mutation = new RowMutations( id.getBytes()); final Put row = new Put( id.getBytes()); row.addColumn( toBytes(getColumnFamily()), toBytes(getColumnQualifier(object)), PersistenceUtils.toBinary(object)); mutation.add(row); writer.write( mutation, getColumnFamily().toString()); writer.close(); } catch (final IOException e) { LOGGER.error( "Unable add object", e); } } public boolean deleteObjects( final ByteArrayId primaryId, final ByteArrayId secondaryId, final String... authorizations ) { try { // Only way to do this is with a Scanner. final Scan scanner = getScanner( primaryId, secondaryId, authorizations); final ResultScanner rS = operations.getScannedResults( scanner, getTablename(), authorizations); final byte[] columnFamily = toBytes(getColumnFamily()); final byte[] columnQualifier = toBytes(getColumnQualifier(secondaryId)); final List<RowMutations> l = new ArrayList<RowMutations>(); for (final Result rr : rS) { final RowMutations deleteMutations = HBaseUtils.getDeleteMutations( rr.getRow(), columnFamily, columnQualifier, authorizations); l.add(deleteMutations); } try (final HBaseWriter deleter = operations.createWriter( getTablename(), METADATA_CFS, false)) { deleter.delete(l); } return true; } catch (final IOException e) { LOGGER.warn( "Unable to delete row from " + getTablename(), e); return false; } } protected boolean objectExists( final ByteArrayId primaryId, final ByteArrayId secondaryId ) { if (getObjectFromCache( primaryId, secondaryId) != null) { return true; } return false; } private class NativeIteratorWrapper implements Iterator<T> { final private Iterator<Result> it; private NativeIteratorWrapper( final Iterator<Result> it ) { this.it = it; } @Override public boolean hasNext() { return it.hasNext(); } @Override public T next() { final Cell cell = it.next().listCells().get( 0); return entryToValue(cell); } @Override public void remove() { it.remove(); } } protected CloseableIterator<T> getAllObjectsWithSecondaryId( final ByteArrayId secondaryId, final String... authorizations ) { try { final Scan scanner = getScanner( null, secondaryId, authorizations); final ResultScanner rS = operations.getScannedResults( scanner, getTablename(), authorizations); final Iterator<Result> it = rS.iterator(); return new CloseableIteratorWrapper<T>( new ScannerClosableWrapper( rS), getNativeIteratorWrapper(it)); } catch (final IOException e) { LOGGER.warn( "Unable to find objects, table '" + getTablename() + "' does not exist", e); } return new CloseableIterator.Empty<T>(); } }