Java tutorial
/** * (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.cassandra; import java.io.IOException; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.SortedMap; import java.util.concurrent.ExecutionException; import com.datastax.driver.core.Statement; import com.datastax.driver.core.exceptions.DriverException; import com.datastax.driver.core.exceptions.DriverInternalError; import com.google.common.base.Preconditions; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.Uninterruptibles; import org.slf4j.Logger; import org.slf4j.LoggerFactory; 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.KijiDataRequestBuilder; import org.kiji.schema.KijiResult; import org.kiji.schema.KijiURI; import org.kiji.schema.NoSuchColumnException; import org.kiji.schema.cassandra.CassandraColumnName; import org.kiji.schema.cassandra.CassandraTableName; import org.kiji.schema.impl.DefaultKijiResult; import org.kiji.schema.impl.EmptyKijiResult; import org.kiji.schema.impl.MaterializedKijiResult; import org.kiji.schema.layout.CassandraColumnNameTranslator; import org.kiji.schema.layout.KijiTableLayout; import org.kiji.schema.layout.impl.CellDecoderProvider; import org.kiji.schema.layout.impl.ColumnId; /** * A utility class which can create a {@link KijiResult} view on a Cassandra Kiji table. */ @ApiAudience.Private public final class CassandraKijiResult { private static final Logger LOG = LoggerFactory.getLogger(CassandraKijiResult.class); /** * Create a new {@link KijiResult} backed by Cassandra. * * @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. * @param <T> The type of value in the {@code KijiCell} of this view. * @return an {@code KijiResult}. * @throws IOException On error while decoding cells. */ public static <T> KijiResult<T> create(final EntityId entityId, final KijiDataRequest dataRequest, final CassandraKijiTable table, final KijiTableLayout layout, final CassandraColumnNameTranslator columnTranslator, final CellDecoderProvider decoderProvider) throws IOException { final KijiDataRequestBuilder unpagedRequestBuilder = KijiDataRequest.builder(); final KijiDataRequestBuilder pagedRequestBuilder = KijiDataRequest.builder(); unpagedRequestBuilder.withTimeRange(dataRequest.getMinTimestamp(), dataRequest.getMaxTimestamp()); pagedRequestBuilder.withTimeRange(dataRequest.getMinTimestamp(), dataRequest.getMaxTimestamp()); for (final Column columnRequest : dataRequest.getColumns()) { if (columnRequest.getFilter() != null) { throw new UnsupportedOperationException(String .format("Cassandra Kiji does not support filters on column requests: %s.", columnRequest)); } if (columnRequest.isPagingEnabled()) { pagedRequestBuilder.newColumnsDef(columnRequest); } else { unpagedRequestBuilder.newColumnsDef(columnRequest); } } final CellDecoderProvider requestDecoderProvider = decoderProvider .getDecoderProviderForRequest(dataRequest); final KijiDataRequest unpagedRequest = unpagedRequestBuilder.build(); final KijiDataRequest pagedRequest = pagedRequestBuilder.build(); if (unpagedRequest.isEmpty() && pagedRequest.isEmpty()) { return new EmptyKijiResult<T>(entityId, dataRequest); } final MaterializedKijiResult<T> materializedKijiResult; if (!unpagedRequest.isEmpty()) { materializedKijiResult = createMaterialized(table.getURI(), entityId, unpagedRequest, layout, columnTranslator, requestDecoderProvider, table.getAdmin()); } else { materializedKijiResult = null; } final CassandraPagedKijiResult<T> pagedKijiResult; if (!pagedRequest.isEmpty()) { pagedKijiResult = new CassandraPagedKijiResult<T>(entityId, pagedRequest, table, layout, columnTranslator, requestDecoderProvider); } else { pagedKijiResult = null; } if (unpagedRequest.isEmpty()) { return pagedKijiResult; } else if (pagedRequest.isEmpty()) { return materializedKijiResult; } else { return DefaultKijiResult.create(dataRequest, materializedKijiResult, pagedKijiResult); } } /** * Create a materialized {@code KijiResult} for a get on a Cassandra Kiji table. * * @param tableURI The table URI. * @param entityId The entity ID of the row to get. * @param dataRequest The data request defining the columns to get. All columns must be non-paged. * @param layout The layout of the table. * @param translator A column name translator for the table. * @param decoderProvider A decoder provider for the table. * @param admin The Cassandra connection. * @param <T> The value type of cells in the result. * @return A materialized {@code KijiResult} for the row. */ public static <T> MaterializedKijiResult<T> createMaterialized(final KijiURI tableURI, final EntityId entityId, final KijiDataRequest dataRequest, final KijiTableLayout layout, final CassandraColumnNameTranslator translator, final CellDecoderProvider decoderProvider, final CassandraAdmin admin) { SortedMap<KijiColumnName, ListenableFuture<Iterator<KijiCell<T>>>> resultFutures = Maps.newTreeMap(); for (final Column columnRequest : dataRequest.getColumns()) { Preconditions.checkArgument(!columnRequest.isPagingEnabled(), "CassandraMaterializedKijiResult can not be created with a paged data request: %s.", dataRequest); resultFutures.put(columnRequest.getColumnName(), CassandraKijiResult.<T>getColumn(tableURI, entityId, columnRequest, dataRequest, layout, translator, decoderProvider, admin)); } SortedMap<KijiColumnName, List<KijiCell<T>>> results = Maps.newTreeMap(); for (Map.Entry<KijiColumnName, ListenableFuture<Iterator<KijiCell<T>>>> entry : resultFutures.entrySet()) { results.put(entry.getKey(), Lists.newArrayList(CassandraKijiResult.unwrapFuture(entry.getValue()))); } return MaterializedKijiResult.create(entityId, dataRequest, layout, results); } // CSOFF: ParameterNumber /** * Query Cassandra for a Kiji qualified-column or column-family in a Kiji row. The result is a * future containing an iterator over the result cells. * * @param tableURI The table URI. * @param entityId The entity ID of the row in the Kiji table. * @param columnRequest The requested column. * @param dataRequest The data request defining the request options. * @param layout The table's layout. * @param translator A column name translator for the table. * @param decoderProvider A decoder provider for the table. * @param admin The Cassandra connection to use for querying. * @param <T> The value type of the column. * @return A future containing an iterator of cells in the column. */ public static <T> ListenableFuture<Iterator<KijiCell<T>>> getColumn(final KijiURI tableURI, final EntityId entityId, final Column columnRequest, final KijiDataRequest dataRequest, final KijiTableLayout layout, final CassandraColumnNameTranslator translator, final CellDecoderProvider decoderProvider, final CassandraAdmin admin) { final KijiColumnName column = columnRequest.getColumnName(); final CassandraColumnName cassandraColumn; try { cassandraColumn = translator.toCassandraColumnName(column); } catch (NoSuchColumnException e) { throw new IllegalArgumentException(String.format("No such column '%s' in table %s.", column, tableURI)); } final ColumnId localityGroupId = layout.getFamilyMap().get(column.getFamily()).getLocalityGroup().getId(); final CassandraTableName table = CassandraTableName.getLocalityGroupTableName(tableURI, localityGroupId); if (column.isFullyQualified()) { final Statement statement = CQLUtils.getQualifiedColumnGetStatement(layout, table, entityId, cassandraColumn, dataRequest, columnRequest); return Futures.transform(admin.executeAsync(statement), RowDecoders.<T>getQualifiedColumnDecoderFunction(column, decoderProvider)); } else { if (columnRequest.getMaxVersions() != 0) { LOG.warn( "Cassandra Kiji can not efficiently get a column family with max versions" + " (column family: {}, max version: {}). Filtering versions on the client.", column, columnRequest.getMaxVersions()); } if (dataRequest.getMaxTimestamp() != Long.MAX_VALUE || dataRequest.getMinTimestamp() != Long.MIN_VALUE) { LOG.warn( "Cassandra Kiji can not efficiently restrict a timestamp on a column family: " + " (column family: {}, data request: {}). Filtering timestamps on the client.", column, dataRequest); } final Statement statement = CQLUtils.getColumnFamilyGetStatement(layout, table, entityId, cassandraColumn, columnRequest); return Futures.transform(admin.executeAsync(statement), RowDecoders.<T>getColumnFamilyDecoderFunction( table, column, columnRequest, dataRequest, layout, translator, decoderProvider)); } } // CSON: ParameterNumber /** * Unwrap a Cassandra listenable future. * * @param future The future to unwrap. * @param <T> The value type of the future. * @return The future's value. */ public static <T> T unwrapFuture(final ListenableFuture<T> future) { // See DefaultResultSetFuture#getUninterruptibly try { return Uninterruptibles.getUninterruptibly(future); } catch (ExecutionException e) { final Throwable cause = e.getCause(); if (cause instanceof DriverException) { throw (DriverException) cause; } else { throw new DriverInternalError("Unexpected exception thrown", cause); } } } /** * Constructor for non-instantiable helper class. */ private CassandraKijiResult() { } }