org.apache.hadoop.hbase.index.IndexSpecification.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.hadoop.hbase.index.IndexSpecification.java

Source

/**
 * Copyright 2011 The Apache Software Foundation
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.apache.hadoop.hbase.index;

import java.io.DataInput;
import java.io.DataOutput;
import java.io.EOFException;
import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.index.ColumnQualifier.ValueType;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.io.WritableComparable;
import org.mortbay.log.Log;

/**
 * An IndexSpecification should have a unique name and can be created on 1 or more columns (Here
 * column refers to columnfamily + qualifier) For each of such column a ColumnQualifier is provided
 * which takes the column details. This includes the column family name and qualifier name. The
 * additional columns are those columns in the main table whose value also will be captured in the
 * secondary index table. Index Specfication name should not start with '.' and '-'. Can contain
 * alphanumerics and '-','.','_'.
 */

public class IndexSpecification implements WritableComparable<IndexSpecification> {

    private byte[] name;

    private Set<ColumnQualifier> indexColumns = new LinkedHashSet<ColumnQualifier>(1);

    private ColumnQualifier lastColumn = null;

    private int totalValueLength = 0;

    private long ttl = -1;

    private int maxVersions = -1;

    // Empty constructor for serialization and deserialization.
    public IndexSpecification() {
    }

    /**
     * @param name should not start with '.' and '-'. Can contain alphanumerics and '-','.','_'.
     * @throws IllegalArgumentException if invalid table name is provided
     */
    public IndexSpecification(String name) {
        validateIndexSpecification(Bytes.toBytes(name));
        this.name = Bytes.toBytes(name);
    }

    private void validateIndexSpecification(byte[] indexSpecName) {
        // throws IllegalArgException if invalid table name is provided
        HTableDescriptor.isLegalTableName(indexSpecName);
    }

    /**
     * @param name should not start with '.' and '-'. Can contain alphanumerics and '-','.','_'.
     * @throws IllegalArgumentException if invalid table name is provided
     */
    public IndexSpecification(byte[] name) {
        validateIndexSpecification(name);
        this.name = name;
    }

    /**
     * @return index name
     */
    public String getName() {
        return Bytes.toString(this.name);
    }

    /**
     * @param cf column family
     * @param qualifier
     * @param type - If type is specified as null then by default ValueType will be taken as String.
     * @param maxValueLength
     * @throws IllegalArgumentException If column family name and/or qualifier is null or blanks.<br/>
     *           If column family name starts with '.',contains control characters or colons.
     * @see ValueType
     */
    public void addIndexColumn(HColumnDescriptor cf, String qualifier, ValueType type, int maxValueLength)
            throws IllegalArgumentException {
        type = checkForType(type);
        isValidFamilyAndQualifier(cf, qualifier);
        maxValueLength = getMaxLength(type, maxValueLength);
        ColumnQualifier cq = new ColumnQualifier(cf.getNameAsString(), qualifier, type, maxValueLength);
        isNotDuplicateEntry(cq);
        formMinTTL(cf);
        formMaxVersions(cf);
        internalAdd(cq);
    }

    private ValueType checkForType(ValueType type) {
        if (type == null) {
            type = ValueType.String;
        }
        return type;
    }

    private int getMaxLength(ValueType type, int maxValueLength) {
        if ((type == ValueType.Int || type == ValueType.Float) && maxValueLength != 4) {
            Log.warn("With integer or float datatypes, the maxValueLength has to be 4 bytes");
            return 4;
        }
        if ((type == ValueType.Double || type == ValueType.Long) && maxValueLength != 8) {
            Log.warn("With Double and Long datatypes, the maxValueLength has to be 8 bytes");
            return 8;
        }
        if ((type == ValueType.Short || type == ValueType.Char) && maxValueLength != 2) {
            Log.warn("With Short and Char datatypes, the maxValueLength has to be 2 bytes");
            return 2;
        }
        if (type == ValueType.Byte && maxValueLength != 1) {
            Log.warn("With Byte datatype, the maxValueLength has to be 1 bytes");
            return 1;
        }
        if (type == ValueType.String && maxValueLength == 0) {
            Log.warn("With String datatype, the minimun value length is 2");
            maxValueLength = 2;
        }
        return maxValueLength;
    }

    /**
     * @param cf Column Family
     * @param qualifier Column Qualifier
     * @param ValuePortion vp
     * @param type Data Type
     * @param maxValueLength
     * @throws IllegalArgumentException
     */
    public void addIndexColumn(HColumnDescriptor cf, String qualifier, ValuePartition vp, ValueType type,
            int maxValueLength) throws IllegalArgumentException {
        checkForType(type);
        isValidFamilyAndQualifier(cf, qualifier);
        maxValueLength = getMaxLength(type, maxValueLength);
        ColumnQualifier cq = new ColumnQualifier(cf.getNameAsString(), qualifier, type, maxValueLength, vp);
        isNotDuplicateEntry(cq);
        formMinTTL(cf);
        formMaxVersions(cf);
        internalAdd(cq);
    }

    private void formMinTTL(HColumnDescriptor cf) {
        int timeToLive = cf.getTimeToLive();
        if (ttl == -1) {
            ttl = timeToLive;
        } else if (timeToLive != HConstants.FOREVER && timeToLive != -1) {
            if (timeToLive < ttl) {
                ttl = timeToLive;
            }
        }
    }

    private void formMaxVersions(HColumnDescriptor cf) {
        int maxVersion = cf.getMaxVersions();
        if (maxVersions == -1) {
            maxVersions = maxVersion;
        } else if (maxVersion != HConstants.FOREVER && maxVersion != -1) {
            if (maxVersion < maxVersions) {
                maxVersions = maxVersion;
            }
        }
    }

    private void internalAdd(ColumnQualifier cq) {
        indexColumns.add(cq);
        lastColumn = cq;
        totalValueLength += cq.getMaxValueLength();
    }

    /**
     * @return List of column specifiers
     */
    public Set<ColumnQualifier> getIndexColumns() {
        return this.indexColumns;
    }

    /**
     * @param cf column family
     * @param qualifier
     * @throws IllegalArgumentException If column family name and/or qualifier is null or blanks
     * @throws IllegalArgumentException If column family name starts with '.',contains control
     *           characters or colons
     */
    private void isValidFamilyAndQualifier(HColumnDescriptor cf, String qualifier) {
        if (null == cf || null == qualifier) {
            throw new IllegalArgumentException("Column family/qualifier should not be null.");
        }
        if (StringUtils.isBlank(cf.getNameAsString()) || StringUtils.isBlank(qualifier)) {
            throw new IllegalArgumentException("Column family/qualifier should not be blank.");
        }
    }

    /**
     * @param ColumnQualifier to check duplicate entry
     */
    private void isNotDuplicateEntry(ColumnQualifier c) {
        if (this.getIndexColumns().contains(c)) {
            throw new IllegalArgumentException(
                    "Duplicate column family and qualifier " + "combination should not be present.");
        }
    }

    /**
     * @param Data Input Stream
     * @throws IOException
     */
    public void readFields(DataInput in) throws IOException {
        this.name = Bytes.readByteArray(in);
        try {
            HTableDescriptor.isLegalTableName(this.name);
        } catch (IllegalArgumentException e) {
            String msg = "Received unexpected data while parsing the column qualifiers :"
                    + Bytes.toString(this.name) + ".";
            Log.warn(msg + " Could be an non-indexed table.");
            throw new EOFException(msg);
        }
        int indexColsSize = in.readInt();
        indexColumns.clear();
        for (int i = 0; i < indexColsSize; i++) {
            ColumnQualifier cq = new ColumnQualifier();
            // Need to revisit this place. May be some other valid value though invalid
            // comes up.
            try {
                cq.readFields(in);
            } catch (IllegalArgumentException e) {
                throw new EOFException("Received unexpected data while parsing the column qualifiers.");
            }
            internalAdd(cq);
        }
        this.maxVersions = in.readInt();
        this.ttl = in.readLong();
    }

    /**
     * @param Data Output Stream
     * @throws IOException
     */
    public void write(DataOutput out) throws IOException {
        Bytes.writeByteArray(out, this.name);
        out.writeInt(this.indexColumns.size());
        for (ColumnQualifier cq : this.indexColumns) {
            cq.write(out);
        }
        out.writeInt(maxVersions);
        out.writeLong(ttl);
    }

    /**
     * @param IndexSpecification
     * @return int
     */
    public int compareTo(IndexSpecification o) {
        return 0;
    }

    public String toString() {
        return "Index : " + getName() + ",Index Columns : " + indexColumns;
    }

    public boolean equals(Object obj) {
        if (obj instanceof IndexSpecification) {
            IndexSpecification other = (IndexSpecification) obj;
            return Bytes.equals(this.name, other.name);
        }
        return false;
    }

    public int hashCode() {
        return Bytes.hashCode(this.name);
    }

    public ColumnQualifier getLastColumn() {
        return this.lastColumn;
    }

    public boolean contains(byte[] family) {
        for (ColumnQualifier qual : indexColumns) {
            if (Bytes.equals(family, qual.getColumnFamily())) {
                return true;
            }
        }
        return false;
    }

    public boolean contains(byte[] family, byte[] qualifier) {
        if (qualifier == null || qualifier.length == 0) {
            return contains(family);
        }
        for (ColumnQualifier qual : indexColumns) {
            if (Bytes.equals(family, qual.getColumnFamily()) && Bytes.equals(qualifier, qual.getQualifier())) {
                return true;
            }
        }
        return false;
    }

    public int getTotalValueLength() {
        return totalValueLength;
    }

    /**
     * Return the minimum of the timeToLive specified for the column families in the specifed index
     * @return
     */
    public long getTTL() {
        return this.ttl;
    }

    /**
     * Return the minimum of the maxVersion specified for the column families in the specified index
     * @return
     */
    public int getMaxVersions() {
        return this.maxVersions;
    }
}