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 static org.junit.Assert.assertTrue;
021import static org.junit.Assert.fail;
022
023import java.io.IOException;
024import org.apache.hadoop.hbase.HBaseClassTestRule;
025import org.apache.hadoop.hbase.HBaseTestingUtility;
026import org.apache.hadoop.hbase.TableName;
027import org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException;
028import org.apache.hadoop.hbase.testclassification.MediumTests;
029import org.apache.hadoop.hbase.util.Bytes;
030import org.junit.AfterClass;
031import org.junit.BeforeClass;
032import org.junit.ClassRule;
033import org.junit.Rule;
034import org.junit.Test;
035import org.junit.experimental.categories.Category;
036import org.junit.rules.TestName;
037
038@Category(MediumTests.class)
039public class TestCheckAndMutate {
040
041  @ClassRule
042  public static final HBaseClassTestRule CLASS_RULE =
043      HBaseClassTestRule.forClass(TestCheckAndMutate.class);
044
045  private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
046  private static final byte[] ROWKEY = Bytes.toBytes("12345");
047  private static final byte[] FAMILY = Bytes.toBytes("cf");
048
049  @Rule
050  public TestName name = new TestName();
051
052  @BeforeClass
053  public static void setUpBeforeClass() throws Exception {
054    TEST_UTIL.startMiniCluster();
055  }
056
057  @AfterClass
058  public static void tearDownAfterClass() throws Exception {
059    TEST_UTIL.shutdownMiniCluster();
060  }
061
062  private Table createTable()
063  throws IOException, InterruptedException {
064    final TableName tableName = TableName.valueOf(name.getMethodName());
065    Table table = TEST_UTIL.createTable(tableName, FAMILY);
066    TEST_UTIL.waitTableAvailable(tableName.getName(), 5000);
067    return table;
068  }
069
070  private void putOneRow(Table table) throws IOException {
071    Put put = new Put(ROWKEY);
072    put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"));
073    put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b"));
074    put.addColumn(FAMILY, Bytes.toBytes("C"), Bytes.toBytes("c"));
075    table.put(put);
076  }
077
078  private void getOneRowAndAssertAllExist(final Table table) throws IOException {
079    Get get = new Get(ROWKEY);
080    Result result = table.get(get);
081    assertTrue("Column A value should be a",
082      Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("A"))).equals("a"));
083    assertTrue("Column B value should be b",
084      Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B"))).equals("b"));
085    assertTrue("Column C value should be c",
086      Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("C"))).equals("c"));
087  }
088
089  private void getOneRowAndAssertAllButCExist(final Table table) throws IOException {
090    Get get = new Get(ROWKEY);
091    Result result = table.get(get);
092    assertTrue("Column A value should be a",
093      Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("A"))).equals("a"));
094    assertTrue("Column B value should be b",
095      Bytes.toString(result.getValue(FAMILY, Bytes.toBytes("B"))).equals("b"));
096    assertTrue("Column C should not exist",
097    result.getValue(FAMILY, Bytes.toBytes("C")) == null);
098  }
099
100  private RowMutations makeRowMutationsWithColumnCDeleted() throws IOException {
101    RowMutations rm = new RowMutations(ROWKEY, 2);
102    Put put = new Put(ROWKEY);
103    put.addColumn(FAMILY, Bytes.toBytes("A"), Bytes.toBytes("a"));
104    put.addColumn(FAMILY, Bytes.toBytes("B"), Bytes.toBytes("b"));
105    rm.add(put);
106    Delete del = new Delete(ROWKEY);
107    del.addColumn(FAMILY, Bytes.toBytes("C"));
108    rm.add(del);
109    return rm;
110  }
111
112  private RowMutations getBogusRowMutations() throws IOException {
113    Put p = new Put(ROWKEY);
114    byte[] value = new byte[0];
115    p.addColumn(new byte[]{'b', 'o', 'g', 'u', 's'}, new byte[]{'A'}, value);
116    RowMutations rm = new RowMutations(ROWKEY);
117    rm.add(p);
118    return rm;
119  }
120
121  @Test
122  public void testCheckAndMutate() throws Throwable {
123    try (Table table = createTable()) {
124      // put one row
125      putOneRow(table);
126      // get row back and assert the values
127      getOneRowAndAssertAllExist(table);
128
129      // put the same row again with C column deleted
130      RowMutations rm = makeRowMutationsWithColumnCDeleted();
131      boolean res = table.checkAndMutate(ROWKEY, FAMILY).qualifier(Bytes.toBytes("A"))
132          .ifEquals(Bytes.toBytes("a")).thenMutate(rm);
133      assertTrue(res);
134
135      // get row back and assert the values
136      getOneRowAndAssertAllButCExist(table);
137
138      //Test that we get a region level exception
139      try {
140        rm = getBogusRowMutations();
141        table.checkAndMutate(ROWKEY, FAMILY).qualifier(Bytes.toBytes("A"))
142            .ifEquals(Bytes.toBytes("a")).thenMutate(rm);
143        fail("Expected NoSuchColumnFamilyException");
144      } catch (RetriesExhaustedWithDetailsException e) {
145        try {
146          throw e.getCause(0);
147        } catch (NoSuchColumnFamilyException e1) {
148          // expected
149        }
150      }
151    }
152  }
153
154  @Test
155  public void testCheckAndMutateWithBuilder() throws Throwable {
156    try (Table table = createTable()) {
157      // put one row
158      putOneRow(table);
159      // get row back and assert the values
160      getOneRowAndAssertAllExist(table);
161
162      // put the same row again with C column deleted
163      RowMutations rm = makeRowMutationsWithColumnCDeleted();
164      boolean res = table.checkAndMutate(ROWKEY, FAMILY).qualifier(Bytes.toBytes("A"))
165          .ifEquals(Bytes.toBytes("a")).thenMutate(rm);
166      assertTrue(res);
167
168      // get row back and assert the values
169      getOneRowAndAssertAllButCExist(table);
170
171      //Test that we get a region level exception
172      try {
173        rm = getBogusRowMutations();
174        table.checkAndMutate(ROWKEY, FAMILY).qualifier(Bytes.toBytes("A"))
175            .ifEquals(Bytes.toBytes("a")).thenMutate(rm);
176        fail("Expected NoSuchColumnFamilyException");
177      } catch (RetriesExhaustedWithDetailsException e) {
178        try {
179          throw e.getCause(0);
180        } catch (NoSuchColumnFamilyException e1) {
181          // expected
182        }
183      }
184    }
185  }
186
187}