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;
026import org.apache.hbase.thirdparty.com.google.common.base.Preconditions;
027
028/**
029 * Used to perform CheckAndMutate operations.
030 * <p>
031 * Use the builder class to instantiate a CheckAndMutate object.
032 * This builder class is fluent style APIs, the code are like:
033 * <pre>
034 * <code>
035 * // A CheckAndMutate operation where do the specified action if the column (specified by the
036 * // family and the qualifier) of the row equals to the specified value
037 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
038 *   .ifEquals(family, qualifier, value)
039 *   .build(put);
040 *
041 * // A CheckAndMutate operation where do the specified action if the column (specified by the
042 * // family and the qualifier) of the row doesn't exist
043 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
044 *   .ifNotExists(family, qualifier)
045 *   .build(put);
046 *
047 * // A CheckAndMutate operation where do the specified action if the row matches the filter
048 * CheckAndMutate checkAndMutate = CheckAndMutate.newBuilder(row)
049 *   .ifMatches(filter)
050 *   .build(delete);
051 * </code>
052 * </pre>
053 */
054@InterfaceAudience.Public
055@InterfaceStability.Evolving
056public final class CheckAndMutate implements Row {
057
058  /**
059   * A builder class for building a CheckAndMutate object.
060   */
061  @InterfaceAudience.Public
062  @InterfaceStability.Evolving
063  public static final class Builder {
064    private final byte[] row;
065    private byte[] family;
066    private byte[] qualifier;
067    private CompareOperator op;
068    private byte[] value;
069    private Filter filter;
070    private TimeRange timeRange;
071
072    private Builder(byte[] row) {
073      this.row = Preconditions.checkNotNull(row, "row is null");
074    }
075
076    /**
077     * Check for lack of column
078     *
079     * @param family family to check
080     * @param qualifier qualifier to check
081     * @return the CheckAndMutate object
082     */
083    public Builder ifNotExists(byte[] family, byte[] qualifier) {
084      return ifEquals(family, qualifier, null);
085    }
086
087    /**
088     * Check for equality
089     *
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("The row of the action <" +
137          Bytes.toStringBinary(action.getRow()) + "> doesn't match the original one <" +
138          Bytes.toStringBinary(this.row) + ">");
139      }
140      Preconditions.checkState(op != null || filter != null, "condition is null. You need to"
141        + " specify the condition by calling ifNotExists/ifEquals/ifMatches before building a"
142        + " CheckAndMutate object");
143    }
144
145    /**
146     * @param put data to put if check succeeds
147     * @return a CheckAndMutate object
148     */
149    public CheckAndMutate build(Put put) {
150      preCheck(put);
151      if (filter != null) {
152        return new CheckAndMutate(row, filter, timeRange, put);
153      } else {
154        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, put);
155      }
156    }
157
158    /**
159     * @param delete data to delete if check succeeds
160     * @return a CheckAndMutate object
161     */
162    public CheckAndMutate build(Delete delete) {
163      preCheck(delete);
164      if (filter != null) {
165        return new CheckAndMutate(row, filter, timeRange, delete);
166      } else {
167        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, delete);
168      }
169    }
170
171    /**
172     * @param increment data to increment if check succeeds
173     * @return a CheckAndMutate object
174     */
175    public CheckAndMutate build(Increment increment) {
176      preCheck(increment);
177      if (filter != null) {
178        return new CheckAndMutate(row, filter, timeRange, increment);
179      } else {
180        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, increment);
181      }
182    }
183
184    /**
185     * @param append data to append if check succeeds
186     * @return a CheckAndMutate object
187     */
188    public CheckAndMutate build(Append append) {
189      preCheck(append);
190      if (filter != null) {
191        return new CheckAndMutate(row, filter, timeRange, append);
192      } else {
193        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, append);
194      }
195    }
196
197    /**
198     * @param mutations mutations to perform if check succeeds
199     * @return a CheckAndMutate object
200     */
201    public CheckAndMutate build(RowMutations mutations) {
202      preCheck(mutations);
203      if (filter != null) {
204        return new CheckAndMutate(row, filter, timeRange, mutations);
205      } else {
206        return new CheckAndMutate(row, family, qualifier, op, value, timeRange, mutations);
207      }
208    }
209  }
210
211  /**
212   * returns a builder object to build a CheckAndMutate object
213   *
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}