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     * @param family    family to check
101     * @param qualifier qualifier to check
102     * @param compareOp comparison operator to use
103     * @param value     the expected value
104     * @return the CheckAndMutate object
105     */
106    public Builder ifMatches(byte[] family, byte[] qualifier, CompareOperator compareOp,
107      byte[] value) {
108      this.family = Preconditions.checkNotNull(family, "family is null");
109      this.qualifier = qualifier;
110      this.op = Preconditions.checkNotNull(compareOp, "compareOp is null");
111      this.value = value;
112      return this;
113    }
114
115    /**
116     * @param filter filter to check
117     * @return the CheckAndMutate object
118     */
119    public Builder ifMatches(Filter filter) {
120      this.filter = Preconditions.checkNotNull(filter, "filter is null");
121      return this;
122    }
123
124    /**
125     * @param timeRange time range to check
126     * @return the CheckAndMutate object
127     */
128    public Builder timeRange(TimeRange timeRange) {
129      this.timeRange = timeRange;
130      return this;
131    }
132
133    private void preCheck(Row action) {
134      Preconditions.checkNotNull(action, "action is null");
135      if (!Bytes.equals(row, action.getRow())) {
136        throw new IllegalArgumentException(
137          "The row of the action <" + Bytes.toStringBinary(action.getRow())
138            + "> doesn't match the original one <" + Bytes.toStringBinary(this.row) + ">");
139      }
140      Preconditions.checkState(op != null || filter != null,
141        "condition is null. You need to"
142          + " specify the condition by calling ifNotExists/ifEquals/ifMatches before building a"
143          + " CheckAndMutate object");
144    }
145
146    /**
147     * @param put data to put if check succeeds
148     * @return a CheckAndMutate object
149     */
150    public CheckAndMutate build(Put put) {
151      preCheck(put);
152      if (filter != null) {
153        return new CheckAndMutate(row, filter, timeRange, put);
154      } else {
155        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, put);
156      }
157    }
158
159    /**
160     * @param delete data to delete if check succeeds
161     * @return a CheckAndMutate object
162     */
163    public CheckAndMutate build(Delete delete) {
164      preCheck(delete);
165      if (filter != null) {
166        return new CheckAndMutate(row, filter, timeRange, delete);
167      } else {
168        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, delete);
169      }
170    }
171
172    /**
173     * @param increment data to increment if check succeeds
174     * @return a CheckAndMutate object
175     */
176    public CheckAndMutate build(Increment increment) {
177      preCheck(increment);
178      if (filter != null) {
179        return new CheckAndMutate(row, filter, timeRange, increment);
180      } else {
181        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, increment);
182      }
183    }
184
185    /**
186     * @param append data to append if check succeeds
187     * @return a CheckAndMutate object
188     */
189    public CheckAndMutate build(Append append) {
190      preCheck(append);
191      if (filter != null) {
192        return new CheckAndMutate(row, filter, timeRange, append);
193      } else {
194        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, append);
195      }
196    }
197
198    /**
199     * @param mutations mutations to perform if check succeeds
200     * @return a CheckAndMutate object
201     */
202    public CheckAndMutate build(RowMutations mutations) {
203      preCheck(mutations);
204      if (filter != null) {
205        return new CheckAndMutate(row, filter, timeRange, mutations);
206      } else {
207        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, mutations);
208      }
209    }
210  }
211
212  /**
213   * returns a builder object to build a CheckAndMutate object
214   * @param row row
215   * @return a builder object
216   */
217  public static Builder newBuilder(byte[] row) {
218    return new Builder(row);
219  }
220
221  private final byte[] row;
222  private final byte[] family;
223  private final byte[] qualifier;
224  private final CompareOperator op;
225  private final byte[] value;
226  private final Filter filter;
227  private final TimeRange timeRange;
228  private final Row action;
229
230  private CheckAndMutate(byte[] row, byte[] family, byte[] qualifier, final CompareOperator op,
231    byte[] value, TimeRange timeRange, Row action) {
232    this.row = row;
233    this.family = family;
234    this.qualifier = qualifier;
235    this.op = op;
236    this.value = value;
237    this.filter = null;
238    this.timeRange = timeRange != null ? timeRange : TimeRange.allTime();
239    this.action = action;
240  }
241
242  private CheckAndMutate(byte[] row, Filter filter, TimeRange timeRange, Row action) {
243    this.row = row;
244    this.family = null;
245    this.qualifier = null;
246    this.op = null;
247    this.value = null;
248    this.filter = filter;
249    this.timeRange = timeRange != null ? timeRange : TimeRange.allTime();
250    this.action = action;
251  }
252
253  /**
254   * @return the row
255   */
256  @Override
257  public byte[] getRow() {
258    return row;
259  }
260
261  /**
262   * @return the family to check
263   */
264  public byte[] getFamily() {
265    return family;
266  }
267
268  /**
269   * @return the qualifier to check
270   */
271  public byte[] getQualifier() {
272    return qualifier;
273  }
274
275  /**
276   * @return the comparison operator
277   */
278  public CompareOperator getCompareOp() {
279    return op;
280  }
281
282  /**
283   * @return the expected value
284   */
285  public byte[] getValue() {
286    return value;
287  }
288
289  /**
290   * @return the filter to check
291   */
292  public Filter getFilter() {
293    return filter;
294  }
295
296  /**
297   * @return whether this has a filter or not
298   */
299  public boolean hasFilter() {
300    return filter != null;
301  }
302
303  /**
304   * @return the time range to check
305   */
306  public TimeRange getTimeRange() {
307    return timeRange;
308  }
309
310  /**
311   * @return the action done if check succeeds
312   */
313  public Row getAction() {
314    return action;
315  }
316}