Package org.kiji.schema.impl.hbase

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

/**
* (c) Copyright 2014 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.Closeable;
import java.io.IOException;
import java.util.Arrays;
import java.util.Iterator;
import java.util.SortedMap;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableSortedMap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.io.Closer;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.filter.ColumnPrefixFilter;
import org.apache.hadoop.hbase.filter.Filter;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.filter.FilterList.Operator;

import org.kiji.annotations.ApiAudience;
import org.kiji.schema.EntityId;
import org.kiji.schema.KijiCell;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.KijiDataRequest;
import org.kiji.schema.KijiDataRequest.Column;
import org.kiji.schema.KijiIOException;
import org.kiji.schema.KijiResult;
import org.kiji.schema.filter.KijiColumnFilter;
import org.kiji.schema.hbase.HBaseColumnName;
import org.kiji.schema.impl.DefaultKijiResult;
import org.kiji.schema.impl.hbase.HBaseDataRequestAdapter.NameTranslatingFilterContext;
import org.kiji.schema.layout.HBaseColumnNameTranslator;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.layout.impl.CellDecoderProvider;
import org.kiji.schema.util.DebugResourceTracker;

/**
* A {@link KijiResult} backed by on-demand HBase scans.
*
* <p>
*   {@code HBasePagedKiijResult} is <em>not</em> thread safe.
* </p>
*
* @param <T> The type of {@code KijiCell} values in the view.
*/
@ApiAudience.Private
public class HBasePagedKijiResult<T> implements KijiResult<T> {
  private final EntityId mEntityId;
  private final KijiDataRequest mDataRequest;
  private final HBaseKijiTable mTable;
  private final KijiTableLayout mLayout;
  private final HBaseColumnNameTranslator mColumnTranslator;
  private final CellDecoderProvider mDecoderProvider;
  private final SortedMap<KijiColumnName, Iterable<KijiCell<T>>> mColumnResults;
  private final Closer mCloser;

  /**
   * This result does not need to be closed unless {@link #iterator()} is called, so we defer
   * registering with the debug resource tracker till that point. This variable keeps track of
   * whether we have registered yet. Does not need to be atomic, because this class is not thread
   * safe.
   */
  private boolean mDebugRegistered = false;

  /**
   * Create a new {@link HBasePagedKijiResult}.
   *
   * @param entityId EntityId of the row from which to read cells.
   * @param dataRequest KijiDataRequest defining the values to retrieve.
   * @param table The table being viewed.
   * @param layout The layout of the table.
   * @param columnTranslator A column name translator for the table.
   * @param decoderProvider A cell decoder provider for the table.
   */
  public HBasePagedKijiResult(
      final EntityId entityId,
      final KijiDataRequest dataRequest,
      final HBaseKijiTable table,
      final KijiTableLayout layout,
      final HBaseColumnNameTranslator columnTranslator,
      final CellDecoderProvider decoderProvider
  ) {
    mEntityId = entityId;
    mDataRequest = dataRequest;
    mLayout = layout;
    mColumnTranslator = columnTranslator;
    mDecoderProvider = decoderProvider;
    mTable = table;
    mCloser = Closer.create();

    final ImmutableSortedMap.Builder<KijiColumnName, Iterable<KijiCell<T>>> columnResults =
        ImmutableSortedMap.naturalOrder();

    for (Column columnRequest : mDataRequest.getColumns()) {
      final PagedColumnIterable columnIterable = new PagedColumnIterable(columnRequest);
      mCloser.register(columnIterable);
      columnResults.put(columnRequest.getColumnName(), columnIterable);
    }

    mColumnResults = columnResults.build();
  }

  /** {@inheritDoc} */
  @Override
  public EntityId getEntityId() {
    return mEntityId;
  }

  /** {@inheritDoc} */
  @Override
  public KijiDataRequest getDataRequest() {
    return mDataRequest;
  }

  /** {@inheritDoc} */
  @Override
  public Iterator<KijiCell<T>> iterator() {
    if (!mDebugRegistered) {
      DebugResourceTracker.get().registerResource(this);
      mDebugRegistered = true;
    }
    return Iterables.concat(mColumnResults.values()).iterator();
  }

  /** {@inheritDoc} */
  @Override
  @SuppressWarnings("unchecked")
  public <U extends T> HBasePagedKijiResult<U> narrowView(final KijiColumnName column) {
    final KijiDataRequest narrowRequest = DefaultKijiResult.narrowRequest(column, mDataRequest);

    return new HBasePagedKijiResult<U>(
        mEntityId,
        narrowRequest,
        mTable,
        mLayout,
        mColumnTranslator,
        mDecoderProvider);
  }

  /** {@inheritDoc} */
  @Override
  public void close() throws IOException {
    try {
      mCloser.close();
    } finally {
      if (mDebugRegistered) {
        DebugResourceTracker.get().unregisterResource(this);
        mDebugRegistered = false;
      }
    }
  }

  // -----------------------------------------------------------------------------------------------
  // Helper classes and methods
  // -----------------------------------------------------------------------------------------------

  /**
   * An iterable which starts an HBase scan for each requested iterator.
   */
  public final class PagedColumnIterable implements Iterable<KijiCell<T>>, Closeable {
    private final KijiColumnName mColumn;
    private final Scan mScan;
    private final Closer mCloser;

    /**
     * Creates an iterable which starts an HBase scan for each requested iterator.
     *
     * @param columnRequest of column to scan.
     */
    private PagedColumnIterable(final Column columnRequest) {
      mColumn = columnRequest.getColumnName();
      mCloser = Closer.create();

      try {
        final KijiColumnFilter.Context filterContext =
            new NameTranslatingFilterContext(mColumnTranslator);
        final KijiColumnFilter kijiFilter = columnRequest.getFilter();
        final Filter filter;
        if (kijiFilter != null) {
          filter = kijiFilter.toHBaseFilter(mColumn, filterContext);
        } else {
          filter = null;
        }

        final byte[] rowkey = mEntityId.getHBaseRowKey();
        mScan = new Scan(rowkey, Arrays.copyOf(rowkey, rowkey.length + 1));

        final HBaseColumnName hbaseColumn = mColumnTranslator.toHBaseColumnName(mColumn);

        if (mColumn.isFullyQualified()) {
          mScan.addColumn(hbaseColumn.getFamily(), hbaseColumn.getQualifier());
          mScan.setFilter(filter);
        } else {
          if (Arrays.equals(hbaseColumn.getQualifier(), new byte[0])) {
            // This can happen with the native translator
            mScan.addFamily(hbaseColumn.getFamily());
            mScan.setFilter(filter);
          } else if (hbaseColumn.getQualifier().length == 0) {
            mScan.addFamily(hbaseColumn.getFamily());
            mScan.setFilter(filter);
          }
          mScan.addFamily(hbaseColumn.getFamily());

          final Filter prefixFilter = new ColumnPrefixFilter(hbaseColumn.getQualifier());
          if (filter != null) {
            final FilterList filters = new FilterList(Operator.MUST_PASS_ALL);
            filters.addFilter(prefixFilter);
            filters.addFilter(filter);
            mScan.setFilter(filters);
          } else {
            mScan.setFilter(prefixFilter);
          }
        }

        mScan.setMaxVersions(columnRequest.getMaxVersions());
        mScan.setTimeRange(mDataRequest.getMinTimestamp(), mDataRequest.getMaxTimestamp());
        mScan.setBatch(columnRequest.getPageSize());
      } catch (IOException e) {
        throw new KijiIOException(e);
      }
    }

    /** {@inheritDoc} */
    @Override
    public Iterator<KijiCell<T>> iterator() {
      final HTableInterface htable;
      final ResultScanner scanner;
      try {
        htable = mTable.openHTableConnection();
        mCloser.register(htable);
        scanner = htable.getScanner(mScan);
        mCloser.register(scanner);
      } catch (IOException e) {
        throw new KijiIOException(e);
      }

      // Decoder functions are stateful, so they should not be shared among multiple iterators
      final Function<KeyValue, KijiCell<T>> decoder =
          ResultDecoders.getDecoderFunction(mColumn, mLayout, mColumnTranslator, mDecoderProvider);

      return
          Iterators.concat(
              Iterators.transform(
                  scanner.iterator(),
                  new Function<Result, Iterator<KijiCell<T>>>() {
                    @Override
                    public Iterator<KijiCell<T>> apply(final Result result) {
                      return Iterators.transform(Iterators.forArray(result.raw()), decoder);
                    }
                  }));
    }

    /** {@inheritDoc} */
    @Override
    public void close() throws IOException {
      mCloser.close();
    }
  }
}
TOP

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

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.