Package org.kiji.schema.impl.hbase

Source Code of org.kiji.schema.impl.hbase.HBaseMapFamilyPager

/**
* (c) Copyright 2013 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 org.kiji.schema.impl.hbase;

import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReference;

import com.google.common.base.Preconditions;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.kiji.annotations.ApiAudience;
import org.kiji.schema.EntityId;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.KijiColumnPagingNotEnabledException;
import org.kiji.schema.KijiDataRequest;
import org.kiji.schema.KijiDataRequestBuilder.ColumnsDef;
import org.kiji.schema.KijiIOException;
import org.kiji.schema.KijiPager;
import org.kiji.schema.KijiRowData;
import org.kiji.schema.filter.Filters;
import org.kiji.schema.filter.KijiColumnFilter;
import org.kiji.schema.filter.KijiColumnRangeFilter;
import org.kiji.schema.filter.StripValueColumnFilter;
import org.kiji.schema.impl.KijiPaginationFilter;
import org.kiji.schema.layout.HBaseColumnNameTranslator;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.util.Debug;

/**
* HBase implementation of KijiPager for map-type families.
*
* <p>
*   This pager lists the qualifiers in the map-type family and nothing else.
*   In particular, the cells content is not retrieved.
* </p>
*
* <p>
*   This pager conforms to the KijiPager interface, in order to implement
*   {@link KijiRowData#getPager(String)}.
*   More straightforward interfaces are available using {@link HBaseQualifierPager} and
*   {@link HBaseQualifierIterator}.
* </p>
*
* @see HBaseQualifierPager
* @see HBaseQualifierIterator
*/
@ApiAudience.Private
public final class HBaseMapFamilyPager implements KijiPager {
  private static final Logger LOG = LoggerFactory.getLogger(HBaseMapFamilyPager.class);

  /** Entity ID of the row being paged through. */
  private final EntityId mEntityId;

  /** HBase KijiTable to read from. */
  private final HBaseKijiTable mTable;

  /** Name of the map-type family being paged through. */
  private final KijiColumnName mFamily;

  /** Full data request. */
  private final KijiDataRequest mDataRequest;

  /** Column data request for the map-type family to page through. */
  private final KijiDataRequest.Column mColumnRequest;

  /** The state of a map family pager instance. */
  private static enum State {
    UNINITIALIZED,
    OPEN,
    CLOSED
  }

  /** Tracks the state of this map family pager. */
  private final AtomicReference<State> mState = new AtomicReference<State>(State.UNINITIALIZED);

  /** True only if there is another page of data to read through {@link #next()}. */
  private boolean mHasNext;

  /**
   * Highest qualifier (according to the HBase bytes comparator) returned so far.
   * This is the low bound (exclusive) for qualifiers to retrieve next.
   */
  private String mMinQualifier = null;

  /**
   * Initializes a pager for a map-type family.
   *
   * <p>
   *   To get a pager for a column with paging enabled, use {@link KijiRowData#getPager(String)}.
   * </p>
   *
   * @param entityId The entityId of the row.
   * @param dataRequest The requested data.
   * @param table The Kiji table that this row belongs to.
   * @param family Iterate through the qualifiers from this map-type family.
   * @throws KijiColumnPagingNotEnabledException If paging is not enabled for the specified family.
   */
  HBaseMapFamilyPager(
      EntityId entityId,
      KijiDataRequest dataRequest,
      HBaseKijiTable table,
      KijiColumnName family)
      throws KijiColumnPagingNotEnabledException {

    Preconditions.checkArgument(!family.isFullyQualified(),
        "Must use HBaseQualifierPager on a map-type family, but got '{}'.", family);
    mFamily = family;

    mDataRequest = dataRequest;
    mColumnRequest = mDataRequest.getColumn(family.getFamily(), null);
    if (!mColumnRequest.isPagingEnabled()) {
      throw new KijiColumnPagingNotEnabledException(
        String.format("Paging is not enabled for column [%s].", family));
    }

    mEntityId = entityId;
    mTable = table;
    mHasNext = true// there might be no page to read, but we don't know until we issue an RPC

    // Only retain the table if everything else ran fine:
    mTable.retain();

    final State oldState = mState.getAndSet(State.OPEN);
    Preconditions.checkState(oldState == State.UNINITIALIZED,
        "Cannot open MapFamilyPager instance in state %s.", oldState);
  }

  /** {@inheritDoc} */
  @Override
  public boolean hasNext() {
    final State state = mState.get();
    Preconditions.checkState(state == State.OPEN,
        "Cannot check has next while MapFamilyPager is in state %s.", state);
    return mHasNext;
  }

  /** {@inheritDoc} */
  @Override
  public KijiRowData next() {
    return next(mColumnRequest.getPageSize());
  }

  /** {@inheritDoc} */
  @Override
  public KijiRowData next(int pageSize) {
    final State state = mState.get();
    Preconditions.checkState(state == State.OPEN,
        "Cannot get next while MapFamilyPager is in state %s.", state);
    if (!mHasNext) {
      throw new NoSuchElementException();
    }
    Preconditions.checkArgument(pageSize > 0, "Page size must be >= 1, got %s", pageSize);

    // Clone the column data request template, adjusting the filters to restrict the range
    // and the number of qualifiers to fetch.

    final KijiColumnFilter filter = Filters.and(
        new KijiColumnRangeFilter(mMinQualifier, false, null, false)// qualifier > mMinQualifier
        mColumnRequest.getFilter()// user filter
        new KijiPaginationFilter(pageSize)// Select at most one version / qualifier.
        new StripValueColumnFilter())// discard the cell content, we just need the qualifiers

    final KijiDataRequest nextPageDataRequest = KijiDataRequest.builder()
        .withTimeRange(mDataRequest.getMinTimestamp(), mDataRequest.getMaxTimestamp())
        .addColumns(ColumnsDef.create()
            .withFilter(filter)
            .withMaxVersions(1// HBase pagination filter forces max-versions to 1
            .add(mFamily))
        .build();

    LOG.debug("HBaseMapPager data request: {} and page size {}", nextPageDataRequest, pageSize);

    final KijiTableLayout layout = mTable.getLayout();
    final HBaseColumnNameTranslator translator = HBaseColumnNameTranslator.from(layout);
    final HBaseDataRequestAdapter adapter =
        new HBaseDataRequestAdapter(nextPageDataRequest, translator);
    try {
      final Get hbaseGet = adapter.toGet(mEntityId, layout);
      if (LOG.isDebugEnabled()) {
        LOG.debug("Sending HBase Get: {} with filter {}",
            hbaseGet, Debug.toDebugString(hbaseGet.getFilter()));
      }
      final Result result = doHBaseGet(hbaseGet);
      LOG.debug("Got {} cells over {} requested", result.size(), pageSize);

      final KijiRowData page =
          // No cell is being decoded here so we don't need a cell decoder provider:
          new HBaseKijiRowData(mTable, nextPageDataRequest, mEntityId, result, null);

      // There is an HBase bug that leads to less KeyValue being returned than expected.
      // An empty result appears to be a reliable way to detect the end of the iteration.
      if (result.isEmpty()) {
        mHasNext = false;
      } else {
        // Update the low qualifier bound for the next iteration:
        mMinQualifier = page.getQualifiers(mFamily.getFamily()).last();
      }

      return page;

    } catch (IOException ioe) {
      throw new KijiIOException(ioe);
    }
  }

  /**
   * 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 {
      return htable.get(get);
    } finally {
      htable.close();
    }
  }

  /** {@inheritDoc} */
  @Override
  public void remove() {
    throw new UnsupportedOperationException("KijiPager.remove() is not supported.");
  }

  /** {@inheritDoc} */
  @Override
  public void close() throws IOException {
    final State oldState = mState.getAndSet(State.CLOSED);
    Preconditions.checkState(oldState == State.OPEN,
        "Cannot close MapFamilyPager while in state %s", oldState);
    mTable.release();
  }
}
TOP

Related Classes of org.kiji.schema.impl.hbase.HBaseMapFamilyPager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.