com.facebook.infrastructure.db.ColumnFamily.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.infrastructure.db.ColumnFamily.java

Source

/**
 * 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 com.facebook.infrastructure.db;

import com.facebook.infrastructure.config.DatabaseDescriptor;
import com.facebook.infrastructure.utils.FBUtilities;
import org.apache.log4j.Logger;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;

import java.lang.reflect.Proxy;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com )
 */

public final class ColumnFamily {
    private static ColumnFamilySerializer serializer_;
    public static final short utfPrefix_ = 2;
    /* The column serializer for this Column Family. Create based on config. */

    private static Logger logger_ = Logger.getLogger(ColumnFamily.class);
    private static Map<String, String> columnTypes_ = new HashMap<String, String>();
    private static Map<String, String> indexTypes_ = new HashMap<String, String>();

    static {
        serializer_ = new ColumnFamilySerializer();
        /* TODO: These are the various column types. Hard coded for now. */
        columnTypes_.put("Standard", "Standard");
        columnTypes_.put("Super", "Super");

        indexTypes_.put("None", "None");
        indexTypes_.put("Name", "Name");
        indexTypes_.put("Time", "Time");
    }

    public static ColumnFamilySerializer serializer() {
        return serializer_;
    }

    /*
     * This method returns the serializer whose methods are
     * preprocessed by a dynamic proxy.
    */
    public static ICompactSerializer2<ColumnFamily> serializerWithIndexes() {
        return (ICompactSerializer2<ColumnFamily>) Proxy.newProxyInstance(ColumnFamily.class.getClassLoader(),
                new Class[] { ICompactSerializer2.class },
                new ColumnIndexInvocationHandler<ColumnFamily>(serializer_));
    }

    public static String getColumnType(String key) {
        if (key == null)
            return columnTypes_.get("Standard");
        return columnTypes_.get(key);
    }

    public static String getColumnIndexProperty(String columnIndexProperty) {
        if (columnIndexProperty == null)
            return indexTypes_.get("None");
        return indexTypes_.get(columnIndexProperty);
    }

    private transient AbstractColumnFactory columnFactory_;

    private String name_;

    private transient ICompactSerializer2<IColumn> columnSerializer_;
    private long markedForDeleteAt = Long.MIN_VALUE;
    private AtomicInteger size_ = new AtomicInteger(0);
    private EfficientBidiMap columns_;

    private Comparator<IColumn> columnComparator_;

    private Comparator<IColumn> getColumnComparator(String cfName, String columnType) {
        if (columnComparator_ == null) {
            /*
             * if this columnfamily has supercolumns or there is an index on the column name,
             * then sort by name
            */
            if ("Super".equals(columnType) || DatabaseDescriptor.isNameIndexEnabled(cfName)) {
                columnComparator_ = ColumnComparatorFactory
                        .getComparator(ColumnComparatorFactory.ComparatorType.NAME);
            }
            /* if this columnfamily has simple columns, and no index on name sort by timestamp */
            else {
                columnComparator_ = ColumnComparatorFactory
                        .getComparator(ColumnComparatorFactory.ComparatorType.TIMESTAMP);
            }
        }

        return columnComparator_;
    }

    /* CTOR for JAXB */
    private ColumnFamily() {
    }

    public ColumnFamily(String cfName) {
        name_ = cfName;
        createColumnFactoryAndColumnSerializer();
    }

    public ColumnFamily(String cfName, String columnType) {
        this(cfName);
        createColumnFactoryAndColumnSerializer(columnType);
    }

    void createColumnFactoryAndColumnSerializer(String columnType) {
        if (columnFactory_ == null) {
            columnFactory_ = AbstractColumnFactory.getColumnFactory(columnType);
            columnSerializer_ = columnFactory_.createColumnSerializer();
            if (columns_ == null)
                columns_ = new EfficientBidiMap(getColumnComparator(name_, columnType));
        }
    }

    void createColumnFactoryAndColumnSerializer() {
        String columnType = DatabaseDescriptor.getColumnFamilyType(name_);
        if (columnType == null) {
            List<String> tables = DatabaseDescriptor.getTables();
            if (tables.size() > 0) {
                String table = tables.get(0);
                columnType = Table.open(table).getColumnFamilyType(name_);
            }
        }
        createColumnFactoryAndColumnSerializer(columnType);
    }

    ColumnFamily cloneMe() {
        ColumnFamily cf = new ColumnFamily(name_);
        cf.markedForDeleteAt = markedForDeleteAt;
        cf.columns_ = columns_.cloneMe();
        return cf;
    }

    public String name() {
        return name_;
    }

    /*
     *  We need to go through each column
     *  in the column family and resolve it before adding
    */
    void addColumns(ColumnFamily cf) {
        for (IColumn column : cf.getColumns().values()) {
            addColumn(column);
        }
    }

    public ICompactSerializer2<IColumn> getColumnSerializer() {
        createColumnFactoryAndColumnSerializer();
        return columnSerializer_;
    }

    public IColumn addColumn(String name) {
        IColumn column = columnFactory_.createColumn(name);
        addColumn(column);
        return column;
    }

    //    int getColumnCount()
    //    {
    //       int count = 0;
    //       Set<IColumn> columns = columns_.getSortedColumns();
    //       if( columns != null )
    //       {
    //          for(IColumn column: columns)
    //          {
    //             count = count + column.getObjectCount();
    //          }
    //       }
    //       return count;
    //    }

    int getColumnCount() {
        int count = 0;
        Map<String, IColumn> columns = columns_.getColumns();
        if (columns != null) {
            if (!isSuper()) {
                count = columns.size();
            } else {
                Collection<IColumn> values = columns.values();
                for (IColumn column : values) {
                    count += column.getObjectCount();
                }
            }
        }
        return count;
    }

    public boolean isSuper() {
        return DatabaseDescriptor.getColumnType(name_).equals("Super");
    }

    public IColumn addColumn(String name, byte[] value) {
        return addColumn(name, value, 0);
    }

    public IColumn addColumn(String name, byte[] value, long timestamp) {
        return addColumn(name, value, timestamp, false);
    }

    public IColumn addColumn(String name, byte[] value, long timestamp, boolean deleted) {
        IColumn column = columnFactory_.createColumn(name, value, timestamp, deleted);
        addColumn(column);
        return column;
    }

    void clear() {
        columns_.clear();
    }

    /*
     * If we find an old column that has the same name
     * the ask it to resolve itself else add the new column .
    */
    void addColumn(IColumn column) {
        String name = column.name();
        IColumn oldColumn = columns_.get(name);
        if (oldColumn != null) {
            if (oldColumn instanceof SuperColumn) {
                int oldSize = oldColumn.size();
                ((SuperColumn) oldColumn).integrate(column);
                size_.addAndGet(oldColumn.size() - oldSize);
            } else {
                if (oldColumn.timestamp() <= column.timestamp()) {
                    columns_.put(name, column);
                    size_.addAndGet(column.size());
                }
            }
        } else {
            size_.addAndGet(column.size());
            columns_.put(name, column);
        }
    }

    public IColumn getColumn(String name) {
        return columns_.get(name);
    }

    public SortedSet<IColumn> getAllColumns() {
        return columns_.getSortedColumns();
    }

    Map<String, IColumn> getColumns() {
        return columns_.getColumns();
    }

    public void remove(String columnName) {
        columns_.remove(columnName);
    }

    void delete(long timestamp) {
        markedForDeleteAt = timestamp;
    }

    /* TODO this is tempting to misuse: the goal is to send a timestamp to the nodes w/ the data,
       saying to delete all applicable columns.  other than that it has no meaning!
       Thus, this should really be part of the RowMutation message. */
    public boolean isMarkedForDelete() {
        return markedForDeleteAt > Long.MIN_VALUE;
    }

    /*
     * This is used as oldCf.merge(newCf). Basically we take the newCf
     * and merge it into the oldCf.
    */
    void merge(ColumnFamily columnFamily) {
        Map<String, IColumn> columns = columnFamily.getColumns();
        Set<String> cNames = columns.keySet();

        for (String cName : cNames) {
            columns_.put(cName, columns.get(cName));
        }
    }

    /*
     * This function will repair a list of columns
     * If there are any columns in the external list which are not present
     * internally then they are added ( this might have to change depending on
     * how we implement delete repairs).
     * Also if there are any columns in teh internal and not in the external
     * they are kept intact.
     * Else the one with the greatest timestamp is considered latest.
     */
    void repair(ColumnFamily columnFamily) {
        for (IColumn column : columnFamily.getAllColumns()) {
            addColumn(column);
        }
    }

    /*
     * This function will calculate the differnce between 2 column families
     * the external input is considered the superset of internal
     * so there are no deletes in the diff.
     */
    ColumnFamily diff(ColumnFamily columnFamily) {
        ColumnFamily cfDiff = new ColumnFamily(columnFamily.name());
        Map<String, IColumn> columns = columnFamily.getColumns();
        Set<String> cNames = columns.keySet();

        for (String cName : cNames) {
            IColumn columnInternal = columns_.get(cName);
            IColumn columnExternal = columns.get(cName);
            if (columnInternal == null) {
                cfDiff.addColumn(columnExternal);
            } else {
                IColumn columnDiff = columnInternal.diff(columnExternal);
                if (columnDiff != null) {
                    cfDiff.addColumn(columnDiff);
                }
            }
        }
        if (cfDiff.getColumns().size() != 0)
            return cfDiff;
        else
            return null;
    }

    int size() {
        if (size_.get() == 0) {
            Set<String> cNames = columns_.getColumns().keySet();
            for (String cName : cNames) {
                size_.addAndGet(columns_.get(cName).size());
            }
        }
        return size_.get();
    }

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

    public boolean equals(Object o) {
        if (!(o instanceof ColumnFamily))
            return false;
        ColumnFamily cf = (ColumnFamily) o;
        return name().equals(cf.name());
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("ColumnFamily(");
        sb.append(name_);

        if (isMarkedForDelete()) {
            sb.append("-delete at " + getMarkedForDeleteAt() + "-");
        }

        sb.append(" [");
        sb.append(StringUtils.join(getAllColumns(), ", "));
        sb.append("])");

        return sb.toString();
    }

    public byte[] digest() {
        Set<IColumn> columns = columns_.getSortedColumns();
        byte[] xorHash = ArrayUtils.EMPTY_BYTE_ARRAY;
        for (IColumn column : columns) {
            if (xorHash.length == 0) {
                xorHash = column.digest();
            } else {
                byte[] tmpHash = column.digest();
                xorHash = FBUtilities.xor(xorHash, tmpHash);
            }
        }
        return xorHash;
    }

    public long getMarkedForDeleteAt() {
        return markedForDeleteAt;
    }
}