package eu.fbk.knowledgestore.datastore.hbase; import java.io.Closeable; import java.io.IOException; import java.util.Iterator; import javax.annotation.Nullable; import com.google.common.base.Preconditions; import com.google.common.collect.AbstractIterator; import com.google.common.collect.Iterables; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.openrdf.model.URI; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import eu.fbk.knowledgestore.data.Record; import eu.fbk.knowledgestore.data.XPath; import eu.fbk.knowledgestore.datastore.hbase.utils.AbstractHBaseUtils; import eu.fbk.knowledgestore.datastore.hbase.utils.AvroSerializer; import eu.fbk.knowledgestore.datastore.hbase.utils.HBaseFilter; /** * Performs an HBase scanner-based retrieval of the records matching an optional condition from a * certain HBase table/column family. */ public class HBaseScanIterator extends AbstractIterator<Record> implements Closeable { /** Logger object used inside HdfsFileStore. */ private static final Logger LOGGER = LoggerFactory.getLogger(HBaseScanIterator.class); /** Optional conditions to be applied locally to match records. */ @Nullable private final XPath condition; /** The properties to return, null if all properties are requested. */ @Nullable private final URI[] properties; /** Created scanner, kept in order to close it after iteration. */ @Nullable private final ResultScanner scanner; /** The iterator returned by the scanner. */ private final Iterator<Result> hbaseIterator; /** The {@code AvroSerializer} used to deserialize rows into records. */ private final AvroSerializer serializer; /** * Creates a new {@code HBaseScanStream} based on the parameters supplied. * * @param hbaseUtils * the {@code AbstractHBaseUtils} object for accessing HBase, not null * @param tableName * the name of the HBase table to access, not null * @param familyName * the name of the HBase column family to access, not null * @param condition * optional condition to be satisfied by matching records, possibly null * @param properties * properties to return, null if all properties are requested * @param localFiltering * true if filtering should be performed locally to the HBase client * @throws IOException * on failure */ public HBaseScanIterator(final AbstractHBaseUtils hbaseUtils, final String tableName, final String familyName, @Nullable final XPath condition, @Nullable final Iterable<? extends URI> properties, final boolean localFiltering) throws IOException { // Check parameters Preconditions.checkNotNull(hbaseUtils); Preconditions.checkNotNull(tableName); Preconditions.checkNotNull(familyName); // Configure Scan operation, differentiating between local or remote filtering final Scan scan = hbaseUtils.getScan(tableName, familyName); if (condition != null && !localFiltering) { scan.setFilter(new HBaseFilter(condition, hbaseUtils.getSerializer())); } // Open a result scanner and keep track of it, so that it can be closed at the end final ResultScanner scanner = hbaseUtils.getScanner(tableName, scan); // Initialize state this.condition = localFiltering ? condition : null; // unset on server-side filtering this.properties = properties == null ? null : Iterables.toArray(properties, URI.class); this.serializer = hbaseUtils.getSerializer(); this.scanner = scanner; this.hbaseIterator = this.scanner.iterator(); } @Override protected Record computeNext() { try { // Iterate until a matching record is found or EOF is reached while (this.hbaseIterator.hasNext()) { // Retrieve next binary result from HBase final byte[] bytes = this.hbaseIterator.next().value(); // Attempt deserialization. Log and skip result on failure Record record; try { record = (Record) this.serializer.fromBytes(bytes); } catch (final Throwable ex) { LOGGER.error("discarded record with avroBytes \"" + bytes + ", " + ex.toString()); continue; } // Evaluate condition locally, if required if (this.condition != null) { final boolean matches = this.condition.evalBoolean(record); if (!matches) { continue; } } // Perform client-side projection, if requested if (this.properties != null) { record = record.retain(this.properties); } // Return the record return record; } // Signal EOF return endOfData(); } catch (Exception e) { LOGGER.warn("ignored Exception |" + e.toString() + "| and returned"); return null; } } /** * {@inheritDoc} Closes the HBase scanner, if previousy created. */ @Override public void close() { if (this.scanner != null) { LOGGER.debug("Closing HBaseScanIterator"); this.scanner.close(); } } }