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}