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.hadoop.hbase.client; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.NavigableMap; import java.util.TreeMap; import java.util.UUID; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.CellScannable; import org.apache.hadoop.hbase.CellScanner; import org.apache.hadoop.hbase.CellUtil; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.KeyValueUtil; import org.apache.hadoop.hbase.Tag; import org.apache.hadoop.hbase.exceptions.DeserializationException; import org.apache.hadoop.hbase.io.HeapSize; import org.apache.hadoop.hbase.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.security.access.AccessControlConstants; import org.apache.hadoop.hbase.security.access.Permission; import org.apache.hadoop.hbase.security.visibility.CellVisibility; import org.apache.hadoop.hbase.security.visibility.VisibilityConstants; import org.apache.hadoop.hbase.util.Bytes; import org.apache.hadoop.hbase.util.ClassSize; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Lists; import com.google.common.io.ByteArrayDataInput; import com.google.common.io.ByteArrayDataOutput; import com.google.common.io.ByteStreams; @InterfaceAudience.Public @InterfaceStability.Evolving public abstract class Mutation extends OperationWithAttributes implements Row, CellScannable, HeapSize { public static final long MUTATION_OVERHEAD = ClassSize.align( // This ClassSize.OBJECT + // row + OperationWithAttributes.attributes 2 * ClassSize.REFERENCE + // Timestamp 1 * Bytes.SIZEOF_LONG + // durability ClassSize.REFERENCE + // familyMap ClassSize.REFERENCE + // familyMap ClassSize.TREEMAP); /** * The attribute for storing the list of clusters that have consumed the change. */ private static final String CONSUMED_CLUSTER_IDS = "_cs.id"; protected byte[] row = null; protected long ts = HConstants.LATEST_TIMESTAMP; protected Durability durability = Durability.USE_DEFAULT; // A Map sorted by column family. protected NavigableMap<byte[], List<Cell>> familyMap = new TreeMap<byte[], List<Cell>>(Bytes.BYTES_COMPARATOR); @Override public CellScanner cellScanner() { return CellUtil.createCellScanner(getFamilyCellMap()); } /** * Creates an empty list if one doesn't exist for the given column family * or else it returns the associated list of Cell objects. * * @param family column family * @return a list of Cell objects, returns an empty list if one doesn't exist. */ List<Cell> getCellList(byte[] family) { List<Cell> list = this.familyMap.get(family); if (list == null) { list = new ArrayList<Cell>(); } return list; } /* * Create a KeyValue with this objects row key and the Put identifier. * * @return a KeyValue with this objects row key and the Put identifier. */ KeyValue createPutKeyValue(byte[] family, byte[] qualifier, long ts, byte[] value) { return new KeyValue(this.row, family, qualifier, ts, KeyValue.Type.Put, value); } /** * Create a KeyValue with this objects row key and the Put identifier. * @param family * @param qualifier * @param ts * @param value * @param tags - Specify the Tags as an Array {@link KeyValue.Tag} * @return a KeyValue with this objects row key and the Put identifier. */ KeyValue createPutKeyValue(byte[] family, byte[] qualifier, long ts, byte[] value, Tag[] tags) { KeyValue kvWithTag = new KeyValue(this.row, family, qualifier, ts, value, tags); return kvWithTag; } /* * Create a KeyValue with this objects row key and the Put identifier. * * @return a KeyValue with this objects row key and the Put identifier. */ KeyValue createPutKeyValue(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value, Tag[] tags) { return new KeyValue(this.row, 0, this.row == null ? 0 : this.row.length, family, 0, family == null ? 0 : family.length, qualifier, ts, KeyValue.Type.Put, value, tags != null ? Arrays.asList(tags) : null); } /** * Compile the column family (i.e. schema) information * into a Map. Useful for parsing and aggregation by debugging, * logging, and administration tools. * @return Map */ @Override public Map<String, Object> getFingerprint() { Map<String, Object> map = new HashMap<String, Object>(); List<String> families = new ArrayList<String>(); // ideally, we would also include table information, but that information // is not stored in each Operation instance. map.put("families", families); for (Map.Entry<byte[], List<Cell>> entry : this.familyMap.entrySet()) { families.add(Bytes.toStringBinary(entry.getKey())); } return map; } /** * Compile the details beyond the scope of getFingerprint (row, columns, * timestamps, etc.) into a Map along with the fingerprinted information. * Useful for debugging, logging, and administration tools. * @param maxCols a limit on the number of columns output prior to truncation * @return Map */ @Override public Map<String, Object> toMap(int maxCols) { // we start with the fingerprint map and build on top of it. Map<String, Object> map = getFingerprint(); // replace the fingerprint's simple list of families with a // map from column families to lists of qualifiers and kv details Map<String, List<Map<String, Object>>> columns = new HashMap<String, List<Map<String, Object>>>(); map.put("families", columns); map.put("row", Bytes.toStringBinary(this.row)); int colCount = 0; // iterate through all column families affected for (Map.Entry<byte[], List<Cell>> entry : this.familyMap.entrySet()) { // map from this family to details for each cell affected within the family List<Map<String, Object>> qualifierDetails = new ArrayList<Map<String, Object>>(); columns.put(Bytes.toStringBinary(entry.getKey()), qualifierDetails); colCount += entry.getValue().size(); if (maxCols <= 0) { continue; } // add details for each cell for (Cell cell : entry.getValue()) { if (--maxCols <= 0) { continue; } // KeyValue v1 expectation. Cast for now until we go all Cell all the time. KeyValue kv = KeyValueUtil.ensureKeyValue(cell); Map<String, Object> kvMap = kv.toStringMap(); // row and family information are already available in the bigger map kvMap.remove("row"); kvMap.remove("family"); qualifierDetails.add(kvMap); } } map.put("totalColumns", colCount); // add the id if set if (getId() != null) { map.put("id", getId()); } return map; } /** * @deprecated Use {@link #getDurability()} instead. * @return true if edits should be applied to WAL, false if not */ @Deprecated public boolean getWriteToWAL() { return this.durability == Durability.SKIP_WAL; } /** * Set whether this Delete should be written to the WAL or not. * Not writing the WAL means you may lose edits on server crash. * This method will reset any changes made via {@link #setDurability(Durability)} * @param write true if edits should be written to WAL, false if not * @deprecated Use {@link #setDurability(Durability)} instead. */ @Deprecated public void setWriteToWAL(boolean write) { setDurability(write ? Durability.USE_DEFAULT : Durability.SKIP_WAL); } /** * Set the durability for this mutation * @param d */ public void setDurability(Durability d) { this.durability = d; } /** Get the current durability */ public Durability getDurability() { return this.durability; } /** * Method for retrieving the put's familyMap * @return familyMap */ public NavigableMap<byte[], List<Cell>> getFamilyCellMap() { return this.familyMap; } /** * Method for setting the put's familyMap */ public void setFamilyCellMap(NavigableMap<byte[], List<Cell>> map) { // TODO: Shut this down or move it up to be a Constructor. Get new object rather than change // this internal data member. this.familyMap = map; } /** * Method for retrieving the put's familyMap that is deprecated and inefficient. * @return the map * @deprecated use {@link #getFamilyCellMap()} instead. */ @Deprecated public NavigableMap<byte[], List<KeyValue>> getFamilyMap() { TreeMap<byte[], List<KeyValue>> fm = new TreeMap<byte[], List<KeyValue>>(Bytes.BYTES_COMPARATOR); for (Map.Entry<byte[], List<Cell>> e : familyMap.entrySet()) { List<KeyValue> kvl = new ArrayList<KeyValue>(e.getValue().size()); for (Cell c : e.getValue()) { kvl.add(KeyValueUtil.ensureKeyValue(c)); } fm.put(e.getKey(), kvl); } return fm; } /** * Method for setting the put's familyMap that is deprecated and inefficient. * @deprecated use {@link #setFamilyCellMap(NavigableMap)} instead. */ @Deprecated public void setFamilyMap(NavigableMap<byte[], List<KeyValue>> map) { TreeMap<byte[], List<Cell>> fm = new TreeMap<byte[], List<Cell>>(Bytes.BYTES_COMPARATOR); for (Map.Entry<byte[], List<KeyValue>> e : map.entrySet()) { fm.put(e.getKey(), Lists.<Cell>newArrayList(e.getValue())); } this.familyMap = fm; } /** * Method to check if the familyMap is empty * @return true if empty, false otherwise */ public boolean isEmpty() { return familyMap.isEmpty(); } /** * Method for retrieving the delete's row * @return row */ @Override public byte[] getRow() { return this.row; } @Override public int compareTo(final Row d) { return Bytes.compareTo(this.getRow(), d.getRow()); } /** * Method for retrieving the timestamp * @return timestamp */ public long getTimeStamp() { return this.ts; } /** * Marks that the clusters with the given clusterIds have consumed the mutation * @param clusterIds of the clusters that have consumed the mutation */ public void setClusterIds(List<UUID> clusterIds) { ByteArrayDataOutput out = ByteStreams.newDataOutput(); out.writeInt(clusterIds.size()); for (UUID clusterId : clusterIds) { out.writeLong(clusterId.getMostSignificantBits()); out.writeLong(clusterId.getLeastSignificantBits()); } setAttribute(CONSUMED_CLUSTER_IDS, out.toByteArray()); } /** * @return the set of clusterIds that have consumed the mutation */ public List<UUID> getClusterIds() { List<UUID> clusterIds = new ArrayList<UUID>(); byte[] bytes = getAttribute(CONSUMED_CLUSTER_IDS); if (bytes != null) { ByteArrayDataInput in = ByteStreams.newDataInput(bytes); int numClusters = in.readInt(); for (int i = 0; i < numClusters; i++) { clusterIds.add(new UUID(in.readLong(), in.readLong())); } } return clusterIds; } /** * Sets the visibility expression associated with cells in this Mutation. * It is illegal to set <code>CellVisibility</code> on <code>Delete</code> mutation. * @param expression */ public void setCellVisibility(CellVisibility expression) { this.setAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY, ProtobufUtil.toCellVisibility(expression).toByteArray()); } /** * @return CellVisibility associated with cells in this Mutation. * @throws DeserializationException */ public CellVisibility getCellVisibility() throws DeserializationException { byte[] cellVisibilityBytes = this.getAttribute(VisibilityConstants.VISIBILITY_LABELS_ATTR_KEY); if (cellVisibilityBytes == null) return null; return ProtobufUtil.toCellVisibility(cellVisibilityBytes); } /** * Number of KeyValues carried by this Mutation. * @return the total number of KeyValues */ public int size() { int size = 0; for (List<Cell> cells : this.familyMap.values()) { size += cells.size(); } return size; } /** * @return the number of different families */ public int numFamilies() { return familyMap.size(); } /** * @return Calculate what Mutation adds to class heap size. */ @Override public long heapSize() { long heapsize = MUTATION_OVERHEAD; // Adding row heapsize += ClassSize.align(ClassSize.ARRAY + this.row.length); // Adding map overhead heapsize += ClassSize.align(this.familyMap.size() * ClassSize.MAP_ENTRY); for (Map.Entry<byte[], List<Cell>> entry : this.familyMap.entrySet()) { //Adding key overhead heapsize += ClassSize.align(ClassSize.ARRAY + entry.getKey().length); //This part is kinds tricky since the JVM can reuse references if you //store the same value, but have a good match with SizeOf at the moment //Adding value overhead heapsize += ClassSize.align(ClassSize.ARRAYLIST); int size = entry.getValue().size(); heapsize += ClassSize.align(ClassSize.ARRAY + size * ClassSize.REFERENCE); for (Cell cell : entry.getValue()) { KeyValue kv = KeyValueUtil.ensureKeyValue(cell); heapsize += kv.heapSize(); } } heapsize += getAttributeSize(); heapsize += extraHeapSize(); return ClassSize.align(heapsize); } /** * @return The serialized ACL for this operation, or null if none */ public byte[] getACL() { return getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL); } /** * @param user User short name * @param perms Permissions for the user */ public void setACL(String user, Permission perms) { setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL, ProtobufUtil.toUsersAndPermissions(user, perms).toByteArray()); } /** * @param perms A map of permissions for a user or users */ public void setACL(Map<String, Permission> perms) { ListMultimap<String, Permission> permMap = ArrayListMultimap.create(); for (Map.Entry<String, Permission> entry : perms.entrySet()) { permMap.put(entry.getKey(), entry.getValue()); } setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL, ProtobufUtil.toUsersAndPermissions(permMap).toByteArray()); } /** * @return true if ACLs should be evaluated on the cell level first */ public boolean getACLStrategy() { byte[] bytes = getAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL_STRATEGY); if (bytes != null) { return Bytes.equals(bytes, AccessControlConstants.OP_ATTRIBUTE_ACL_STRATEGY_CELL_FIRST); } return false; } /** * @param cellFirstStrategy true if ACLs should be evaluated on the cell * level first, false if ACL should first be checked at the CF and table * levels */ public void setACLStrategy(boolean cellFirstStrategy) { if (cellFirstStrategy) { setAttribute(AccessControlConstants.OP_ATTRIBUTE_ACL_STRATEGY, AccessControlConstants.OP_ATTRIBUTE_ACL_STRATEGY_CELL_FIRST); } } /** * Subclasses should override this method to add the heap size of their own fields. * @return the heap size to add (will be aligned). */ protected long extraHeapSize() { return 0L; } /** * @param row Row to check * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or * > {@link HConstants#MAX_ROW_LENGTH} * @return <code>row</code> */ static byte[] checkRow(final byte[] row) { return checkRow(row, 0, row == null ? 0 : row.length); } /** * @param row Row to check * @param offset * @param length * @throws IllegalArgumentException Thrown if <code>row</code> is empty or null or * > {@link HConstants#MAX_ROW_LENGTH} * @return <code>row</code> */ static byte[] checkRow(final byte[] row, final int offset, final int length) { if (row == null) { throw new IllegalArgumentException("Row buffer is null"); } if (length == 0) { throw new IllegalArgumentException("Row length is 0"); } if (length > HConstants.MAX_ROW_LENGTH) { throw new IllegalArgumentException("Row length " + length + " is > " + HConstants.MAX_ROW_LENGTH); } return row; } static void checkRow(ByteBuffer row) { if (row == null) { throw new IllegalArgumentException("Row buffer is null"); } if (row.remaining() == 0) { throw new IllegalArgumentException("Row length is 0"); } if (row.remaining() > HConstants.MAX_ROW_LENGTH) { throw new IllegalArgumentException( "Row length " + row.remaining() + " is > " + HConstants.MAX_ROW_LENGTH); } } }