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; 027import java.util.Arrays; 028import java.util.Objects; 029 030/** 031 * Used to perform CheckAndMutate operations. 032 * <p> 033 * Use the builder class to instantiate a CheckAndMutate object. 034 * This builder class is fluent style APIs, the code are like: 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 * 081 * @param family family to check 082 * @param qualifier qualifier to check 083 * @return the CheckAndMutate object 084 */ 085 public Builder ifNotExists(byte[] family, byte[] qualifier) { 086 return ifEquals(family, qualifier, null); 087 } 088 089 /** 090 * Check for equality 091 * 092 * @param family family to check 093 * @param qualifier qualifier to check 094 * @param value the expected value 095 * @return the CheckAndMutate object 096 */ 097 public Builder ifEquals(byte[] family, byte[] qualifier, byte[] value) { 098 return ifMatches(family, qualifier, CompareOperator.EQUAL, value); 099 } 100 101 /** 102 * @param family family to check 103 * @param qualifier qualifier to check 104 * @param compareOp comparison operator to use 105 * @param value the expected value 106 * @return the CheckAndMutate object 107 */ 108 public Builder ifMatches(byte[] family, byte[] qualifier, CompareOperator compareOp, 109 byte[] value) { 110 this.family = Preconditions.checkNotNull(family, "family is null"); 111 this.qualifier = qualifier; 112 this.op = Preconditions.checkNotNull(compareOp, "compareOp is null"); 113 this.value = value; 114 return this; 115 } 116 117 /** 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 * @param timeRange time range to check 128 * @return the CheckAndMutate object 129 */ 130 public Builder timeRange(TimeRange timeRange) { 131 this.timeRange = timeRange; 132 return this; 133 } 134 135 private void preCheck(Row action) { 136 Preconditions.checkNotNull(action, "action is null"); 137 if (!Bytes.equals(row, action.getRow())) { 138 throw new IllegalArgumentException("The row of the action <" + 139 Bytes.toStringBinary(action.getRow()) + "> doesn't match the original one <" + 140 Bytes.toStringBinary(this.row) + ">"); 141 } 142 Preconditions.checkState(op != null || filter != null, "condition is null. You need to" 143 + " specify the condition by calling ifNotExists/ifEquals/ifMatches before building a" 144 + " CheckAndMutate object"); 145 } 146 147 /** 148 * @param put data to put if check succeeds 149 * @return a CheckAndMutate object 150 */ 151 public CheckAndMutate build(Put put) { 152 preCheck(put); 153 if (filter != null) { 154 return new CheckAndMutate(row, filter, timeRange, put); 155 } else { 156 return new CheckAndMutate(row, family, qualifier, op, value, timeRange, put); 157 } 158 } 159 160 /** 161 * @param delete data to delete if check succeeds 162 * @return a CheckAndMutate object 163 */ 164 public CheckAndMutate build(Delete delete) { 165 preCheck(delete); 166 if (filter != null) { 167 return new CheckAndMutate(row, filter, timeRange, delete); 168 } else { 169 return new CheckAndMutate(row, family, qualifier, op, value, timeRange, delete); 170 } 171 } 172 173 /** 174 * @param increment data to increment if check succeeds 175 * @return a CheckAndMutate object 176 */ 177 public CheckAndMutate build(Increment increment) { 178 preCheck(increment); 179 if (filter != null) { 180 return new CheckAndMutate(row, filter, timeRange, increment); 181 } else { 182 return new CheckAndMutate(row, family, qualifier, op, value, timeRange, increment); 183 } 184 } 185 186 /** 187 * @param append data to append if check succeeds 188 * @return a CheckAndMutate object 189 */ 190 public CheckAndMutate build(Append append) { 191 preCheck(append); 192 if (filter != null) { 193 return new CheckAndMutate(row, filter, timeRange, append); 194 } else { 195 return new CheckAndMutate(row, family, qualifier, op, value, timeRange, append); 196 } 197 } 198 199 /** 200 * @param mutations mutations to perform if check succeeds 201 * @return a CheckAndMutate object 202 */ 203 public CheckAndMutate build(RowMutations mutations) { 204 preCheck(mutations); 205 if (filter != null) { 206 return new CheckAndMutate(row, filter, timeRange, mutations); 207 } else { 208 return new CheckAndMutate(row, family, qualifier, op, value, timeRange, mutations); 209 } 210 } 211 } 212 213 /** 214 * returns a builder object to build a CheckAndMutate object 215 * 216 * @param row row 217 * @return a builder object 218 */ 219 public static Builder newBuilder(byte[] row) { 220 return new Builder(row); 221 } 222 223 private final byte[] row; 224 private final byte[] family; 225 private final byte[] qualifier; 226 private final CompareOperator op; 227 private final byte[] value; 228 private final Filter filter; 229 private final TimeRange timeRange; 230 private final Row action; 231 232 private CheckAndMutate(byte[] row, byte[] family, byte[] qualifier,final CompareOperator op, 233 byte[] value, TimeRange timeRange, Row action) { 234 this.row = row; 235 this.family = family; 236 this.qualifier = qualifier; 237 this.op = op; 238 this.value = value; 239 this.filter = null; 240 this.timeRange = timeRange != null ? timeRange : TimeRange.allTime(); 241 this.action = action; 242 } 243 244 private CheckAndMutate(byte[] row, Filter filter, TimeRange timeRange, Row action) { 245 this.row = row; 246 this.family = null; 247 this.qualifier = null; 248 this.op = null; 249 this.value = null; 250 this.filter = filter; 251 this.timeRange = timeRange != null ? timeRange : TimeRange.allTime(); 252 this.action = action; 253 } 254 255 /** 256 * @return the row 257 */ 258 @Override 259 public byte[] getRow() { 260 return row; 261 } 262 263 @Override 264 public int compareTo(Row row) { 265 return Bytes.compareTo(this.getRow(), row.getRow()); 266 } 267 268 // Added to get rid of the stopbugs error 269 @Override 270 public boolean equals(Object obj) { 271 if (this == obj) { 272 return true; 273 } 274 if (obj == null || getClass() != obj.getClass()) { 275 return false; 276 } 277 Row other = (Row) obj; 278 return compareTo(other) == 0; 279 } 280 281 @Override 282 public int hashCode() { 283 return Bytes.hashCode(this.getRow()); 284 } 285 286 /** 287 * @return the family to check 288 */ 289 public byte[] getFamily() { 290 return family; 291 } 292 293 /** 294 * @return the qualifier to check 295 */ 296 public byte[] getQualifier() { 297 return qualifier; 298 } 299 300 /** 301 * @return the comparison operator 302 */ 303 public CompareOperator getCompareOp() { 304 return op; 305 } 306 307 /** 308 * @return the expected value 309 */ 310 public byte[] getValue() { 311 return value; 312 } 313 314 /** 315 * @return the filter to check 316 */ 317 public Filter getFilter() { 318 return filter; 319 } 320 321 /** 322 * @return whether this has a filter or not 323 */ 324 public boolean hasFilter() { 325 return filter != null; 326 } 327 328 /** 329 * @return the time range to check 330 */ 331 public TimeRange getTimeRange() { 332 return timeRange; 333 } 334 335 /** 336 * @return the action done if check succeeds 337 */ 338 public Row getAction() { 339 return action; 340 } 341}