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.accumulo.core.data; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.accumulo.core.data.thrift.TMutation; import org.apache.accumulo.core.security.ColumnVisibility; import org.apache.accumulo.core.util.ByteBufferUtil; import org.apache.accumulo.core.util.TextUtil; import org.apache.hadoop.io.Text; import org.apache.hadoop.io.Writable; /** * Will read/write old mutations. */ public class OldMutation implements Writable { static final int VALUE_SIZE_COPY_CUTOFF = 1 << 15; private byte[] row; private byte[] data; private int entries; private List<byte[]> values; // created this little class instead of using ByteArrayOutput stream and DataOutputStream // because both are synchronized... lots of small syncs slow things down private static class ByteBuffer { int offset; byte data[] = new byte[64]; private void reserve(int l) { if (offset + l > data.length) { int newSize = data.length * 2; while (newSize <= offset + l) newSize = newSize * 2; byte[] newData = new byte[newSize]; System.arraycopy(data, 0, newData, 0, offset); data = newData; } } void add(byte[] b) { reserve(b.length); System.arraycopy(b, 0, data, offset, b.length); offset += b.length; } public void add(byte[] bytes, int off, int length) { reserve(length); System.arraycopy(bytes, off, data, offset, length); offset += length; } void add(boolean b) { reserve(1); if (b) data[offset++] = 1; else data[offset++] = 0; } void add(long v) { reserve(8); data[offset++] = (byte) (v >>> 56); data[offset++] = (byte) (v >>> 48); data[offset++] = (byte) (v >>> 40); data[offset++] = (byte) (v >>> 32); data[offset++] = (byte) (v >>> 24); data[offset++] = (byte) (v >>> 16); data[offset++] = (byte) (v >>> 8); data[offset++] = (byte) (v >>> 0); } void add(int i) { reserve(4); data[offset++] = (byte) (i >>> 24); data[offset++] = (byte) (i >>> 16); data[offset++] = (byte) (i >>> 8); data[offset++] = (byte) (i >>> 0); } public byte[] toArray() { byte ret[] = new byte[offset]; System.arraycopy(data, 0, ret, 0, offset); return ret; } } private static class SimpleReader { int offset; byte data[]; SimpleReader(byte b[]) { this.data = b; } int readInt() { return (data[offset++] << 24) + ((data[offset++] & 255) << 16) + ((data[offset++] & 255) << 8) + ((data[offset++] & 255) << 0); } long readLong() { return (((long) data[offset++] << 56) + ((long) (data[offset++] & 255) << 48) + ((long) (data[offset++] & 255) << 40) + ((long) (data[offset++] & 255) << 32) + ((long) (data[offset++] & 255) << 24) + ((data[offset++] & 255) << 16) + ((data[offset++] & 255) << 8) + ((data[offset++] & 255) << 0)); } void readBytes(byte b[]) { System.arraycopy(data, offset, b, 0, b.length); offset += b.length; } boolean readBoolean() { return (data[offset++] == 1); } } private ByteBuffer buffer; private List<ColumnUpdate> updates; private static final byte[] EMPTY_BYTES = new byte[0]; private void serialize() { if (buffer != null) { data = buffer.toArray(); buffer = null; } } public OldMutation(Text row) { this.row = new byte[row.getLength()]; System.arraycopy(row.getBytes(), 0, this.row, 0, row.getLength()); buffer = new ByteBuffer(); } public OldMutation(CharSequence row) { this(new Text(row.toString())); } public OldMutation() { } public OldMutation(TMutation tmutation) { this.row = ByteBufferUtil.toBytes(tmutation.row); this.data = ByteBufferUtil.toBytes(tmutation.data); this.entries = tmutation.entries; this.values = ByteBufferUtil.toBytesList(tmutation.values); if (this.row == null) { throw new IllegalArgumentException("null row"); } if (this.data == null) { throw new IllegalArgumentException("null serialized data"); } } public byte[] getRow() { return row; } private void put(byte b[]) { buffer.add(b.length); buffer.add(b); } private void put(Text t) { buffer.add(t.getLength()); buffer.add(t.getBytes(), 0, t.getLength()); } private void put(boolean b) { buffer.add(b); } private void put(int i) { buffer.add(i); } private void put(long l) { buffer.add(l); } private void put(Text cf, Text cq, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val) { if (buffer == null) throw new IllegalStateException("Can not add to mutation after serializing it"); put(cf); put(cq); put(cv); put(hasts); put(ts); put(deleted); if (val.length < VALUE_SIZE_COPY_CUTOFF) { put(val); } else { if (values == null) values = new ArrayList<>(); byte copy[] = new byte[val.length]; System.arraycopy(val, 0, copy, 0, val.length); values.add(copy); put(-1 * values.size()); } entries++; } private void put(CharSequence cf, CharSequence cq, byte[] cv, boolean hasts, long ts, boolean deleted, byte[] val) { put(new Text(cf.toString()), new Text(cq.toString()), cv, hasts, ts, deleted, val); } private void put(CharSequence cf, CharSequence cq, byte[] cv, boolean hasts, long ts, boolean deleted, CharSequence val) { put(cf, cq, cv, hasts, ts, deleted, TextUtil.getBytes(new Text(val.toString()))); } public void put(Text columnFamily, Text columnQualifier, Value value) { put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value.get()); } public void put(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility, Value value) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value.get()); } public void put(Text columnFamily, Text columnQualifier, long timestamp, Value value) { put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value.get()); } public void put(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility, long timestamp, Value value) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value.get()); } public void putDelete(Text columnFamily, Text columnQualifier) { put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, true, EMPTY_BYTES); } public void putDelete(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, true, EMPTY_BYTES); } public void putDelete(Text columnFamily, Text columnQualifier, long timestamp) { put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, true, EMPTY_BYTES); } public void putDelete(Text columnFamily, Text columnQualifier, ColumnVisibility columnVisibility, long timestamp) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, true, EMPTY_BYTES); } public void put(CharSequence columnFamily, CharSequence columnQualifier, Value value) { put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value.get()); } public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, Value value) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value.get()); } public void put(CharSequence columnFamily, CharSequence columnQualifier, long timestamp, Value value) { put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value.get()); } public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, long timestamp, Value value) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value.get()); } public void putDelete(CharSequence columnFamily, CharSequence columnQualifier) { put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, true, EMPTY_BYTES); } public void putDelete(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, true, EMPTY_BYTES); } public void putDelete(CharSequence columnFamily, CharSequence columnQualifier, long timestamp) { put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, true, EMPTY_BYTES); } public void putDelete(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, long timestamp) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, true, EMPTY_BYTES); } public void put(CharSequence columnFamily, CharSequence columnQualifier, CharSequence value) { put(columnFamily, columnQualifier, EMPTY_BYTES, false, 0l, false, value); } public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, CharSequence value) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), false, 0l, false, value); } public void put(CharSequence columnFamily, CharSequence columnQualifier, long timestamp, CharSequence value) { put(columnFamily, columnQualifier, EMPTY_BYTES, true, timestamp, false, value); } public void put(CharSequence columnFamily, CharSequence columnQualifier, ColumnVisibility columnVisibility, long timestamp, CharSequence value) { put(columnFamily, columnQualifier, columnVisibility.getExpression(), true, timestamp, false, value); } private byte[] readBytes(SimpleReader in) { int len = in.readInt(); if (len == 0) return EMPTY_BYTES; byte bytes[] = new byte[len]; in.readBytes(bytes); return bytes; } public List<ColumnUpdate> getUpdates() { serialize(); SimpleReader in = new SimpleReader(data); if (updates == null) { if (entries == 1) { updates = Collections.singletonList(deserializeColumnUpdate(in)); } else { ColumnUpdate[] tmpUpdates = new ColumnUpdate[entries]; for (int i = 0; i < entries; i++) tmpUpdates[i] = deserializeColumnUpdate(in); updates = Arrays.asList(tmpUpdates); } } return updates; } private ColumnUpdate deserializeColumnUpdate(SimpleReader in) { byte[] cf = readBytes(in); byte[] cq = readBytes(in); byte[] cv = readBytes(in); boolean hasts = in.readBoolean(); long ts = in.readLong(); boolean deleted = in.readBoolean(); byte[] val; int valLen = in.readInt(); if (valLen < 0) { val = values.get((-1 * valLen) - 1); } else if (valLen == 0) { val = EMPTY_BYTES; } else { val = new byte[valLen]; in.readBytes(val); } return new ColumnUpdate(cf, cq, cv, hasts, ts, deleted, val); } private int cachedValLens = -1; long getValueLengths() { if (values == null) return 0; if (cachedValLens == -1) { int tmpCVL = 0; for (byte[] val : values) tmpCVL += val.length; cachedValLens = tmpCVL; } return cachedValLens; } public long numBytes() { serialize(); return row.length + data.length + getValueLengths(); } public long estimatedMemoryUsed() { return numBytes() + 230; } /** * @return the number of column value pairs added to the mutation */ public int size() { return entries; } @Override public void readFields(DataInput in) throws IOException { // Clear out cached column updates and value lengths so // that we recalculate them based on the (potentially) new // data we are about to read in. updates = null; cachedValLens = -1; buffer = null; int len = in.readInt(); row = new byte[len]; in.readFully(row); len = in.readInt(); data = new byte[len]; in.readFully(data); entries = in.readInt(); boolean valuesPresent = in.readBoolean(); if (!valuesPresent) { values = null; } else { values = new ArrayList<>(); int numValues = in.readInt(); for (int i = 0; i < numValues; i++) { len = in.readInt(); byte val[] = new byte[len]; in.readFully(val); values.add(val); } } } @Override public void write(DataOutput out) throws IOException { serialize(); out.writeInt(row.length); out.write(row); out.writeInt(data.length); out.write(data); out.writeInt(entries); if (values == null) out.writeBoolean(false); else { out.writeBoolean(true); out.writeInt(values.size()); for (int i = 0; i < values.size(); i++) { byte val[] = values.get(i); out.writeInt(val.length); out.write(val); } } } @Override public boolean equals(Object o) { if (o instanceof OldMutation) return equals((OldMutation) o); return false; } @Override public int hashCode() { return toThrift().hashCode(); } public boolean equals(OldMutation m) { serialize(); if (!Arrays.equals(row, m.getRow())) return false; List<ColumnUpdate> oldcus = this.getUpdates(); List<ColumnUpdate> newcus = m.getUpdates(); if (oldcus.size() != newcus.size()) return false; for (int i = 0; i < newcus.size(); i++) { ColumnUpdate oldcu = oldcus.get(i); ColumnUpdate newcu = newcus.get(i); if (!oldcu.equals(newcu)) return false; } return false; } public TMutation toThrift() { serialize(); return new TMutation(java.nio.ByteBuffer.wrap(row), java.nio.ByteBuffer.wrap(data), ByteBufferUtil.toByteBuffers(values), entries); } }