001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.client;
019
020import org.apache.hadoop.hbase.CompareOperator;
021import org.apache.hadoop.hbase.filter.Filter;
022import org.apache.hadoop.hbase.io.TimeRange;
023import org.apache.hadoop.hbase.util.Bytes;
024import org.apache.yetus.audience.InterfaceAudience;
025import org.apache.yetus.audience.InterfaceStability;
026
027import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
028
029/**
030 * Used to perform CheckAndMutate operations.
031 * <p>
032 * Use the builder class to instantiate a CheckAndMutate object. This builder class is fluent style
033 * APIs, the code are like:
034 *
035 * <pre>
036 * <code>
037 * // A CheckAndMutate operation where do the specified action if the column (specified by the
038 * // family and the qualifier) of the row equals to the specified value
039 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
040 *   .ifEquals(family, qualifier, value)
041 *   .build(put);
042 *
043 * // A CheckAndMutate operation where do the specified action if the column (specified by the
044 * // family and the qualifier) of the row doesn't exist
045 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
046 *   .ifNotExists(family, qualifier)
047 *   .build(put);
048 *
049 * // A CheckAndMutate operation where do the specified action if the row matches the filter
050 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
051 *   .ifMatches(filter)
052 *   .build(delete);
053 * </code>
054 * </pre>
055 */
056@InterfaceAudience.Public
057@InterfaceStability.Evolving
058public final class CheckAndMutate implements Row {
059
060  /**
061   * A builder class for building a CheckAndMutate object.
062   */
063  @InterfaceAudience.Public
064  @InterfaceStability.Evolving
065  public static final class Builder {
066    private final byte[] row;
067    private byte[] family;
068    private byte[] qualifier;
069    private CompareOperator op;
070    private byte[] value;
071    private Filter filter;
072    private TimeRange timeRange;
073
074    private Builder(byte[] row) {
075      this.row = Preconditions.checkNotNull(row, "row is null");
076    }
077
078    /**
079     * Check for lack of column
080     * @param family    family to check
081     * @param qualifier qualifier to check
082     * @return the CheckAndMutate object
083     */
084    public Builder ifNotExists(byte[] family, byte[] qualifier) {
085      return ifEquals(family, qualifier, null);
086    }
087
088    /**
089     * Check for equality
090     * @param family    family to check
091     * @param qualifier qualifier to check
092     * @param value     the expected value
093     * @return the CheckAndMutate object
094     */
095    public Builder ifEquals(byte[] family, byte[] qualifier, byte[] value) {
096      return ifMatches(family, qualifier, CompareOperator.EQUAL, value);
097    }
098
099    /**
100     * Check for match
101     * @param family    family to check
102     * @param qualifier qualifier to check
103     * @param compareOp comparison operator to use
104     * @param value     the expected value
105     * @return the CheckAndMutate object
106     */
107    public Builder ifMatches(byte[] family, byte[] qualifier, CompareOperator compareOp,
108      byte[] value) {
109      this.family = Preconditions.checkNotNull(family, "family is null");
110      this.qualifier = qualifier;
111      this.op = Preconditions.checkNotNull(compareOp, "compareOp is null");
112      this.value = value;
113      return this;
114    }
115
116    /**
117     * Check for match
118     * @param filter filter to check
119     * @return the CheckAndMutate object
120     */
121    public Builder ifMatches(Filter filter) {
122      this.filter = Preconditions.checkNotNull(filter, "filter is null");
123      return this;
124    }
125
126    /**
127     * Specify a timerange
128     * @param timeRange time range to check
129     * @return the CheckAndMutate object
130     */
131    public Builder timeRange(TimeRange timeRange) {
132      this.timeRange = timeRange;
133      return this;
134    }
135
136    private void preCheck(Row action) {
137      Preconditions.checkNotNull(action, "action is null");
138      if (!Bytes.equals(row, action.getRow())) {
139        throw new IllegalArgumentException(
140          "The row of the action <" + Bytes.toStringBinary(action.getRow())
141            + "> doesn't match the original one <" + Bytes.toStringBinary(this.row) + ">");
142      }
143      Preconditions.checkState(op != null || filter != null,
144        "condition is null. You need to"
145          + " specify the condition by calling ifNotExists/ifEquals/ifMatches before building a"
146          + " CheckAndMutate object");
147    }
148
149    /**
150     * Build the CheckAndMutate object
151     * @param put data to put if check succeeds
152     * @return a CheckAndMutate object
153     */
154    public CheckAndMutate build(Put put) {
155      preCheck(put);
156      if (filter != null) {
157        return new CheckAndMutate(row, filter, timeRange, put);
158      } else {
159        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, put);
160      }
161    }
162
163    /**
164     * Build the CheckAndMutate object
165     * @param delete data to delete if check succeeds
166     * @return a CheckAndMutate object
167     */
168    public CheckAndMutate build(Delete delete) {
169      preCheck(delete);
170      if (filter != null) {
171        return new CheckAndMutate(row, filter, timeRange, delete);
172      } else {
173        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, delete);
174      }
175    }
176
177    /**
178     * Build the CheckAndMutate object with an Increment to commit if the check succeeds.
179     * @param increment data to increment if check succeeds
180     * @return a CheckAndMutate object
181     */
182    public CheckAndMutate build(Increment increment) {
183      preCheck(increment);
184      if (filter != null) {
185        return new CheckAndMutate(row, filter, timeRange, increment);
186      } else {
187        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, increment);
188      }
189    }
190
191    /**
192     * Build the CheckAndMutate object with an Append to commit if the check succeeds.
193     * @param append data to append if check succeeds
194     * @return a CheckAndMutate object
195     */
196    public CheckAndMutate build(Append append) {
197      preCheck(append);
198      if (filter != null) {
199        return new CheckAndMutate(row, filter, timeRange, append);
200      } else {
201        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, append);
202      }
203    }
204
205    /**
206     * Build the CheckAndMutate object with a RowMutations to commit if the check succeeds.
207     * @param mutations mutations to perform if check succeeds
208     * @return a CheckAndMutate object
209     */
210    public CheckAndMutate build(RowMutations mutations) {
211      preCheck(mutations);
212      if (filter != null) {
213        return new CheckAndMutate(row, filter, timeRange, mutations);
214      } else {
215        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, mutations);
216      }
217    }
218  }
219
220  /**
221   * returns a builder object to build a CheckAndMutate object
222   * @param row row
223   * @return a builder object
224   */
225  public static Builder newBuilder(byte[] row) {
226    return new Builder(row);
227  }
228
229  private final byte[] row;
230  private final byte[] family;
231  private final byte[] qualifier;
232  private final CompareOperator op;
233  private final byte[] value;
234  private final Filter filter;
235  private final TimeRange timeRange;
236  private final Row action;
237
238  private CheckAndMutate(byte[] row, byte[] family, byte[] qualifier, final CompareOperator op,
239    byte[] value, TimeRange timeRange, Row action) {
240    this.row = row;
241    this.family = family;
242    this.qualifier = qualifier;
243    this.op = op;
244    this.value = value;
245    this.filter = null;
246    this.timeRange = timeRange != null ? timeRange : TimeRange.allTime();
247    this.action = action;
248  }
249
250  private CheckAndMutate(byte[] row, Filter filter, TimeRange timeRange, Row action) {
251    this.row = row;
252    this.family = null;
253    this.qualifier = null;
254    this.op = null;
255    this.value = null;
256    this.filter = filter;
257    this.timeRange = timeRange != null ? timeRange : TimeRange.allTime();
258    this.action = action;
259  }
260
261  /** Returns the row */
262  @Override
263  public byte[] getRow() {
264    return row;
265  }
266
267  /** Returns the family to check */
268  public byte[] getFamily() {
269    return family;
270  }
271
272  /** Returns the qualifier to check */
273  public byte[] getQualifier() {
274    return qualifier;
275  }
276
277  /** Returns the comparison operator */
278  public CompareOperator getCompareOp() {
279    return op;
280  }
281
282  /** Returns the expected value */
283  public byte[] getValue() {
284    return value;
285  }
286
287  /** Returns the filter to check */
288  public Filter getFilter() {
289    return filter;
290  }
291
292  /** Returns whether this has a filter or not */
293  public boolean hasFilter() {
294    return filter != null;
295  }
296
297  /** Returns the time range to check */
298  public TimeRange getTimeRange() {
299    return timeRange;
300  }
301
302  /** Returns the action done if check succeeds */
303  public Row getAction() {
304    return action;
305  }
306}