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.CellBuilder;
029import org.apache.hadoop.hbase.CellBuilderType;
030import org.apache.hadoop.hbase.CellUtil;
031import org.apache.hadoop.hbase.KeyValue;
032import org.apache.hadoop.hbase.io.TimeRange;
033import org.apache.hadoop.hbase.security.access.Permission;
034import org.apache.hadoop.hbase.security.visibility.CellVisibility;
035import org.apache.hadoop.hbase.util.Bytes;
036import org.apache.hadoop.hbase.util.ClassSize;
037import org.apache.yetus.audience.InterfaceAudience;
038
039/**
040 * Used to perform Increment operations on a single row.
041 * <p>
042 * This operation ensures atomicity to readers. Increments are done
043 * under a single row lock, so write operations to a row are synchronized, and
044 * readers are guaranteed to see this operation fully completed.
045 * <p>
046 * To increment columns of a row, instantiate an Increment object with the row
047 * to increment.  At least one column to increment must be specified using the
048 * {@link #addColumn(byte[], byte[], long)} method.
049 */
050@InterfaceAudience.Public
051public class Increment extends Mutation {
052  private static final int HEAP_OVERHEAD = ClassSize.REFERENCE + ClassSize.TIMERANGE;
053  private TimeRange tr = TimeRange.allTime();
054
055  /**
056   * Create a Increment operation for the specified row.
057   * <p>
058   * At least one column must be incremented.
059   * @param row row key (we will make a copy of this).
060   */
061  public Increment(byte [] row) {
062    this(row, 0, row.length);
063  }
064
065  /**
066   * Create a Increment operation for the specified row.
067   * <p>
068   * At least one column must be incremented.
069   * @param row row key (we will make a copy of this).
070   */
071  public Increment(final byte [] row, final int offset, final int length) {
072    checkRow(row, offset, length);
073    this.row = Bytes.copy(row, offset, length);
074  }
075  /**
076   * Copy constructor
077   * @param incrementToCopy increment to copy
078   */
079  public Increment(Increment incrementToCopy) {
080    super(incrementToCopy);
081    this.tr = incrementToCopy.getTimeRange();
082  }
083
084  /**
085   * Construct the Increment with user defined data. NOTED:
086   * 1) all cells in the familyMap must have the Type.Put
087   * 2) the row of each cell must be same with passed row.
088   * @param row row. CAN'T be null
089   * @param ts timestamp
090   * @param familyMap the map to collect all cells internally. CAN'T be null
091   */
092  public Increment(byte[] row, long ts, NavigableMap<byte [], List<Cell>> familyMap) {
093    super(row, ts, familyMap);
094  }
095
096  /**
097   * Add the specified KeyValue to this operation.
098   * @param cell individual Cell
099   * @return this
100   * @throws java.io.IOException e
101   */
102  public Increment add(Cell cell) throws IOException{
103    super.add(cell);
104    return this;
105  }
106
107  /**
108   * Increment the column from the specific family with the specified qualifier
109   * by the specified amount.
110   * <p>
111   * Overrides previous calls to addColumn for this family and qualifier.
112   * @param family family name
113   * @param qualifier column qualifier
114   * @param amount amount to increment by
115   * @return the Increment object
116   */
117  public Increment addColumn(byte [] family, byte [] qualifier, long amount) {
118    if (family == null) {
119      throw new IllegalArgumentException("family cannot be null");
120    }
121    List<Cell> list = getCellList(family);
122    KeyValue kv = createPutKeyValue(family, qualifier, ts, Bytes.toBytes(amount));
123    list.add(kv);
124    return this;
125  }
126
127  /**
128   * Gets the TimeRange used for this increment.
129   * @return TimeRange
130   */
131  public TimeRange getTimeRange() {
132    return this.tr;
133  }
134
135  /**
136   * Sets the TimeRange to be used on the Get for this increment.
137   * <p>
138   * This is useful for when you have counters that only last for specific
139   * periods of time (ie. counters that are partitioned by time).  By setting
140   * the range of valid times for this increment, you can potentially gain
141   * some performance with a more optimal Get operation.
142   * Be careful adding the time range to this class as you will update the old cell if the
143   * time range doesn't include the latest cells.
144   * <p>
145   * This range is used as [minStamp, maxStamp).
146   * @param minStamp minimum timestamp value, inclusive
147   * @param maxStamp maximum timestamp value, exclusive
148   * @throws IOException if invalid time range
149   * @return this
150   */
151  public Increment setTimeRange(long minStamp, long maxStamp) throws IOException {
152    tr = TimeRange.between(minStamp, maxStamp);
153    return this;
154  }
155
156  @Override
157  public Increment setTimestamp(long timestamp) {
158    super.setTimestamp(timestamp);
159    return this;
160  }
161
162  /**
163   * @param returnResults True (default) if the increment operation should return the results. A
164   *          client that is not interested in the result can save network bandwidth setting this
165   *          to false.
166   */
167  @Override
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  @Override
178  public boolean isReturnResults() {
179    return super.isReturnResults();
180  }
181
182  /**
183   * Method for retrieving the number of families to increment from
184   * @return number of families
185   */
186  @Override
187  public int numFamilies() {
188    return this.familyMap.size();
189  }
190
191  /**
192   * Method for checking if any families have been inserted into this Increment
193   * @return true if familyMap is non empty false otherwise
194   */
195  public boolean hasFamilies() {
196    return !this.familyMap.isEmpty();
197  }
198
199  /**
200   * Before 0.95, when you called Increment#getFamilyMap(), you got back
201   * a map of families to a list of Longs.  Now, {@link #getFamilyCellMap()} returns
202   * families by list of Cells.  This method has been added so you can have the
203   * old behavior.
204   * @return Map of families to a Map of qualifiers and their Long increments.
205   * @since 0.95.0
206   */
207  public Map<byte[], NavigableMap<byte [], Long>> getFamilyMapOfLongs() {
208    NavigableMap<byte[], List<Cell>> map = super.getFamilyCellMap();
209    Map<byte [], NavigableMap<byte[], Long>> results = new TreeMap<>(Bytes.BYTES_COMPARATOR);
210    for (Map.Entry<byte [], List<Cell>> entry: map.entrySet()) {
211      NavigableMap<byte [], Long> longs = new TreeMap<>(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.isEmpty()) {
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  protected long extraHeapSize(){
268    return HEAP_OVERHEAD;
269  }
270
271  @Override
272  public Increment setAttribute(String name, byte[] value) {
273    return (Increment) super.setAttribute(name, value);
274  }
275
276  @Override
277  public Increment setId(String id) {
278    return (Increment) super.setId(id);
279  }
280
281  @Override
282  public Increment setDurability(Durability d) {
283    return (Increment) super.setDurability(d);
284  }
285
286  @Override
287  public Increment setClusterIds(List<UUID> clusterIds) {
288    return (Increment) super.setClusterIds(clusterIds);
289  }
290
291  @Override
292  public Increment setCellVisibility(CellVisibility expression) {
293    return (Increment) super.setCellVisibility(expression);
294  }
295
296  @Override
297  public Increment setACL(String user, Permission perms) {
298    return (Increment) super.setACL(user, perms);
299  }
300
301  @Override
302  public Increment setACL(Map<String, Permission> perms) {
303    return (Increment) super.setACL(perms);
304  }
305
306  @Override
307  public Increment setTTL(long ttl) {
308    return (Increment) super.setTTL(ttl);
309  }
310
311  @Override
312  public Increment setPriority(int priority) {
313    return (Increment) super.setPriority(priority);
314  }
315
316  @Override
317  public CellBuilder getCellBuilder(CellBuilderType type) {
318    return getCellBuilder(type, Cell.Type.Put);
319  }
320}