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.coprocessor; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertNotNull; 022 023import java.io.File; 024import java.lang.reflect.InvocationTargetException; 025import org.apache.hadoop.conf.Configuration; 026import org.apache.hadoop.hbase.Abortable; 027import org.apache.hadoop.hbase.Coprocessor; 028import org.apache.hadoop.hbase.CoprocessorEnvironment; 029import org.apache.hadoop.hbase.HBaseClassTestRule; 030import org.apache.hadoop.hbase.HBaseCommonTestingUtility; 031import org.apache.hadoop.hbase.HBaseConfiguration; 032import org.apache.hadoop.hbase.testclassification.SmallTests; 033import org.apache.hadoop.hbase.util.ClassLoaderTestHelper; 034import org.junit.Assert; 035import org.junit.ClassRule; 036import org.junit.Test; 037import org.junit.experimental.categories.Category; 038 039@Category({ SmallTests.class }) 040public class TestCoprocessorHost { 041 042 @ClassRule 043 public static final HBaseClassTestRule CLASS_RULE = 044 HBaseClassTestRule.forClass(TestCoprocessorHost.class); 045 046 private static final HBaseCommonTestingUtility TEST_UTIL = new HBaseCommonTestingUtility(); 047 048 /** 049 * An {@link Abortable} implementation for tests. 050 */ 051 private static class TestAbortable implements Abortable { 052 private volatile boolean aborted = false; 053 054 @Override 055 public void abort(String why, Throwable e) { 056 this.aborted = true; 057 Assert.fail(e.getMessage()); 058 } 059 060 @Override 061 public boolean isAborted() { 062 return this.aborted; 063 } 064 } 065 066 @Test 067 public void testDoubleLoadingAndPriorityValue() { 068 final Configuration conf = HBaseConfiguration.create(); 069 final String key = "KEY"; 070 final String coprocessor = "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver"; 071 072 CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host; 073 host = new CoprocessorHostForTest<>(conf); 074 int overridePriority = Integer.MAX_VALUE - 1; 075 076 final String coprocessor_v3 = SimpleRegionObserverV3.class.getName() + "|" + overridePriority; 077 078 // Try and load a coprocessor three times 079 conf.setStrings(key, coprocessor, coprocessor, coprocessor, 080 SimpleRegionObserverV2.class.getName(), coprocessor_v3); 081 host.loadSystemCoprocessors(conf, key); 082 083 // Three coprocessors(SimpleRegionObserver, SimpleRegionObserverV2, 084 // SimpleRegionObserverV3) loaded 085 Assert.assertEquals(3, host.coprocEnvironments.size()); 086 087 // Check the priority value 088 CoprocessorEnvironment<?> simpleEnv = 089 host.findCoprocessorEnvironment(SimpleRegionObserver.class.getName()); 090 CoprocessorEnvironment<?> simpleEnv_v2 = 091 host.findCoprocessorEnvironment(SimpleRegionObserverV2.class.getName()); 092 CoprocessorEnvironment<?> simpleEnv_v3 = 093 host.findCoprocessorEnvironment(SimpleRegionObserverV3.class.getName()); 094 095 assertNotNull(simpleEnv); 096 assertNotNull(simpleEnv_v2); 097 assertNotNull(simpleEnv_v3); 098 assertEquals(Coprocessor.PRIORITY_SYSTEM, simpleEnv.getPriority()); 099 assertEquals(Coprocessor.PRIORITY_SYSTEM + 1, simpleEnv_v2.getPriority()); 100 assertEquals(overridePriority, simpleEnv_v3.getPriority()); 101 } 102 103 @Test 104 public void testLoadSystemCoprocessorWithPath() throws Exception { 105 Configuration conf = TEST_UTIL.getConfiguration(); 106 final String key = "KEY"; 107 final String testClassName = "TestSystemCoprocessor"; 108 final String testClassNameWithPriorityAndPath = testClassName + "PriorityAndPath"; 109 110 File jarFile = buildCoprocessorJar(testClassName); 111 File jarFileWithPriorityAndPath = buildCoprocessorJar(testClassNameWithPriorityAndPath); 112 113 try { 114 CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host; 115 host = new CoprocessorHostForTest<>(conf); 116 117 // make a string of coprocessor with only priority 118 int overridePriority = Integer.MAX_VALUE - 1; 119 final String coprocessorWithPriority = 120 SimpleRegionObserverV3.class.getName() + "|" + overridePriority; 121 // make a string of coprocessor with path but no priority 122 final String coprocessorWithPath = 123 String.format("%s|%s|%s", testClassName, "", jarFile.getAbsolutePath()); 124 // make a string of coprocessor with priority and path 125 final String coprocessorWithPriorityAndPath = 126 String.format("%s|%s|%s", testClassNameWithPriorityAndPath, (overridePriority - 1), 127 jarFileWithPriorityAndPath.getAbsolutePath()); 128 129 // Try and load a system coprocessors 130 conf.setStrings(key, SimpleRegionObserverV2.class.getName(), coprocessorWithPriority, 131 coprocessorWithPath, coprocessorWithPriorityAndPath); 132 host.loadSystemCoprocessors(conf, key); 133 134 // first loaded system coprocessor with default priority 135 CoprocessorEnvironment<?> simpleEnv = 136 host.findCoprocessorEnvironment(SimpleRegionObserverV2.class.getName()); 137 assertNotNull(simpleEnv); 138 assertEquals(Coprocessor.PRIORITY_SYSTEM, simpleEnv.getPriority()); 139 140 // external system coprocessor with default priority 141 CoprocessorEnvironment<?> coprocessorEnvironmentWithPath = 142 host.findCoprocessorEnvironment(testClassName); 143 assertNotNull(coprocessorEnvironmentWithPath); 144 assertEquals(Coprocessor.PRIORITY_SYSTEM + 1, coprocessorEnvironmentWithPath.getPriority()); 145 146 // system coprocessor with configured priority 147 CoprocessorEnvironment<?> coprocessorEnvironmentWithPriority = 148 host.findCoprocessorEnvironment(SimpleRegionObserverV3.class.getName()); 149 assertNotNull(coprocessorEnvironmentWithPriority); 150 assertEquals(overridePriority, coprocessorEnvironmentWithPriority.getPriority()); 151 152 // external system coprocessor with override priority 153 CoprocessorEnvironment<?> coprocessorEnvironmentWithPriorityAndPath = 154 host.findCoprocessorEnvironment(testClassNameWithPriorityAndPath); 155 assertNotNull(coprocessorEnvironmentWithPriorityAndPath); 156 assertEquals(overridePriority - 1, coprocessorEnvironmentWithPriorityAndPath.getPriority()); 157 } finally { 158 if (jarFile.exists()) { 159 jarFile.delete(); 160 } 161 if (jarFileWithPriorityAndPath.exists()) { 162 jarFileWithPriorityAndPath.delete(); 163 } 164 } 165 } 166 167 @Test(expected = AssertionError.class) 168 public void testLoadSystemCoprocessorWithPathDoesNotExist() throws Exception { 169 Configuration conf = TEST_UTIL.getConfiguration(); 170 final String key = "KEY"; 171 final String testClassName = "TestSystemCoprocessor"; 172 173 CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host; 174 host = new CoprocessorHostForTest<>(conf); 175 176 // make a string of coprocessor with path but no priority 177 final String coprocessorWithPath = testClassName + "||" + testClassName + ".jar"; 178 179 // Try and load a system coprocessors 180 conf.setStrings(key, coprocessorWithPath); 181 // when loading non-exist with CoprocessorHostForTest host, it aborts with AssertionError 182 host.loadSystemCoprocessors(conf, key); 183 } 184 185 @Test(expected = AssertionError.class) 186 public void testLoadSystemCoprocessorWithPathDoesNotExistAndPriority() throws Exception { 187 Configuration conf = TEST_UTIL.getConfiguration(); 188 final String key = "KEY"; 189 final String testClassName = "TestSystemCoprocessor"; 190 191 CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host; 192 host = new CoprocessorHostForTest<>(conf); 193 194 int overridePriority = Integer.MAX_VALUE - 1; 195 // make a string of coprocessor with path and priority 196 final String coprocessor = 197 testClassName + "|" + overridePriority + "|" + testClassName + ".jar"; 198 199 // Try and load a system coprocessors 200 conf.setStrings(key, coprocessor); 201 // when loading non-exist coprocessor, it aborts with AssertionError 202 host.loadSystemCoprocessors(conf, key); 203 } 204 205 public static class SimpleRegionObserverV2 extends SimpleRegionObserver { 206 } 207 208 public static class SimpleRegionObserverV3 extends SimpleRegionObserver { 209 210 } 211 212 private static class CoprocessorHostForTest<E extends Coprocessor> 213 extends CoprocessorHost<E, CoprocessorEnvironment<E>> { 214 final Configuration cpHostConf; 215 216 public CoprocessorHostForTest(Configuration conf) { 217 super(new TestAbortable()); 218 cpHostConf = conf; 219 } 220 221 @Override 222 public E checkAndGetInstance(Class<?> implClass) 223 throws InstantiationException, IllegalAccessException { 224 try { 225 return (E) implClass.getDeclaredConstructor().newInstance(); 226 } catch (InvocationTargetException | NoSuchMethodException e) { 227 throw (InstantiationException) new InstantiationException().initCause(e); 228 } 229 } 230 231 @Override 232 public CoprocessorEnvironment<E> createEnvironment(final E instance, final int priority, 233 int sequence, Configuration conf) { 234 return new BaseEnvironment<>(instance, priority, 0, cpHostConf); 235 } 236 } 237 238 private File buildCoprocessorJar(String className) throws Exception { 239 String dataTestDir = TEST_UTIL.getDataTestDir().toString(); 240 String code = String.format("import org.apache.hadoop.hbase.coprocessor.*; public class %s" 241 + " implements RegionCoprocessor {}", className); 242 return ClassLoaderTestHelper.buildJar(dataTestDir, className, code); 243 } 244}