Java tutorial
/** * 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.cassandra.db; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; import java.util.Collection; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; import org.apache.cassandra.utils.FBUtilities; /** * Author : Avinash Lakshman ( alakshman@facebook.com) & Prashant Malik ( pmalik@facebook.com ) */ public final class SuperColumn implements IColumn, Serializable { private static Logger logger_ = Logger.getLogger(SuperColumn.class); private static SuperColumnSerializer serializer_ = new SuperColumnSerializer(); private final static String seperator_ = ":"; static SuperColumnSerializer serializer() { return serializer_; } private String name_; private EfficientBidiMap columns_ = new EfficientBidiMap( ColumnComparatorFactory.getComparator(ColumnComparatorFactory.ComparatorType.TIMESTAMP)); private int localDeletionTime = Integer.MIN_VALUE; private long markedForDeleteAt = Long.MIN_VALUE; private AtomicInteger size_ = new AtomicInteger(0); SuperColumn() { } SuperColumn(String name) { name_ = name; } public boolean isMarkedForDelete() { return markedForDeleteAt > Long.MIN_VALUE; } public String name() { return name_; } public Collection<IColumn> getSubColumns() { return columns_.getSortedColumns(); } public IColumn getSubColumn(String columnName) { IColumn column = columns_.get(columnName); assert column == null || column instanceof Column; return column; } public int compareTo(IColumn superColumn) { return (name_.compareTo(superColumn.name())); } public int size() { /* * return the size of the individual columns * that make up the super column. This is an * APPROXIMATION of the size used only from the * Memtable. */ return size_.get(); } /** * This returns the size of the super-column when serialized. * @see org.apache.cassandra.db.IColumn#serializedSize() */ public int serializedSize() { /* * Size of a super-column is = * size of a name (UtfPrefix + length of the string) * + 1 byte to indicate if the super-column has been deleted * + 4 bytes for size of the sub-columns * + 4 bytes for the number of sub-columns * + size of all the sub-columns. */ /* * We store the string as UTF-8 encoded, so when we calculate the length, it * should be converted to UTF-8. */ /* * We need to keep the way we are calculating the column size in sync with the * way we are calculating the size for the column family serializer. */ return IColumn.UtfPrefix_ + FBUtilities.getUTF8Length(name_) + DBConstants.boolSize_ + DBConstants.intSize_ + DBConstants.intSize_ + getSizeOfAllColumns(); } /** * This calculates the exact size of the sub columns on the fly */ int getSizeOfAllColumns() { int size = 0; Collection<IColumn> subColumns = getSubColumns(); for (IColumn subColumn : subColumns) { size += subColumn.serializedSize(); } return size; } public void remove(String columnName) { columns_.remove(columnName); } public long timestamp() { throw new UnsupportedOperationException("This operation is not supported for Super Columns."); } public long timestamp(String key) { IColumn column = columns_.get(key); if (column instanceof SuperColumn) throw new UnsupportedOperationException("A super column cannot hold other super columns."); if (column != null) return column.timestamp(); throw new IllegalArgumentException("Timestamp was requested for a column that does not exist."); } public byte[] value() { throw new UnsupportedOperationException("This operation is not supported for Super Columns."); } public byte[] value(String key) { IColumn column = columns_.get(key); if (column != null) return column.value(); throw new IllegalArgumentException("Value was requested for a column that does not exist."); } public void addColumn(String name, IColumn column) { if (column instanceof SuperColumn) throw new UnsupportedOperationException("A super column cannot hold other super columns."); IColumn oldColumn = columns_.get(name); if (oldColumn == null) { columns_.put(name, column); size_.addAndGet(column.size()); } else { if (oldColumn.timestamp() <= column.timestamp()) { columns_.put(name, column); int delta = (-1) * oldColumn.size(); /* subtract the size of the oldColumn */ size_.addAndGet(delta); /* add the size of the new column */ size_.addAndGet(column.size()); } } } /* * Go through each sub column if it exists then as it to resolve itself * if the column does not exist then create it. */ public void putColumn(IColumn column) { if (!(column instanceof SuperColumn)) throw new UnsupportedOperationException("Only Super column objects should be put here"); if (!name_.equals(column.name())) throw new IllegalArgumentException( "The name should match the name of the current column or super column"); for (IColumn subColumn : column.getSubColumns()) { addColumn(subColumn.name(), subColumn); } markedForDeleteAt = Math.max(markedForDeleteAt, column.getMarkedForDeleteAt()); } public int getObjectCount() { return 1 + columns_.size(); } public long getMarkedForDeleteAt() { return markedForDeleteAt; } int getColumnCount() { return columns_.size(); } public IColumn diff(IColumn column) { IColumn columnDiff = new SuperColumn(column.name()); Collection<IColumn> columns = column.getSubColumns(); for (IColumn subColumn : columns) { IColumn columnInternal = columns_.get(subColumn.name()); if (columnInternal == null) { columnDiff.addColumn(subColumn.name(), subColumn); } else { IColumn subColumnDiff = columnInternal.diff(subColumn); if (subColumnDiff != null) { columnDiff.addColumn(subColumn.name(), subColumnDiff); } } } if (columnDiff.getSubColumns().size() != 0) return columnDiff; else return null; } public byte[] digest() { Set<IColumn> columns = columns_.getSortedColumns(); byte[] xorHash = ArrayUtils.EMPTY_BYTE_ARRAY; if (name_ == null) return xorHash; xorHash = name_.getBytes(); for (IColumn column : columns) { xorHash = FBUtilities.xor(xorHash, column.digest()); } return xorHash; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("SuperColumn("); sb.append(name_); if (isMarkedForDelete()) { sb.append(" -delete at " + getMarkedForDeleteAt() + "-"); } sb.append(" ["); sb.append(StringUtils.join(getSubColumns(), ", ")); sb.append("])"); return sb.toString(); } public int getLocalDeletionTime() { return localDeletionTime; } public void markForDeleteAt(int localDeleteTime, long timestamp) { this.localDeletionTime = localDeleteTime; this.markedForDeleteAt = timestamp; } } class SuperColumnSerializer implements ICompactSerializer2<IColumn> { public void serialize(IColumn column, DataOutputStream dos) throws IOException { SuperColumn superColumn = (SuperColumn) column; dos.writeUTF(superColumn.name()); dos.writeInt(superColumn.getLocalDeletionTime()); dos.writeLong(superColumn.getMarkedForDeleteAt()); Collection<IColumn> columns = column.getSubColumns(); int size = columns.size(); dos.writeInt(size); dos.writeInt(superColumn.getSizeOfAllColumns()); for (IColumn subColumn : columns) { Column.serializer().serialize(subColumn, dos); } } /* * Use this method to create a bare bones Super Column. This super column * does not have any of the Column information. */ private SuperColumn defreezeSuperColumn(DataInputStream dis) throws IOException { String name = dis.readUTF(); SuperColumn superColumn = new SuperColumn(name); superColumn.markForDeleteAt(dis.readInt(), dis.readLong()); return superColumn; } public IColumn deserialize(DataInputStream dis) throws IOException { SuperColumn superColumn = defreezeSuperColumn(dis); fillSuperColumn(superColumn, dis); return superColumn; } public void skip(DataInputStream dis) throws IOException { defreezeSuperColumn(dis); /* read the number of columns stored */ dis.readInt(); /* read the size of all columns to skip */ int size = dis.readInt(); dis.skip(size); } private void fillSuperColumn(IColumn superColumn, DataInputStream dis) throws IOException { assert dis.available() != 0; /* read the number of columns */ int size = dis.readInt(); /* read the size of all columns */ dis.readInt(); for (int i = 0; i < size; ++i) { IColumn subColumn = Column.serializer().deserialize(dis); superColumn.addColumn(subColumn.name(), subColumn); } } public IColumn deserialize(DataInputStream dis, IFilter filter) throws IOException { if (dis.available() == 0) return null; IColumn superColumn = defreezeSuperColumn(dis); superColumn = filter.filter(superColumn, dis); if (superColumn != null) { fillSuperColumn(superColumn, dis); return superColumn; } else { /* read the number of columns stored */ dis.readInt(); /* read the size of all columns to skip */ int size = dis.readInt(); dis.skip(size); return null; } } /* * Deserialize a particular column since the name is in the form of * superColumn:column. */ public IColumn deserialize(DataInputStream dis, String name, IFilter filter) throws IOException { if (dis.available() == 0) return null; String[] names = RowMutation.getColumnAndColumnFamily(name); if (names.length == 1) { IColumn superColumn = defreezeSuperColumn(dis); if (name.equals(superColumn.name())) { /* read the number of columns stored */ int size = dis.readInt(); /* read the size of all columns */ dis.readInt(); IColumn column = null; for (int i = 0; i < size; ++i) { column = Column.serializer().deserialize(dis, filter); if (column != null) { superColumn.addColumn(column.name(), column); column = null; if (filter.isDone()) { break; } } } return superColumn; } else { /* read the number of columns stored */ dis.readInt(); /* read the size of all columns to skip */ int size = dis.readInt(); dis.skip(size); return null; } } SuperColumn superColumn = defreezeSuperColumn(dis); if (!superColumn.isMarkedForDelete()) { int size = dis.readInt(); /* skip the size of the columns */ dis.readInt(); if (size > 0) { for (int i = 0; i < size; ++i) { IColumn subColumn = Column.serializer().deserialize(dis, names[1], filter); if (subColumn != null) { superColumn.addColumn(subColumn.name(), subColumn); break; } } } } return superColumn; } }