/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hbase.constraint; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MediumTests; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.RetriesExhaustedWithDetailsException; import org.apache.hadoop.hbase.util.Bytes; import org.junit.After; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.experimental.categories.Category; /** * Do the complex testing of constraints against a minicluster */ @Category(MediumTests.class) public class TestConstraint { private static final Log LOG = LogFactory .getLog(TestConstraint.class); private static HBaseTestingUtility util; private static final byte[] tableName = Bytes.toBytes("test"); private static final byte[] dummy = Bytes.toBytes("dummy"); private static final byte[] row1 = Bytes.toBytes("r1"); private static final byte[] test = Bytes.toBytes("test"); @BeforeClass public static void setUpBeforeClass() throws Exception { util = new HBaseTestingUtility(); util.startMiniCluster(); } /** * Test that we run a passing constraint * @throws Exception */ @SuppressWarnings("unchecked") @Test public void testConstraintPasses() throws Exception { // create the table // it would be nice if this was also a method on the util HTableDescriptor desc = new HTableDescriptor(tableName); for (byte[] family : new byte[][] { dummy, test }) { desc.addFamily(new HColumnDescriptor(family)); } // add a constraint Constraints.add(desc, CheckWasRunConstraint.class); util.getHBaseAdmin().createTable(desc); HTable table = new HTable(util.getConfiguration(), tableName); table.setAutoFlush(true); // test that we don't fail on a valid put Put put = new Put(row1); byte[] value = Integer.toString(10).getBytes(); put.add(dummy, new byte[0], value); table.put(put); assertTrue(CheckWasRunConstraint.wasRun); } /** * Test that constraints will fail properly * @throws Exception */ @SuppressWarnings("unchecked") @Test(timeout = 10000) public void testConstraintFails() throws Exception { // create the table // it would be nice if this was also a method on the util HTableDescriptor desc = new HTableDescriptor(tableName); for (byte[] family : new byte[][] { dummy, test }) { desc.addFamily(new HColumnDescriptor(family)); } // add a constraint that is sure to fail Constraints.add(desc, AllFailConstraint.class); util.getHBaseAdmin().createTable(desc); HTable table = new HTable(util.getConfiguration(), tableName); table.setAutoFlush(true); // test that we do fail on violation Put put = new Put(row1); put.add(dummy, new byte[0], "fail".getBytes()); LOG.warn("Doing put in table"); try { table.put(put); fail("This put should not have suceeded - AllFailConstraint was not run!"); } catch (RetriesExhaustedWithDetailsException e) { List<Throwable> causes = e.getCauses(); assertEquals( "More than one failure cause - should only be the failure constraint exception", 1, causes.size()); Throwable t = causes.get(0); assertEquals(ConstraintException.class, t.getClass()); } table.close(); } /** * Check that if we just disable one constraint, then * @throws Throwable */ @SuppressWarnings("unchecked") @Test public void testDisableConstraint() throws Throwable { // create the table HTableDescriptor desc = new HTableDescriptor(tableName); // add a family to the table for (byte[] family : new byte[][] { dummy, test }) { desc.addFamily(new HColumnDescriptor(family)); } // add a constraint to make sure it others get run Constraints.add(desc, CheckWasRunConstraint.class); // Add Constraint to check Constraints.add(desc, AllFailConstraint.class); // and then disable the failing constraint Constraints.disableConstraint(desc, AllFailConstraint.class); util.getHBaseAdmin().createTable(desc); HTable table = new HTable(util.getConfiguration(), tableName); table.setAutoFlush(true); // test that we don't fail because its disabled Put put = new Put(row1); put.add(dummy, new byte[0], "pass".getBytes()); table.put(put); assertTrue(CheckWasRunConstraint.wasRun); } /** * Test that if we disable all constraints, then nothing gets run * @throws Throwable */ @SuppressWarnings("unchecked") @Test public void testDisableConstraints() throws Throwable { // create the table HTableDescriptor desc = new HTableDescriptor(tableName); // add a family to the table for (byte[] family : new byte[][] { dummy, test }) { desc.addFamily(new HColumnDescriptor(family)); } // add a constraint to check to see if is run Constraints.add(desc, CheckWasRunConstraint.class); // then disable all the constraints Constraints.disable(desc); util.getHBaseAdmin().createTable(desc); HTable table = new HTable(util.getConfiguration(), tableName); table.setAutoFlush(true); // test that we do fail on violation Put put = new Put(row1); put.add(dummy, new byte[0], "pass".getBytes()); LOG.warn("Doing put in table"); table.put(put); assertFalse(CheckWasRunConstraint.wasRun); } /** * Check to make sure a constraint is unloaded when it fails * @throws Exception */ @Test public void testIsUnloaded() throws Exception { // create the table HTableDescriptor desc = new HTableDescriptor(tableName); // add a family to the table for (byte[] family : new byte[][] { dummy, test }) { desc.addFamily(new HColumnDescriptor(family)); } // make sure that constraints are unloaded Constraints.add(desc, RuntimeFailConstraint.class); // add a constraint to check to see if is run Constraints.add(desc, CheckWasRunConstraint.class); CheckWasRunConstraint.wasRun = false; util.getHBaseAdmin().createTable(desc); HTable table = new HTable(util.getConfiguration(), tableName); table.setAutoFlush(true); // test that we do fail on violation Put put = new Put(row1); put.add(dummy, new byte[0], "pass".getBytes()); try{ table.put(put); fail("RuntimeFailConstraint wasn't triggered - this put shouldn't work!"); } catch (Exception e) {// NOOP } // try the put again, this time constraints are not used, so it works table.put(put); // and we make sure that constraints were not run... assertFalse(CheckWasRunConstraint.wasRun); table.close(); } @After public void cleanup() throws Exception { // cleanup CheckWasRunConstraint.wasRun = false; util.getHBaseAdmin().disableTable(tableName); util.getHBaseAdmin().deleteTable(tableName); } @AfterClass public static void tearDownAfterClass() throws Exception { util.shutdownMiniCluster(); } /** * Constraint to check that it was actually run (or not) */ public static class CheckWasRunConstraint extends BaseConstraint { public static boolean wasRun = false; @Override public void check(Put p) { wasRun = true; } } }