View Javadoc

1   /*
2    * Copyright 2011 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.client;
22  
23  import java.util.ArrayList;
24  import java.util.HashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.TreeMap;
28  import java.util.UUID;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.hbase.HConstants;
33  import org.apache.hadoop.hbase.KeyValue;
34  import org.apache.hadoop.hbase.util.Bytes;
35  
36  import com.google.common.io.ByteArrayDataInput;
37  import com.google.common.io.ByteArrayDataOutput;
38  import com.google.common.io.ByteStreams;
39  
40  public abstract class Mutation extends OperationWithAttributes implements Row {
41    private static final Log LOG = LogFactory.getLog(Mutation.class);
42    // Attribute used in Mutations to indicate the originating cluster.
43    private static final String CLUSTER_ID_ATTR = "_c.id_";
44    private static final String DURABILITY_ID_ATTR = "_dur_";
45  
46    /**
47     * The attribute for storing the list of clusters that have consumed the change.
48     */
49    private static final String CONSUMED_CLUSTER_IDS = "_cs.id";
50    protected byte [] row = null;
51    protected long ts = HConstants.LATEST_TIMESTAMP;
52    protected long lockId = -1L;
53    protected boolean writeToWAL = true;
54    protected Map<byte [], List<KeyValue>> familyMap =
55        new TreeMap<byte [], List<KeyValue>>(Bytes.BYTES_COMPARATOR);
56  
57    /**
58     * Compile the column family (i.e. schema) information
59     * into a Map. Useful for parsing and aggregation by debugging,
60     * logging, and administration tools.
61     * @return Map
62     */
63    @Override
64    public Map<String, Object> getFingerprint() {
65      Map<String, Object> map = new HashMap<String, Object>();
66      List<String> families = new ArrayList<String>();
67      // ideally, we would also include table information, but that information
68      // is not stored in each Operation instance.
69      map.put("families", families);
70      for (Map.Entry<byte [], List<KeyValue>> entry : this.familyMap.entrySet()) {
71        families.add(Bytes.toStringBinary(entry.getKey()));
72      } 
73      return map;
74    }
75  
76    /**
77     * Compile the details beyond the scope of getFingerprint (row, columns,
78     * timestamps, etc.) into a Map along with the fingerprinted information.
79     * Useful for debugging, logging, and administration tools.
80     * @param maxCols a limit on the number of columns output prior to truncation
81     * @return Map
82     */
83    @Override
84    public Map<String, Object> toMap(int maxCols) {
85      // we start with the fingerprint map and build on top of it.
86      Map<String, Object> map = getFingerprint();
87      // replace the fingerprint's simple list of families with a 
88      // map from column families to lists of qualifiers and kv details
89      Map<String, List<Map<String, Object>>> columns =
90        new HashMap<String, List<Map<String, Object>>>();
91      map.put("families", columns);
92      map.put("row", Bytes.toStringBinary(this.row));
93      int colCount = 0;
94      // iterate through all column families affected
95      for (Map.Entry<byte [], List<KeyValue>> entry : this.familyMap.entrySet()) {
96        // map from this family to details for each kv affected within the family
97        List<Map<String, Object>> qualifierDetails =
98          new ArrayList<Map<String, Object>>();
99        columns.put(Bytes.toStringBinary(entry.getKey()), qualifierDetails);
100       colCount += entry.getValue().size();
101       if (maxCols <= 0) {
102         continue;
103       }
104       // add details for each kv
105       for (KeyValue kv : entry.getValue()) {
106         if (--maxCols <= 0 ) {
107           continue;
108         }
109         Map<String, Object> kvMap = kv.toStringMap();
110         // row and family information are already available in the bigger map
111         kvMap.remove("row");
112         kvMap.remove("family");
113         qualifierDetails.add(kvMap);
114       }
115     }
116     map.put("totalColumns", colCount);
117     // add the id if set
118     if (getId() != null) {
119       map.put("id", getId());
120     }
121     return map;
122   }
123 
124   /**
125    * @deprecated Use {@link #getDurability()} instead.
126    * @return true if edits should be applied to WAL, false if not
127    */
128   public boolean getWriteToWAL() {
129     return this.writeToWAL;
130   }
131 
132   /**
133    * Set whether this Delete should be written to the WAL or not.
134    * Not writing the WAL means you may lose edits on server crash.
135    * This method will reset any changes made via {@link #setDurability(Durability)} 
136    * @param write true if edits should be written to WAL, false if not
137    * @deprecated Use {@link #setDurability(Durability)} instead.
138    */
139   public void setWriteToWAL(boolean write) {
140     setDurability(write ? Durability.USE_DEFAULT : Durability.SKIP_WAL);
141   }
142 
143   /**
144    * Set the durability for this mutation.
145    * Note that RegionServers prior to 0.94.7 will only honor {@link Durability#SKIP_WAL}.
146    * This method will reset any changes made via {@link #setWriteToWAL(boolean)} 
147    * @param d
148    */
149   public void setDurability(Durability d) {
150     setAttribute(DURABILITY_ID_ATTR, Bytes.toBytes(d.ordinal()));
151     this.writeToWAL = d != Durability.SKIP_WAL;
152   }
153 
154   /** Get the current durability */
155   public Durability getDurability() {
156     byte[] attr = getAttribute(DURABILITY_ID_ATTR);
157     if (attr != null) {
158       try {
159         return Durability.valueOf(Bytes.toInt(attr));
160       } catch (IllegalArgumentException iax) {
161         LOG.warn("Invalid or unknown durability settting", iax);
162       }
163     }
164     return writeToWAL ? Durability.USE_DEFAULT : Durability.SKIP_WAL;
165   }
166 
167   /**
168    * Method for retrieving the put's familyMap
169    * @return familyMap
170    */
171   public Map<byte [], List<KeyValue>> getFamilyMap() {
172     return this.familyMap;
173   }
174 
175   /**
176    * Method for setting the put's familyMap
177    */
178   public void setFamilyMap(Map<byte [], List<KeyValue>> map) {
179     this.familyMap = map;
180   }
181 
182   /**
183    * Method to check if the familyMap is empty
184    * @return true if empty, false otherwise
185    */
186   public boolean isEmpty() {
187     return familyMap.isEmpty();
188   }
189 
190   /**
191    * Method for retrieving the delete's row
192    * @return row
193    */
194   @Override
195   public byte [] getRow() {
196     return this.row;
197   }
198 
199   public int compareTo(final Row d) {
200     return Bytes.compareTo(this.getRow(), d.getRow());
201   }
202 
203   /**
204    * Method for retrieving the delete's RowLock
205    * @return RowLock
206    * @deprecated {@link RowLock} and associated operations are deprecated
207    */
208   public RowLock getRowLock() {
209     return new RowLock(this.row, this.lockId);
210   }
211 
212   /**
213    * Method for retrieving the delete's lock ID.
214    *
215    * @return The lock ID.
216    * @deprecated {@link RowLock} and associated operations are deprecated
217    */
218   public long getLockId() {
219   return this.lockId;
220   }
221 
222   /**
223    * Method for retrieving the timestamp
224    * @return timestamp
225    */
226   public long getTimeStamp() {
227     return this.ts;
228   }
229 
230   /**
231    * Set the replication custer id.
232    * @param clusterId
233    */
234   public void setClusterId(UUID clusterId) {
235     if (clusterId == null) return;
236     byte[] val = new byte[2*Bytes.SIZEOF_LONG];
237     Bytes.putLong(val, 0, clusterId.getMostSignificantBits());
238     Bytes.putLong(val, Bytes.SIZEOF_LONG, clusterId.getLeastSignificantBits());
239     setAttribute(CLUSTER_ID_ATTR, val);
240   }
241 
242   /**
243    * @return The replication cluster id.
244    */
245   public UUID getClusterId() {
246     byte[] attr = getAttribute(CLUSTER_ID_ATTR);
247     if (attr == null) {
248       return HConstants.DEFAULT_CLUSTER_ID;
249     }
250     return new UUID(Bytes.toLong(attr,0), Bytes.toLong(attr, Bytes.SIZEOF_LONG));
251   }
252 
253   /**
254    * Marks that the clusters with the given clusterIds have consumed the mutation
255    * @param clusterIds of the clusters that have consumed the mutation
256    */
257   public void setClusterIds(List<UUID> clusterIds) {
258     ByteArrayDataOutput out = ByteStreams.newDataOutput();
259     out.writeInt(clusterIds.size());
260     for (UUID clusterId : clusterIds) {
261       out.writeLong(clusterId.getMostSignificantBits());
262       out.writeLong(clusterId.getLeastSignificantBits());
263     }
264     setAttribute(CONSUMED_CLUSTER_IDS, out.toByteArray());
265   }
266 
267   /**
268    * @return the set of cluster Ids that have consumed the mutation
269    */
270   public List<UUID> getClusterIds() {
271     List<UUID> clusterIds = new ArrayList<UUID>();
272     byte[] bytes = getAttribute(CONSUMED_CLUSTER_IDS);
273     if(bytes != null) {
274       ByteArrayDataInput in = ByteStreams.newDataInput(bytes);
275       int numClusters = in.readInt();
276       for(int i=0; i<numClusters; i++){
277         clusterIds.add(new UUID(in.readLong(), in.readLong()));
278       }
279     }
280     return clusterIds;
281   }
282 
283   /**
284    * @return the total number of KeyValues
285    */
286   public int size() {
287     int size = 0;
288     for(List<KeyValue> kvList : this.familyMap.values()) {
289       size += kvList.size();
290     }
291     return size;
292   }
293 
294   /**
295    * @return the number of different families
296    */
297   public int numFamilies() {
298     return familyMap.size();
299   }
300 }