001/**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *     http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019package org.apache.hadoop.hbase.client;
020
021import java.io.IOException;
022import java.util.List;
023import java.util.Map;
024import java.util.NavigableMap;
025import java.util.TreeMap;
026import java.util.UUID;
027import org.apache.hadoop.hbase.Cell;
028import org.apache.hadoop.hbase.CellUtil;
029import org.apache.hadoop.hbase.KeyValue;
030import org.apache.hadoop.hbase.io.TimeRange;
031import org.apache.hadoop.hbase.security.access.Permission;
032import org.apache.hadoop.hbase.security.visibility.CellVisibility;
033import org.apache.hadoop.hbase.util.Bytes;
034import org.apache.hadoop.hbase.util.ClassSize;
035import org.apache.yetus.audience.InterfaceAudience;
036
037/**
038 * Used to perform Increment operations on a single row.
039 * <p>
040 * This operation ensures atomicity to readers. Increments are done
041 * under a single row lock, so write operations to a row are synchronized, and
042 * readers are guaranteed to see this operation fully completed.
043 * <p>
044 * To increment columns of a row, instantiate an Increment object with the row
045 * to increment.  At least one column to increment must be specified using the
046 * {@link #addColumn(byte[], byte[], long)} method.
047 */
048@InterfaceAudience.Public
049public class Increment extends Mutation {
050  private static final int HEAP_OVERHEAD = ClassSize.REFERENCE + ClassSize.TIMERANGE;
051  private TimeRange tr = TimeRange.allTime();
052
053  /**
054   * Create a Increment operation for the specified row.
055   * <p>
056   * At least one column must be incremented.
057   * @param row row key (we will make a copy of this).
058   */
059  public Increment(byte [] row) {
060    this(row, 0, row.length);
061  }
062
063  /**
064   * Create a Increment operation for the specified row.
065   * <p>
066   * At least one column must be incremented.
067   * @param row row key (we will make a copy of this).
068   */
069  public Increment(final byte [] row, final int offset, final int length) {
070    checkRow(row, offset, length);
071    this.row = Bytes.copy(row, offset, length);
072  }
073  /**
074   * Copy constructor
075   * @param incrementToCopy increment to copy
076   */
077  public Increment(Increment incrementToCopy) {
078    super(incrementToCopy);
079    this.tr = incrementToCopy.getTimeRange();
080  }
081
082  /**
083   * Construct the Increment with user defined data. NOTED:
084   * 1) all cells in the familyMap must have the Type.Put
085   * 2) the row of each cell must be same with passed row.
086   * @param row row. CAN'T be null
087   * @param ts timestamp
088   * @param familyMap the map to collect all cells internally. CAN'T be null
089   */
090  public Increment(byte[] row, long ts, NavigableMap<byte [], List<Cell>> familyMap) {
091    super(row, ts, familyMap);
092  }
093
094  /**
095   * Add the specified KeyValue to this operation.
096   * @param cell individual Cell
097   * @return this
098   * @throws java.io.IOException e
099   */
100  public Increment add(Cell cell) throws IOException{
101    super.add(cell);
102    return this;
103  }
104
105  /**
106   * Increment the column from the specific family with the specified qualifier
107   * by the specified amount.
108   * <p>
109   * Overrides previous calls to addColumn for this family and qualifier.
110   * @param family family name
111   * @param qualifier column qualifier
112   * @param amount amount to increment by
113   * @return the Increment object
114   */
115  public Increment addColumn(byte [] family, byte [] qualifier, long amount) {
116    if (family == null) {
117      throw new IllegalArgumentException("family cannot be null");
118    }
119    List<Cell> list = getCellList(family);
120    KeyValue kv = createPutKeyValue(family, qualifier, ts, Bytes.toBytes(amount));
121    list.add(kv);
122    return this;
123  }
124
125  /**
126   * Gets the TimeRange used for this increment.
127   * @return TimeRange
128   */
129  public TimeRange getTimeRange() {
130    return this.tr;
131  }
132
133  /**
134   * Sets the TimeRange to be used on the Get for this increment.
135   * <p>
136   * This is useful for when you have counters that only last for specific
137   * periods of time (ie. counters that are partitioned by time).  By setting
138   * the range of valid times for this increment, you can potentially gain
139   * some performance with a more optimal Get operation.
140   * Be careful adding the time range to this class as you will update the old cell if the
141   * time range doesn't include the latest cells.
142   * <p>
143   * This range is used as [minStamp, maxStamp).
144   * @param minStamp minimum timestamp value, inclusive
145   * @param maxStamp maximum timestamp value, exclusive
146   * @throws IOException if invalid time range
147   * @return this
148   */
149  public Increment setTimeRange(long minStamp, long maxStamp)
150  throws IOException {
151    tr = new TimeRange(minStamp, maxStamp);
152    return this;
153  }
154
155  @Override
156  public Increment setTimestamp(long timestamp) {
157    super.setTimestamp(timestamp);
158    return this;
159  }
160
161  /**
162   * @param returnResults True (default) if the increment operation should return the results. A
163   *          client that is not interested in the result can save network bandwidth setting this
164   *          to false.
165   */
166  @Override
167  public Increment setReturnResults(boolean returnResults) {
168    super.setReturnResults(returnResults);
169    return this;
170  }
171
172  /**
173   * @return current setting for returnResults
174   */
175  // This method makes public the superclasses's protected method.
176  @Override
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 = new TreeMap<>(Bytes.BYTES_COMPARATOR);
209    for (Map.Entry<byte [], List<Cell>> entry: map.entrySet()) {
210      NavigableMap<byte [], Long> longs = new TreeMap<>(Bytes.BYTES_COMPARATOR);
211      for (Cell cell: entry.getValue()) {
212        longs.put(CellUtil.cloneQualifier(cell),
213            Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
214      }
215      results.put(entry.getKey(), longs);
216    }
217    return results;
218  }
219
220  /**
221   * @return String
222   */
223  @Override
224  public String toString() {
225    StringBuilder sb = new StringBuilder();
226    sb.append("row=");
227    sb.append(Bytes.toStringBinary(this.row));
228    if(this.familyMap.isEmpty()) {
229      sb.append(", no columns set to be incremented");
230      return sb.toString();
231    }
232    sb.append(", families=");
233    boolean moreThanOne = false;
234    for(Map.Entry<byte [], List<Cell>> entry: this.familyMap.entrySet()) {
235      if(moreThanOne) {
236        sb.append("), ");
237      } else {
238        moreThanOne = true;
239        sb.append("{");
240      }
241      sb.append("(family=");
242      sb.append(Bytes.toString(entry.getKey()));
243      sb.append(", columns=");
244      if(entry.getValue() == null) {
245        sb.append("NONE");
246      } else {
247        sb.append("{");
248        boolean moreThanOneB = false;
249        for(Cell cell : entry.getValue()) {
250          if(moreThanOneB) {
251            sb.append(", ");
252          } else {
253            moreThanOneB = true;
254          }
255          sb.append(CellUtil.getCellKeyAsString(cell) + "+=" +
256              Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
257        }
258        sb.append("}");
259      }
260    }
261    sb.append("}");
262    return sb.toString();
263  }
264
265  /**
266   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
267   *             No replacement.
268   */
269  @Deprecated
270  @Override
271  public int hashCode() {
272    // TODO: This is wrong.  Can't have two gets the same just because on same row.  But it
273    // matches how equals works currently and gets rid of the findbugs warning.
274    return Bytes.hashCode(this.getRow());
275  }
276
277  /**
278   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
279   *             Use {@link Row#COMPARATOR} instead
280   */
281  @Deprecated
282  @Override
283  public boolean equals(Object obj) {
284    // TODO: This is wrong.  Can't have two the same just because on same row.
285    if (this == obj) {
286      return true;
287    }
288    if (obj == null || getClass() != obj.getClass()) {
289      return false;
290    }
291    Row other = (Row) obj;
292    return compareTo(other) == 0;
293  }
294
295  @Override
296  protected long extraHeapSize(){
297    return HEAP_OVERHEAD;
298  }
299
300  @Override
301  public Increment setAttribute(String name, byte[] value) {
302    return (Increment) super.setAttribute(name, value);
303  }
304
305  @Override
306  public Increment setId(String id) {
307    return (Increment) super.setId(id);
308  }
309
310  @Override
311  public Increment setDurability(Durability d) {
312    return (Increment) super.setDurability(d);
313  }
314
315  /**
316   * Method for setting the Increment's familyMap
317   * @deprecated As of release 2.0.0, this will be removed in HBase 3.0.0.
318   *             Use {@link Increment#Increment(byte[], long, NavigableMap)} instead
319   */
320  @Deprecated
321  @Override
322  public Increment setFamilyCellMap(NavigableMap<byte[], List<Cell>> map) {
323    return (Increment) super.setFamilyCellMap(map);
324  }
325
326  @Override
327  public Increment setClusterIds(List<UUID> clusterIds) {
328    return (Increment) super.setClusterIds(clusterIds);
329  }
330
331  @Override
332  public Increment setCellVisibility(CellVisibility expression) {
333    return (Increment) super.setCellVisibility(expression);
334  }
335
336  @Override
337  public Increment setACL(String user, Permission perms) {
338    return (Increment) super.setACL(user, perms);
339  }
340
341  @Override
342  public Increment setACL(Map<String, Permission> perms) {
343    return (Increment) super.setACL(perms);
344  }
345
346  @Override
347  public Increment setTTL(long ttl) {
348    return (Increment) super.setTTL(ttl);
349  }
350
351  @Override
352  public Increment setPriority(int priority) {
353    return (Increment) super.setPriority(priority);
354  }
355}