View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.client;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  import java.util.NavigableMap;
25  import java.util.TreeMap;
26  import java.util.UUID;
27  
28  import org.apache.hadoop.hbase.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.classification.InterfaceStability;
30  import org.apache.hadoop.hbase.Cell;
31  import org.apache.hadoop.hbase.CellUtil;
32  import org.apache.hadoop.hbase.KeyValue;
33  import org.apache.hadoop.hbase.io.TimeRange;
34  import org.apache.hadoop.hbase.security.access.Permission;
35  import org.apache.hadoop.hbase.security.visibility.CellVisibility;
36  import org.apache.hadoop.hbase.util.Bytes;
37  import org.apache.hadoop.hbase.util.ClassSize;
38  
39  /**
40   * Used to perform Increment operations on a single row.
41   * <p>
42   * This operation does not appear atomic to readers.  Increments are done
43   * under a single row lock, so write operations to a row are synchronized, but
44   * readers do not take row locks so get and scan operations can see this
45   * operation partially completed.
46   * <p>
47   * To increment columns of a row, instantiate an Increment object with the row
48   * to increment.  At least one column to increment must be specified using the
49   * {@link #addColumn(byte[], byte[], long)} method.
50   */
51  @InterfaceAudience.Public
52  @InterfaceStability.Stable
53  public class Increment extends Mutation implements Comparable<Row> {
54    private static final long HEAP_OVERHEAD =  ClassSize.REFERENCE + ClassSize.TIMERANGE;
55  
56    private static final String RETURN_RESULTS = "_rr_";
57  
58    private TimeRange tr = new TimeRange();
59  
60    /**
61     * Create a Increment operation for the specified row.
62     * <p>
63     * At least one column must be incremented.
64     * @param row row key (we will make a copy of this).
65     */
66    public Increment(byte [] row) {
67      this(row, 0, row.length);
68    }
69  
70    /**
71     * Create a Increment operation for the specified row.
72     * <p>
73     * At least one column must be incremented.
74     * @param row row key (we will make a copy of this).
75     */
76    public Increment(final byte [] row, final int offset, final int length) {
77      checkRow(row, offset, length);
78      this.row = Bytes.copy(row, offset, length);
79    }
80    /**
81     * Copy constructor
82     * @param i
83     */
84    public Increment(Increment i) {
85      this.row = i.getRow();
86      this.ts = i.getTimeStamp();
87      this.tr = i.getTimeRange();
88      this.familyMap.putAll(i.getFamilyCellMap());
89      for (Map.Entry<String, byte[]> entry : i.getAttributesMap().entrySet()) {
90        this.setAttribute(entry.getKey(), entry.getValue());
91      }
92    }
93  
94    /**
95     * Add the specified KeyValue to this operation.
96     * @param cell individual Cell
97     * @return this
98     * @throws java.io.IOException e
99     */
100   public Increment add(Cell cell) throws IOException{
101     byte [] family = CellUtil.cloneFamily(cell);
102     List<Cell> list = getCellList(family);
103     //Checking that the row of the kv is the same as the put
104     int res = Bytes.compareTo(this.row, 0, row.length,
105         cell.getRowArray(), cell.getRowOffset(), cell.getRowLength());
106     if (res != 0) {
107       throw new WrongRowIOException("The row in " + cell +
108         " doesn't match the original one " +  Bytes.toStringBinary(this.row));
109     }
110     list.add(cell);
111     familyMap.put(family, list);
112     return this;
113   }
114 
115   /**
116    * Increment the column from the specific family with the specified qualifier
117    * by the specified amount.
118    * <p>
119    * Overrides previous calls to addColumn for this family and qualifier.
120    * @param family family name
121    * @param qualifier column qualifier
122    * @param amount amount to increment by
123    * @return the Increment object
124    */
125   public Increment addColumn(byte [] family, byte [] qualifier, long amount) {
126     if (family == null) {
127       throw new IllegalArgumentException("family cannot be null");
128     }
129     if (qualifier == null) {
130       throw new IllegalArgumentException("qualifier cannot be null");
131     }
132     List<Cell> list = getCellList(family);
133     KeyValue kv = createPutKeyValue(family, qualifier, ts, Bytes.toBytes(amount));
134     list.add(kv);
135     familyMap.put(CellUtil.cloneFamily(kv), list);
136     return this;
137   }
138 
139   /**
140    * Gets the TimeRange used for this increment.
141    * @return TimeRange
142    */
143   public TimeRange getTimeRange() {
144     return this.tr;
145   }
146 
147   /**
148    * Sets the TimeRange to be used on the Get for this increment.
149    * <p>
150    * This is useful for when you have counters that only last for specific
151    * periods of time (ie. counters that are partitioned by time).  By setting
152    * the range of valid times for this increment, you can potentially gain
153    * some performance with a more optimal Get operation.
154    * <p>
155    * This range is used as [minStamp, maxStamp).
156    * @param minStamp minimum timestamp value, inclusive
157    * @param maxStamp maximum timestamp value, exclusive
158    * @throws IOException if invalid time range
159    * @return this
160    */
161   public Increment setTimeRange(long minStamp, long maxStamp)
162   throws IOException {
163     tr = new TimeRange(minStamp, maxStamp);
164     return this;
165   }
166   
167   /**
168    * @param returnResults True (default) if the increment operation should return the results. A
169    *          client that is not interested in the result can save network bandwidth setting this
170    *          to false.
171    */
172   public Increment setReturnResults(boolean returnResults) {
173     setAttribute(RETURN_RESULTS, Bytes.toBytes(returnResults));
174     return this;
175   }
176 
177   /**
178    * @return current value for returnResults
179    */
180   public boolean isReturnResults() {
181     byte[] v = getAttribute(RETURN_RESULTS);
182     return v == null ? true : Bytes.toBoolean(v);
183   }
184 
185   /**
186    * Method for retrieving the number of families to increment from
187    * @return number of families
188    */
189   @Override
190   public int numFamilies() {
191     return this.familyMap.size();
192   }
193 
194   /**
195    * Method for checking if any families have been inserted into this Increment
196    * @return true if familyMap is non empty false otherwise
197    */
198   public boolean hasFamilies() {
199     return !this.familyMap.isEmpty();
200   }
201 
202   /**
203    * Before 0.95, when you called Increment#getFamilyMap(), you got back
204    * a map of families to a list of Longs.  Now, {@link #getFamilyCellMap()} returns
205    * families by list of Cells.  This method has been added so you can have the
206    * old behavior.
207    * @return Map of families to a Map of qualifiers and their Long increments.
208    * @since 0.95.0
209    */
210   public Map<byte[], NavigableMap<byte [], Long>> getFamilyMapOfLongs() {
211     NavigableMap<byte[], List<Cell>> map = super.getFamilyCellMap();
212     Map<byte [], NavigableMap<byte[], Long>> results =
213       new TreeMap<byte[], NavigableMap<byte [], Long>>(Bytes.BYTES_COMPARATOR);
214     for (Map.Entry<byte [], List<Cell>> entry: map.entrySet()) {
215       NavigableMap<byte [], Long> longs = new TreeMap<byte [], Long>(Bytes.BYTES_COMPARATOR);
216       for (Cell cell: entry.getValue()) {
217         longs.put(CellUtil.cloneQualifier(cell),
218             Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
219       }
220       results.put(entry.getKey(), longs);
221     }
222     return results;
223   }
224 
225   /**
226    * @return String
227    */
228   @Override
229   public String toString() {
230     StringBuilder sb = new StringBuilder();
231     sb.append("row=");
232     sb.append(Bytes.toStringBinary(this.row));
233     if(this.familyMap.size() == 0) {
234       sb.append(", no columns set to be incremented");
235       return sb.toString();
236     }
237     sb.append(", families=");
238     boolean moreThanOne = false;
239     for(Map.Entry<byte [], List<Cell>> entry: this.familyMap.entrySet()) {
240       if(moreThanOne) {
241         sb.append("), ");
242       } else {
243         moreThanOne = true;
244         sb.append("{");
245       }
246       sb.append("(family=");
247       sb.append(Bytes.toString(entry.getKey()));
248       sb.append(", columns=");
249       if(entry.getValue() == null) {
250         sb.append("NONE");
251       } else {
252         sb.append("{");
253         boolean moreThanOneB = false;
254         for(Cell cell : entry.getValue()) {
255           if(moreThanOneB) {
256             sb.append(", ");
257           } else {
258             moreThanOneB = true;
259           }
260           sb.append(CellUtil.getCellKeyAsString(cell) + "+=" +
261               Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
262         }
263         sb.append("}");
264       }
265     }
266     sb.append("}");
267     return sb.toString();
268   }
269 
270   @Override
271   public int compareTo(Row i) {
272     // TODO: This is wrong.  Can't have two the same just because on same row.
273     return Bytes.compareTo(this.getRow(), i.getRow());
274   }
275 
276   @Override
277   public int hashCode() {
278     // TODO: This is wrong.  Can't have two gets the same just because on same row.  But it
279     // matches how equals works currently and gets rid of the findbugs warning.
280     return Bytes.hashCode(this.getRow());
281   }
282 
283   @Override
284   public boolean equals(Object obj) {
285     // TODO: This is wrong.  Can't have two the same just because on same row.
286     if (this == obj) {
287       return true;
288     }
289     if (obj == null || getClass() != obj.getClass()) {
290       return false;
291     }
292     Row other = (Row) obj;
293     return compareTo(other) == 0;
294   }
295 
296   @Override
297   protected long extraHeapSize(){
298     return HEAP_OVERHEAD;
299   }
300 
301   @Override
302   public Increment setAttribute(String name, byte[] value) {
303     return (Increment) super.setAttribute(name, value);
304   }
305 
306   @Override
307   public Increment setId(String id) {
308     return (Increment) super.setId(id);
309   }
310 
311   @Override
312   @Deprecated
313   public Increment setWriteToWAL(boolean write) {
314     return (Increment) super.setWriteToWAL(write);
315   }
316 
317   @Override
318   public Increment setDurability(Durability d) {
319     return (Increment) super.setDurability(d);
320   }
321 
322   @Override
323   public Increment setFamilyCellMap(NavigableMap<byte[], List<Cell>> map) {
324     return (Increment) super.setFamilyCellMap(map);
325   }
326 
327   @Override
328   @Deprecated
329   public Increment setFamilyMap(NavigableMap<byte[], List<KeyValue>> map) {
330     return (Increment) super.setFamilyMap(map);
331   }
332 
333   @Override
334   public Increment setClusterIds(List<UUID> clusterIds) {
335     return (Increment) super.setClusterIds(clusterIds);
336   }
337 
338   @Override
339   public Increment setCellVisibility(CellVisibility expression) {
340     return (Increment) super.setCellVisibility(expression);
341   }
342 
343   @Override
344   public Increment setACL(String user, Permission perms) {
345     return (Increment) super.setACL(user, perms);
346   }
347 
348   @Override
349   public Increment setACL(Map<String, Permission> perms) {
350     return (Increment) super.setACL(perms);
351   }
352 
353   @Override
354   public Increment setTTL(long ttl) {
355     return (Increment) super.setTTL(ttl);
356   }
357 }