org.kiji.schema.mapreduce.KijiDelete.java Source code

Java tutorial

Introduction

Here is the source code for org.kiji.schema.mapreduce.KijiDelete.java

Source

/**
 * (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 org.kiji.schema.mapreduce;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;

import com.google.common.base.Objects;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.hadoop.hbase.client.Delete;

import org.kiji.schema.EntityId;
import org.kiji.schema.HBaseColumnName;
import org.kiji.schema.KijiColumnName;
import org.kiji.schema.NoSuchColumnException;
import org.kiji.schema.impl.HBaseEntityId;
import org.kiji.schema.layout.ColumnNameTranslator;
import org.kiji.schema.layout.KijiTableLayout;
import org.kiji.schema.layout.KijiTableLayout.LocalityGroupLayout.FamilyLayout;

/**
 * KijiDeletes are used to delete rows or specific columns of a row from Kiji Tables.
 */
public class KijiDelete implements KijiMutation {
    private EntityId mEntityId;
    private String mFamily;
    private String mQualifier;
    private Long mTimestamp;
    private KijiDeleteOperation mOperation;

    /**
     * Helper enum to decide what to do with the Delete.
     */
    private enum KijiDeleteOperation {
        // Exact - version specified by timestamp
        // Upto - version until and including this timestamp
        // Latest - latest version
        // All - every version
        CELL_EXACT, CELL_UPTO, CELL_ALL, CELL_LATEST, FAMILY_EXACT, FAMILY_UPTO, FAMILY_ALL, FAMILY_LATEST, ROW_ALL, ROW_EXACT
    }

    /**
     * Enum used while creating KijiDelete to specify the scope
     * of the operation, i.e. all versions/latest/exact.
     */
    public static enum KijiDeleteScope {
        MULTIPLE_VERSIONS, SINGLE_VERSION
    }

    /**
     * Empty constructor for Writable serialization.
     */
    public KijiDelete() {
    }

    /**
     * Creates a new KijiDelete for all versions of a family.
     * This is not supported on map type families.
     *
     * @param entityId The entityId of the row to delete.
     * @param family The family of the column to delete.
     * @param scope Indication to delete all or latest version.
     */
    public KijiDelete(EntityId entityId, String family, KijiDeleteScope scope) {
        this(entityId, family, null, null, null);
        if (KijiDeleteScope.MULTIPLE_VERSIONS == scope) {
            mOperation = KijiDeleteOperation.FAMILY_ALL;
        } else {
            mOperation = KijiDeleteOperation.FAMILY_LATEST;
        }
    }

    /**
     * Creates a new KijiDelete for all versions of the family up to
     * this timestamp. This is not supported on map type families.
     *
     * @param entityId The entityId of the row to delete.
     * @param family The family of the column to delete.
     * @param timestamp The version of the row to delete.
     * @param scope Indication to delete upto a version or the exact version.
     */
    public KijiDelete(EntityId entityId, String family, Long timestamp, KijiDeleteScope scope) {
        this(entityId, family, null, timestamp, scope);
        if (KijiDeleteScope.MULTIPLE_VERSIONS == scope) {
            mOperation = KijiDeleteOperation.FAMILY_UPTO;
        } else {
            mOperation = KijiDeleteOperation.FAMILY_EXACT;
        }
    }

    /**
     * Creates a new KijiDelete for entire row.
     *
     * @param entityId The entityId of the row to delete.
     */
    public KijiDelete(EntityId entityId) {
        this(entityId, null, null, null, null);
        mOperation = KijiDeleteOperation.ROW_ALL;
    }

    /**
     * Creates a new KijiDelete for a specified row and timestamp.
     *
     * @param entityId The entityId of the row to delete.
     * @param timestamp The version of the row to delete.
     */
    public KijiDelete(EntityId entityId, Long timestamp) {
        this(entityId, null, null, timestamp, null);
        mOperation = KijiDeleteOperation.ROW_EXACT;
    }

    /**
     * Creates a new KijiDelete for all versions of specified columns or the latest
     * version (as specified by scope).
     *
     * @param entityId The entityId of the row to delete.
     * @param family The family of the column to delete
     * @param qualifier The qualifier of the column to delete
     * @param scope The scope of delete, i.e. all versions or latest (specified by exact)
     */
    public KijiDelete(EntityId entityId, String family, String qualifier, KijiDeleteScope scope) {
        this(entityId, family, qualifier, null, scope);
        if (KijiDeleteScope.MULTIPLE_VERSIONS == scope) {
            mOperation = KijiDeleteOperation.CELL_UPTO;
        } else {
            mOperation = KijiDeleteOperation.CELL_LATEST;
        }
    }

    /**
     * Creates a new KijiDelete for a specific column within a family.
     * The scope along with the timestamp decides whether to delete all
     * versions up to this timestamp or only the version specified by timestamp.
     *
     * @param entityId The entityId of the row to delete.
     * @param family The family of the column to delete.
     * @param qualifier The qualifier of the column to delete.
     * @param timestamp The version of the row to delete.
     * @param scope The scope of the delete, i.e. up to or the exact version
     */
    public KijiDelete(EntityId entityId, String family, String qualifier, Long timestamp, KijiDeleteScope scope) {
        mEntityId = entityId;
        mFamily = family;
        mQualifier = qualifier;
        mTimestamp = timestamp;
        if (KijiDeleteScope.MULTIPLE_VERSIONS == scope) {
            mOperation = KijiDeleteOperation.CELL_ALL;
        } else {
            mOperation = KijiDeleteOperation.CELL_EXACT;
        }
    }

    /**
     * Helper to convert a Kiji cell to an Hbase cell.
     *
     * @param translator The ColumnNameTranslator used to convert kiji column names to their HBase
     *     counterparts.
     * @return An Hbase column name.
     * @throws NoSuchColumnException If the column does not exist.
     */
    private HBaseColumnName convertKijiCelltoHBaseColumn(ColumnNameTranslator translator)
            throws NoSuchColumnException {
        final KijiColumnName kijiColumnName = new KijiColumnName(mFamily, mQualifier);
        final HBaseColumnName hColumn = translator.toHBaseColumnName(kijiColumnName);
        return hColumn;
    }

    /**
     * Helper to converts mFamily to a FamilyLayout object.
     *
     * @param translator The ColumnNameTranslator used to convert kiji column names to their HBase
     *     counterparts.
     * @return A FamilyLayout object representing this mFamily.
     */
    private FamilyLayout getFamilyLayout(ColumnNameTranslator translator) {
        final KijiTableLayout tLayout = translator.getTableLayout();
        return tLayout.getFamilyMap().get(mFamily);
    }

    /**
     * Builds a HBase Delete from this KijiDelete.
     *
     * @param translator The ColumnNameTranslator used to convert kiji column names to their HBase
     *     counterparts.
     * @return An HBase Delete for this KijiDelete.
     * @throws IOException if there is an error building the HBase Delete.
     */
    public Delete toDelete(ColumnNameTranslator translator) throws IOException {
        // Build HBase Delete.
        final Delete del;
        if (KijiDeleteOperation.ROW_EXACT == mOperation) {
            del = new Delete(mEntityId.getHBaseRowKey(), mTimestamp, null);
        } else {
            del = new Delete(mEntityId.getHBaseRowKey());
        }

        final HBaseColumnName hColumn;
        final FamilyLayout familyLayout;
        switch (mOperation) {
        // Delete all versions of the specified column.
        case CELL_ALL:
            hColumn = convertKijiCelltoHBaseColumn(translator);
            del.deleteColumns(hColumn.getFamily(), hColumn.getQualifier());
            break;
        // Delete the specified version of the specified column.
        case CELL_EXACT:
            hColumn = convertKijiCelltoHBaseColumn(translator);
            del.deleteColumn(hColumn.getFamily(), hColumn.getQualifier(), mTimestamp);
            break;
        // Delete the latest version of a column
        case CELL_LATEST:
            hColumn = convertKijiCelltoHBaseColumn(translator);
            del.deleteColumn(hColumn.getFamily(), hColumn.getQualifier());
            break;
        // Delete all versions of the specified column with a timestamp
        // less than or equal to the specified timestamp.
        case CELL_UPTO:
            hColumn = convertKijiCelltoHBaseColumn(translator);
            del.deleteColumns(hColumn.getFamily(), hColumn.getQualifier(), mTimestamp);
            break;
        // Delete all versions of all columns in the family
        case FAMILY_ALL:
            familyLayout = getFamilyLayout(translator);
            if (!familyLayout.isMapType()) {
                for (FamilyLayout.ColumnLayout columnLayout : familyLayout.getColumns()) {
                    HBaseColumnName hColumnName = translator
                            .toHBaseColumnName(new KijiColumnName(mFamily, columnLayout.getName()));
                    del.deleteColumns(hColumnName.getFamily(), hColumnName.getQualifier());
                }
            } else {
                throw new UnsupportedOperationException("Deleting map type family " + "is not yet supported");
            }
            break;
        // Delete the exact version of all columns in the family as specified by timestamp
        case FAMILY_EXACT:
            familyLayout = getFamilyLayout(translator);
            if (!familyLayout.isMapType()) {
                for (FamilyLayout.ColumnLayout columnLayout : familyLayout.getColumns()) {
                    HBaseColumnName hColumnName = translator
                            .toHBaseColumnName(new KijiColumnName(mFamily, columnLayout.getName()));
                    del.deleteColumn(hColumnName.getFamily(), hColumnName.getQualifier(), mTimestamp);
                }
            } else {
                throw new UnsupportedOperationException("Deleting map type family " + "is not yet supported");
            }
            break;
        // Delete the latest version of all columns in the family
        case FAMILY_LATEST:
            familyLayout = getFamilyLayout(translator);
            if (!familyLayout.isMapType()) {
                for (FamilyLayout.ColumnLayout columnLayout : familyLayout.getColumns()) {
                    HBaseColumnName hColumnName = translator
                            .toHBaseColumnName(new KijiColumnName(mFamily, columnLayout.getName()));
                    del.deleteColumn(hColumnName.getFamily(), hColumnName.getQualifier());
                }
            } else {
                throw new UnsupportedOperationException("Deleting map type family " + "is not yet supported");
            }
            break;
        // Delete all columns of the specified family with a timestamp
        // less than or equal to the specified timestamp.
        case FAMILY_UPTO:
            familyLayout = getFamilyLayout(translator);
            if (!familyLayout.isMapType()) {
                for (FamilyLayout.ColumnLayout columnLayout : familyLayout.getColumns()) {
                    HBaseColumnName hColumnName = translator
                            .toHBaseColumnName(new KijiColumnName(mFamily, columnLayout.getName()));
                    del.deleteColumns(hColumnName.getFamily(), hColumnName.getQualifier(), mTimestamp);
                }
            } else {
                throw new UnsupportedOperationException("Deleting map type family " + "is not yet supported");
            }
            break;
        default:
            // Then it's an operation on the entire row, so we don't need to do specify any
            // column-related information in the HBase Delete.
        }
        return del;
    }

    /**
     * During serialization, if an attribute is null, write the String NULL to DataOutput,
     * else write the actual value.
     *
     * @param toWrite Object to write to DataOutput.
     * @param out Interface to output stream.
     * @throws IOException if an I/O error occurs.
     */
    private void writeOptionalValue(Object toWrite, DataOutput out) throws IOException {
        if (null == toWrite) {
            out.writeUTF("NULL");
            return;
        }
        if (toWrite instanceof KijiDeleteOperation) {
            out.writeUTF(((KijiDeleteOperation) toWrite).name());
            return;
        }
        if (toWrite instanceof Long) {
            out.writeUTF(((Long) toWrite).toString());
            return;
        }
        out.writeUTF((String) toWrite);
    }

    /**
     * During deserialization, read attributes that may have been NULL while serializing.
     *
     * @param in Interface to input stream.
     * @param type The class to read.
     * @return An object of "type" or null.
     * @throws IOException if an I/O error occurs.
     */
    private Object readOptionalValue(DataInput in, Class<?> type) throws IOException {
        String input = in.readUTF();
        if (input.equals("NULL")) {
            return null;
        }
        if (KijiDeleteOperation.class == type) {
            return KijiDeleteOperation.valueOf(input);
        }
        if (Long.class == type) {
            return new Long(input);
        }
        return input;
    }

    /** {@inheritDoc} */
    @Override
    public void write(DataOutput out) throws IOException {
        // EntityId.
        final byte[] bytes = mEntityId.getHBaseRowKey();
        out.writeInt(bytes.length);
        out.write(bytes);

        // Family/Qualifier/Timestamp.
        writeOptionalValue(mFamily, out);
        writeOptionalValue(mQualifier, out);
        writeOptionalValue(mTimestamp, out);
        writeOptionalValue(mOperation, out);
    }

    /** {@inheritDoc} */
    @Override
    public void readFields(DataInput in) throws IOException {
        // EntityId.
        final byte[] bytes = new byte[in.readInt()];
        in.readFully(bytes);
        mEntityId = new HBaseEntityId(bytes);

        // Family/Qualifier/Timestamp.
        mFamily = (String) readOptionalValue(in, String.class);

        mQualifier = (String) readOptionalValue(in, String.class);
        mTimestamp = (Long) readOptionalValue(in, Long.class);
        mOperation = (KijiDeleteOperation) readOptionalValue(in, KijiDeleteOperation.class);
    }

    /** {@inheritDoc} */
    @Override
    public boolean equals(Object other) {
        if (null == other || !(other instanceof KijiDelete)) {
            return false;
        }
        final KijiDelete o = (KijiDelete) other;
        return new EqualsBuilder().append(mEntityId, o.mEntityId).append(mFamily, o.mFamily)
                .append(mQualifier, o.mQualifier).append(mTimestamp, o.mTimestamp).append(mOperation, o.mOperation)
                .isEquals();
    }

    /** {@inheritDoc} */
    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(mEntityId).append(mFamily).append(mQualifier).append(mTimestamp)
                .append(mOperation).hashCode();
    }

    /** {@inheritDoc} */
    @Override
    public String toString() {
        return Objects.toStringHelper(this).add("entityId", mEntityId).add("family", mFamily)
                .add("qualifier", mQualifier).add("timestamp", mTimestamp).add("operation", mOperation).toString();
    }
}