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