Java tutorial
/** * (c) Copyright 2012 WibiData, Inc. * * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.moz.fiji.schema.impl.hbase; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicReference; import com.google.common.base.Objects; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.HTableInterface; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.Scan; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.moz.fiji.annotations.ApiAudience; import com.moz.fiji.commons.ResourceTracker; import com.moz.fiji.schema.EntityId; import com.moz.fiji.schema.HBaseEntityId; import com.moz.fiji.schema.InternalFijiError; import com.moz.fiji.schema.FijiColumnName; import com.moz.fiji.schema.FijiDataRequest; import com.moz.fiji.schema.FijiDataRequestValidator; import com.moz.fiji.schema.FijiPartition; import com.moz.fiji.schema.FijiResult; import com.moz.fiji.schema.FijiResultScanner; import com.moz.fiji.schema.FijiRowData; import com.moz.fiji.schema.FijiRowScanner; import com.moz.fiji.schema.FijiTableReader; import com.moz.fiji.schema.FijiTableReaderBuilder; import com.moz.fiji.schema.FijiTableReaderBuilder.OnDecoderCacheMiss; import com.moz.fiji.schema.NoSuchColumnException; import com.moz.fiji.schema.SpecificCellDecoderFactory; import com.moz.fiji.schema.filter.FijiRowFilter; import com.moz.fiji.schema.filter.FijiRowFilterApplicator; import com.moz.fiji.schema.hbase.HBaseScanOptions; import com.moz.fiji.schema.impl.BoundColumnReaderSpec; import com.moz.fiji.schema.impl.LayoutConsumer; import com.moz.fiji.schema.layout.CellSpec; import com.moz.fiji.schema.layout.ColumnReaderSpec; import com.moz.fiji.schema.layout.HBaseColumnNameTranslator; import com.moz.fiji.schema.layout.InvalidLayoutException; import com.moz.fiji.schema.layout.FijiTableLayout; import com.moz.fiji.schema.layout.impl.CellDecoderProvider; /** * Reads from a fiji table by sending the requests directly to the HBase tables. */ @ApiAudience.Private public final class HBaseFijiTableReader implements FijiTableReader { private static final Logger LOG = LoggerFactory.getLogger(HBaseFijiTableReader.class); /** HBase FijiTable to read from. */ private final HBaseFijiTable mTable; /** Behavior when a cell decoder cannot be found. */ private final OnDecoderCacheMiss mOnDecoderCacheMiss; /** States of a fiji table reader instance. */ private static enum State { UNINITIALIZED, OPEN, CLOSED } /** Tracks the state of this FijiTableReader instance. */ private final AtomicReference<State> mState = new AtomicReference<State>(State.UNINITIALIZED); /** Map of overridden CellSpecs to use when reading. Null when mOverrides is not null. */ private final Map<FijiColumnName, CellSpec> mCellSpecOverrides; /** Map of overridden column read specifications. Null when mCellSpecOverrides is not null. */ private final Map<FijiColumnName, BoundColumnReaderSpec> mOverrides; /** Map of backup column read specifications. Null when mCellSpecOverrides is not null. */ private final Collection<BoundColumnReaderSpec> mAlternatives; /** Layout consumer registration resource. */ private final LayoutConsumer.Registration mLayoutConsumerRegistration; /** * Encapsulation of all table layout related state necessary for the operation of this reader. * Can be hot swapped to reflect a table layout update. */ private ReaderLayoutCapsule mReaderLayoutCapsule = null; /** * Container class encapsulating all reader state which must be updated in response to a table * layout update. */ private static final class ReaderLayoutCapsule { private final CellDecoderProvider mCellDecoderProvider; private final FijiTableLayout mLayout; private final HBaseColumnNameTranslator mTranslator; /** * Default constructor. * * @param cellDecoderProvider the CellDecoderProvider to cache. This provider should reflect * all overrides appropriate to this reader. * @param layout the FijiTableLayout to cache. * @param translator the FijiColumnNameTranslator to cache. */ private ReaderLayoutCapsule(final CellDecoderProvider cellDecoderProvider, final FijiTableLayout layout, final HBaseColumnNameTranslator translator) { mCellDecoderProvider = cellDecoderProvider; mLayout = layout; mTranslator = translator; } /** * Get the column name translator for the current layout. * @return the column name translator for the current layout. */ private HBaseColumnNameTranslator getColumnNameTranslator() { return mTranslator; } /** * Get the current table layout for the table to which this reader is associated. * @return the current table layout for the table to which this reader is associated. */ private FijiTableLayout getLayout() { return mLayout; } /** * Get the CellDecoderProvider including CellSpec overrides for providing cell decoders for the * current layout. * @return the CellDecoderProvider including CellSpec overrides for providing cell decoders for * the current layout. */ private CellDecoderProvider getCellDecoderProvider() { return mCellDecoderProvider; } } /** Provides for the updating of this Reader in response to a table layout update. */ private final class InnerLayoutUpdater implements LayoutConsumer { /** {@inheritDoc} */ @Override public void update(FijiTableLayout layout) throws IOException { if (mState.get() == State.CLOSED) { LOG.debug("FijiTableReader instance is closed; ignoring layout update."); return; } final CellDecoderProvider provider; if (null != mCellSpecOverrides) { provider = CellDecoderProvider.create(layout, mTable.getFiji().getSchemaTable(), SpecificCellDecoderFactory.get(), mCellSpecOverrides); } else { provider = CellDecoderProvider.create(layout, mOverrides, mAlternatives, mOnDecoderCacheMiss); } if (mReaderLayoutCapsule != null) { LOG.debug("Updating layout used by FijiTableReader: {} for table: {} from version: {} to: {}", this, mTable.getURI(), mReaderLayoutCapsule.getLayout().getDesc().getLayoutId(), layout.getDesc().getLayoutId()); } else { // If the capsule is null this is the initial setup and we need a different log message. LOG.debug("Initializing FijiTableReader: {} for table: {} with table layout version: {}", this, mTable.getURI(), layout.getDesc().getLayoutId()); } mReaderLayoutCapsule = new ReaderLayoutCapsule(provider, layout, HBaseColumnNameTranslator.from(layout)); } } /** * Creates a new <code>HBaseFijiTableReader</code> instance that sends the read requests * directly to HBase. * * @param table Fiji table from which to read. * @throws IOException on I/O error. * @return a new HBaseFijiTableReader. */ public static HBaseFijiTableReader create(final HBaseFijiTable table) throws IOException { return HBaseFijiTableReaderBuilder.create(table).buildAndOpen(); } /** * Creates a new <code>HbaseFijiTableReader</code> instance that sends read requests directly to * HBase. * * @param table Fiji table from which to read. * @param overrides layout overrides to modify read behavior. * @return a new HBaseFijiTableReader. * @throws IOException in case of an error opening the reader. * @deprecated use {@link #createWithOptions}. */ @Deprecated public static HBaseFijiTableReader createWithCellSpecOverrides(final HBaseFijiTable table, final Map<FijiColumnName, CellSpec> overrides) throws IOException { return new HBaseFijiTableReader(table, overrides); } /** * Create a new <code>HBaseFijiTableReader</code> instance that sends read requests directly to * HBase. * * @param table Fiji table from which to read. * @param onDecoderCacheMiss behavior to use when a {@link ColumnReaderSpec} override * specified in a {@link FijiDataRequest} cannot be found in the prebuilt cache of cell * decoders. * @param overrides mapping from columns to overriding read behavior for those columns. * @param alternatives mapping from columns to reader spec alternatives which the * FijiTableReader will accept as overrides in data requests. * @return a new HBaseFijiTableReader. * @throws IOException in case of an error opening the reader. */ public static HBaseFijiTableReader createWithOptions(final HBaseFijiTable table, final OnDecoderCacheMiss onDecoderCacheMiss, final Map<FijiColumnName, ColumnReaderSpec> overrides, final Multimap<FijiColumnName, ColumnReaderSpec> alternatives) throws IOException { return new HBaseFijiTableReader(table, onDecoderCacheMiss, overrides, alternatives); } /** * Open a table reader whose behavior is customized by overriding CellSpecs. * * @param table Fiji table from which this reader will read. * @param cellSpecOverrides specifications of overriding read behaviors. * @throws IOException in case of an error opening the reader. */ private HBaseFijiTableReader(final HBaseFijiTable table, final Map<FijiColumnName, CellSpec> cellSpecOverrides) throws IOException { mTable = table; mCellSpecOverrides = cellSpecOverrides; mOnDecoderCacheMiss = FijiTableReaderBuilder.DEFAULT_CACHE_MISS; mOverrides = null; mAlternatives = null; mLayoutConsumerRegistration = mTable.registerLayoutConsumer(new InnerLayoutUpdater()); Preconditions.checkState(mReaderLayoutCapsule != null, "FijiTableReader for table: %s failed to initialize.", mTable.getURI()); // Retain the table only when everything succeeds. mTable.retain(); final State oldState = mState.getAndSet(State.OPEN); Preconditions.checkState(oldState == State.UNINITIALIZED, "Cannot open FijiTableReader instance in state %s.", oldState); ResourceTracker.get().registerResource(this); } /** * Creates a new <code>HBaseFijiTableReader</code> instance that sends read requests directly to * HBase. * * @param table Fiji table from which to read. * @param onDecoderCacheMiss behavior to use when a {@link ColumnReaderSpec} override * specified in a {@link FijiDataRequest} cannot be found in the prebuilt cache of cell * decoders. * @param overrides mapping from columns to overriding read behavior for those columns. * @param alternatives mapping from columns to reader spec alternatives which the * FijiTableReader will accept as overrides in data requests. * @throws IOException on I/O error. */ private HBaseFijiTableReader(final HBaseFijiTable table, final OnDecoderCacheMiss onDecoderCacheMiss, final Map<FijiColumnName, ColumnReaderSpec> overrides, final Multimap<FijiColumnName, ColumnReaderSpec> alternatives) throws IOException { mTable = table; mOnDecoderCacheMiss = onDecoderCacheMiss; final FijiTableLayout layout = mTable.getLayout(); final Set<FijiColumnName> layoutColumns = layout.getColumnNames(); final Map<FijiColumnName, BoundColumnReaderSpec> boundOverrides = Maps.newHashMap(); for (Map.Entry<FijiColumnName, ColumnReaderSpec> override : overrides.entrySet()) { final FijiColumnName column = override.getKey(); if (!layoutColumns.contains(column) && !layoutColumns.contains(FijiColumnName.create(column.getFamily()))) { throw new NoSuchColumnException( String.format("FijiTableLayout: %s does not contain column: %s", layout, column)); } else { boundOverrides.put(column, BoundColumnReaderSpec.create(override.getValue(), column)); } } mOverrides = boundOverrides; final Collection<BoundColumnReaderSpec> boundAlternatives = Sets.newHashSet(); for (Map.Entry<FijiColumnName, ColumnReaderSpec> altsEntry : alternatives.entries()) { final FijiColumnName column = altsEntry.getKey(); if (!layoutColumns.contains(column) && !layoutColumns.contains(FijiColumnName.create(column.getFamily()))) { throw new NoSuchColumnException( String.format("FijiTableLayout: %s does not contain column: %s", layout, column)); } else { boundAlternatives.add(BoundColumnReaderSpec.create(altsEntry.getValue(), altsEntry.getKey())); } } mAlternatives = boundAlternatives; mCellSpecOverrides = null; mLayoutConsumerRegistration = mTable.registerLayoutConsumer(new InnerLayoutUpdater()); Preconditions.checkState(mReaderLayoutCapsule != null, "FijiTableReader for table: %s failed to initialize.", mTable.getURI()); // Retain the table only when everything succeeds. mTable.retain(); final State oldState = mState.getAndSet(State.OPEN); Preconditions.checkState(oldState == State.UNINITIALIZED, "Cannot open FijiTableReader instance in state %s.", oldState); ResourceTracker.get().registerResource(this); } /** {@inheritDoc} */ @Override public FijiRowData get(EntityId entityId, FijiDataRequest dataRequest) throws IOException { final State state = mState.get(); Preconditions.checkState(state == State.OPEN, "Cannot get row from FijiTableReader instance %s in state %s.", this, state); final ReaderLayoutCapsule capsule = mReaderLayoutCapsule; // Make sure the request validates against the layout of the table. final FijiTableLayout tableLayout = capsule.getLayout(); validateRequestAgainstLayout(dataRequest, tableLayout); // Construct an HBase Get to send to the HTable. HBaseDataRequestAdapter hbaseRequestAdapter = new HBaseDataRequestAdapter(dataRequest, capsule.getColumnNameTranslator()); Get hbaseGet; try { hbaseGet = hbaseRequestAdapter.toGet(entityId, tableLayout); } catch (InvalidLayoutException e) { // The table layout should never be invalid at this point, since we got it from a valid // opened table. If it is, there's something seriously wrong. throw new InternalFijiError(e); } // Send the HTable Get. final Result result = hbaseGet.hasFamilies() ? doHBaseGet(hbaseGet) : new Result(); // Parse the result. return new HBaseFijiRowData(mTable, dataRequest, entityId, result, capsule.getCellDecoderProvider()); } /** * Get a FijiResult for the given EntityId and data request. * * <p> * This method allows the caller to specify a type-bound on the values of the {@code FijiCell}s * of the returned {@code FijiResult}. The caller should be careful to only specify an * appropriate type. If the type is too specific (or wrong), a runtime * {@link java.lang.ClassCastException} will be thrown when the returned {@code FijiResult} is * used. See the 'Type Safety' section of {@link FijiResult}'s documentation for more details. * </p> * * @param entityId EntityId of the row from which to get data. * @param dataRequest Specification of the data to get from the given row. * @param <T> type {@code FijiCell} value returned by the {@code FijiResult}. * @return a new FijiResult for the given EntityId and data request. * @throws IOException in case of an error getting the data. */ @Override public <T> FijiResult<T> getResult(final EntityId entityId, final FijiDataRequest dataRequest) throws IOException { final State state = mState.get(); Preconditions.checkState(state == State.OPEN, "Cannot get row from FijiTableReader instance %s in state %s.", this, state); final ReaderLayoutCapsule capsule = mReaderLayoutCapsule; final FijiTableLayout tableLayout = capsule.getLayout(); validateRequestAgainstLayout(dataRequest, tableLayout); final HBaseDataRequestAdapter hbaseDataRequestAdapter = new HBaseDataRequestAdapter(dataRequest, capsule.getColumnNameTranslator()); final Get get = hbaseDataRequestAdapter.toGet(entityId, tableLayout); final Result result = get.hasFamilies() ? doHBaseGet(get) : new Result(); return HBaseFijiResult.create(entityId, dataRequest, result, mTable, capsule.getLayout(), capsule.getColumnNameTranslator(), capsule.getCellDecoderProvider()); } /** {@inheritDoc} */ @Override public List<FijiRowData> bulkGet(List<EntityId> entityIds, FijiDataRequest dataRequest) throws IOException { final State state = mState.get(); Preconditions.checkState(state == State.OPEN, "Cannot get rows from FijiTableReader instance %s in state %s.", this, state); // Bulk gets have some overhead associated with them, // so delegate work to get(EntityId, FijiDataRequest) if possible. if (entityIds.size() == 1) { return Collections.singletonList(this.get(entityIds.get(0), dataRequest)); } final ReaderLayoutCapsule capsule = mReaderLayoutCapsule; final FijiTableLayout tableLayout = capsule.getLayout(); validateRequestAgainstLayout(dataRequest, tableLayout); final HBaseDataRequestAdapter hbaseRequestAdapter = new HBaseDataRequestAdapter(dataRequest, capsule.getColumnNameTranslator()); // Construct a list of hbase Gets to send to the HTable. final List<Get> hbaseGetList = makeGetList(entityIds, tableLayout, hbaseRequestAdapter); // Send the HTable Gets. final Result[] results = doHBaseGet(hbaseGetList); Preconditions.checkState(entityIds.size() == results.length); // Parse the results. If a Result is null, then the corresponding FijiRowData should also // be null. This indicates that there was an error retrieving this row. return parseResults(results, entityIds, dataRequest); } /** {@inheritDoc} */ @Override public <T> List<FijiResult<T>> bulkGetResults(final List<EntityId> entityIds, final FijiDataRequest dataRequest) throws IOException { final State state = mState.get(); Preconditions.checkState(state == State.OPEN, "Cannot get rows from FijiTableReader instance %s in state %s.", this, state); // Bulk gets have some overhead associated with them, // so delegate work to getResult(EntityId, FijiDataRequest) if possible. if (entityIds.size() == 1) { return Collections.singletonList(this.<T>getResult(entityIds.get(0), dataRequest)); } final ReaderLayoutCapsule capsule = mReaderLayoutCapsule; final FijiTableLayout tableLayout = capsule.getLayout(); validateRequestAgainstLayout(dataRequest, tableLayout); final HBaseDataRequestAdapter hbaseRequestAdapter = new HBaseDataRequestAdapter(dataRequest, capsule.getColumnNameTranslator()); // Construct a list of hbase Gets to send to the HTable. final List<Get> hbaseGetList = makeGetList(entityIds, tableLayout, hbaseRequestAdapter); // Send the HTable Gets. final Result[] results = doHBaseGet(hbaseGetList); Preconditions.checkState(entityIds.size() == results.length); final List<FijiResult<T>> fijiResults = Lists.newArrayList(); for (int i = 0; i < entityIds.size(); i++) { fijiResults.add(HBaseFijiResult.<T>create(entityIds.get(i), dataRequest, results[i], mTable, capsule.getLayout(), capsule.getColumnNameTranslator(), capsule.getCellDecoderProvider())); } return fijiResults; } /** {@inheritDoc} */ @Override public FijiRowScanner getScanner(FijiDataRequest dataRequest) throws IOException { return getScanner(dataRequest, new FijiScannerOptions()); } /** {@inheritDoc} */ @Override public FijiRowScanner getScanner(FijiDataRequest dataRequest, FijiScannerOptions fijiScannerOptions) throws IOException { final State state = mState.get(); Preconditions.checkState(state == State.OPEN, "Cannot get scanner from FijiTableReader instance %s in state %s.", this, state); try { EntityId startRow = fijiScannerOptions.getStartRow(); EntityId stopRow = fijiScannerOptions.getStopRow(); FijiRowFilter rowFilter = fijiScannerOptions.getFijiRowFilter(); HBaseScanOptions scanOptions = fijiScannerOptions.getHBaseScanOptions(); final ReaderLayoutCapsule capsule = mReaderLayoutCapsule; final HBaseDataRequestAdapter dataRequestAdapter = new HBaseDataRequestAdapter(dataRequest, capsule.getColumnNameTranslator()); final FijiTableLayout tableLayout = capsule.getLayout(); validateRequestAgainstLayout(dataRequest, tableLayout); final Scan scan = dataRequestAdapter.toScan(tableLayout, scanOptions); if (null != startRow) { scan.setStartRow(startRow.getHBaseRowKey()); } if (null != stopRow) { scan.setStopRow(stopRow.getHBaseRowKey()); } scan.setCaching(fijiScannerOptions.getRowCaching()); if (null != rowFilter) { final FijiRowFilterApplicator applicator = FijiRowFilterApplicator.create(rowFilter, tableLayout, mTable.getFiji().getSchemaTable()); applicator.applyTo(scan); } return new HBaseFijiRowScanner(new HBaseFijiRowScanner.Options().withDataRequest(dataRequest) .withTable(mTable).withScan(scan).withCellDecoderProvider(capsule.getCellDecoderProvider()) .withReopenScannerOnTimeout(fijiScannerOptions.getReopenScannerOnTimeout())); } catch (InvalidLayoutException e) { // The table layout should never be invalid at this point, since we got it from a valid // opened table. If it is, there's something seriously wrong. throw new InternalFijiError(e); } } /** {@inheritDoc} */ @Override public <T> HBaseFijiResultScanner<T> getFijiResultScanner(final FijiDataRequest request) throws IOException { return getFijiResultScanner(request, new FijiScannerOptions()); } /** * Get a FijiResultScanner for the given data request and scan options. * * <p> * This method allows the caller to specify a type-bound on the values of the {@code FijiCell}s * of the returned {@code FijiResult}s. The caller should be careful to only specify an * appropriate type. If the type is too specific (or wrong), a runtime * {@link java.lang.ClassCastException} will be thrown when the returned {@code FijiResult} is * used. See the 'Type Safety' section of {@code FijiResult}'s documentation for more details. * </p> * * @param request Data request defining the data to retrieve from each row. * @param scannerOptions Options to control the operation of the scanner. * @param <T> type {@code FijiCell} value returned by the {@code FijiResult}. * @return A new FijiResultScanner. * @throws IOException in case of an error creating the scanner. */ public <T> HBaseFijiResultScanner<T> getFijiResultScanner(final FijiDataRequest request, final FijiScannerOptions scannerOptions) throws IOException { final State state = mState.get(); Preconditions.checkState(state == State.OPEN, "Cannot get scanner from FijiTableReader instance %s in state %s.", this, state); final ReaderLayoutCapsule capsule = mReaderLayoutCapsule; final HBaseDataRequestAdapter adapter = new HBaseDataRequestAdapter(request, capsule.getColumnNameTranslator()); final FijiTableLayout layout = capsule.getLayout(); validateRequestAgainstLayout(request, layout); final Scan scan = adapter.toScan(layout, scannerOptions.getHBaseScanOptions()); if (null != scannerOptions.getStartRow()) { scan.setStartRow(scannerOptions.getStartRow().getHBaseRowKey()); } if (null != scannerOptions.getStopRow()) { scan.setStopRow(scannerOptions.getStopRow().getHBaseRowKey()); } scan.setCaching(scannerOptions.getRowCaching()); if (null != scannerOptions.getFijiRowFilter()) { final FijiRowFilterApplicator applicator = FijiRowFilterApplicator .create(scannerOptions.getFijiRowFilter(), layout, mTable.getFiji().getSchemaTable()); applicator.applyTo(scan); } return new HBaseFijiResultScanner<T>(request, mTable, scan, capsule.getLayout(), capsule.getCellDecoderProvider(), capsule.getColumnNameTranslator(), scannerOptions.getReopenScannerOnTimeout()); } /** {@inheritDoc} */ @Override public <T> FijiResultScanner<T> getFijiResultScanner(final FijiDataRequest dataRequest, final FijiPartition partition) throws IOException { Preconditions.checkArgument(partition instanceof HBaseFijiPartition, "Can not scan an HBase table with a non-HBase partition."); final HBaseFijiPartition hbasePartition = (HBaseFijiPartition) partition; final FijiScannerOptions options = new FijiScannerOptions(); options.setStartRow(HBaseEntityId.fromHBaseRowKey(hbasePartition.getStartKey())); options.setStopRow(HBaseEntityId.fromHBaseRowKey(hbasePartition.getEndKey())); return getFijiResultScanner(dataRequest, options); } /** {@inheritDoc} */ @Override public String toString() { return Objects.toStringHelper(HBaseFijiTableReader.class).add("id", System.identityHashCode(this)) .add("table", mTable.getURI()) .add("layout-version", mReaderLayoutCapsule.getLayout().getDesc().getLayoutId()) .add("state", mState.get()).toString(); } /** * Parses an array of hbase Results, returned from a bulk get, to a List of * FijiRowData. * * @param results The results to parse. * @param entityIds The matching set of EntityIds. * @param dataRequest The FijiDataRequest. * @return The list of FijiRowData returned by these results. * @throws IOException If there is an error. */ private List<FijiRowData> parseResults(final Result[] results, final List<EntityId> entityIds, final FijiDataRequest dataRequest) throws IOException { List<FijiRowData> rowDataList = new ArrayList<FijiRowData>(results.length); for (int i = 0; i < results.length; i++) { Result result = results[i]; EntityId entityId = entityIds.get(i); final HBaseFijiRowData rowData = (null == result) ? null : new HBaseFijiRowData(mTable, dataRequest, entityId, result, mReaderLayoutCapsule.getCellDecoderProvider()); rowDataList.add(rowData); } return rowDataList; } /** * Creates a list of hbase Gets for a set of entityIds. * * @param entityIds The set of entityIds to collect. * @param tableLayout The table layout specifying constraints on what data to return for a row. * @param hbaseRequestAdapter The HBaseDataRequestAdapter. * @return A list of hbase Gets-- one for each entity id. * @throws IOException If there is an error. */ private static List<Get> makeGetList(List<EntityId> entityIds, FijiTableLayout tableLayout, HBaseDataRequestAdapter hbaseRequestAdapter) throws IOException { List<Get> hbaseGetList = new ArrayList<Get>(entityIds.size()); try { for (EntityId entityId : entityIds) { hbaseGetList.add(hbaseRequestAdapter.toGet(entityId, tableLayout)); } return hbaseGetList; } catch (InvalidLayoutException ile) { // The table layout should never be invalid at this point, since we got it from a valid // opened table. If it is, there's something seriously wrong. throw new InternalFijiError(ile); } } /** * Validate a data request against a table layout. * * @param dataRequest A FijiDataRequest. * @param layout the FijiTableLayout of the table against which to validate the data request. */ private void validateRequestAgainstLayout(FijiDataRequest dataRequest, FijiTableLayout layout) { // TODO(SCHEMA-263): This could be made more efficient if the layout and/or validator were // cached. FijiDataRequestValidator.validatorForLayout(layout).validate(dataRequest); } /** {@inheritDoc} */ @Override public void close() throws IOException { final State oldState = mState.getAndSet(State.CLOSED); Preconditions.checkState(oldState == State.OPEN, "Cannot close FijiTableReader instance %s in state %s.", this, oldState); ResourceTracker.get().unregisterResource(this); mLayoutConsumerRegistration.close(); mTable.release(); } /** * Sends an HBase Get request. * * @param get HBase Get request. * @return the HBase Result. * @throws IOException on I/O error. */ private Result doHBaseGet(Get get) throws IOException { final HTableInterface htable = mTable.openHTableConnection(); try { LOG.debug("Sending HBase Get: {}", get); return htable.get(get); } finally { htable.close(); } } /** * Sends a batch of HBase Get requests. * * @param get HBase Get requests. * @return the HBase Results. * @throws IOException on I/O error. */ private Result[] doHBaseGet(List<Get> get) throws IOException { final HTableInterface htable = mTable.openHTableConnection(); try { LOG.debug("Sending bulk HBase Get: {}", get); return htable.get(get); } finally { htable.close(); } } }