Skip to content

Commit 39f43a3

Browse files
committed
HBASE-26714 Introduce path configuration for system coprocessors
1 parent 7be9f26 commit 39f43a3

3 files changed

Lines changed: 141 additions & 6 deletions

File tree

hbase-common/src/test/java/org/apache/hadoop/hbase/util/ClassLoaderTestHelper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ public static void addJarFilesToJar(File targetJar,
213213
LOG.info("Adding jar file to outer jar file completed");
214214
}
215215

216-
static String localDirPath(Configuration conf) {
216+
public static String localDirPath(Configuration conf) {
217217
return conf.get(ClassLoaderBase.LOCAL_DIR_KEY)
218218
+ File.separator + "jars" + File.separator;
219219
}

hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import org.apache.hadoop.hbase.security.User;
4747
import org.apache.hadoop.hbase.util.CoprocessorClassLoader;
4848
import org.apache.hadoop.hbase.util.SortedList;
49+
import org.apache.hbase.thirdparty.com.google.common.base.Strings;
4950

5051
/**
5152
* Provides the common setup framework and runtime services for coprocessor
@@ -144,21 +145,41 @@ protected void loadSystemCoprocessors(Configuration conf, String confKey) {
144145

145146
int currentSystemPriority = Coprocessor.PRIORITY_SYSTEM;
146147
for (String className : defaultCPClasses) {
147-
String[] classNameAndPriority = className.split("\\|");
148+
// After HBASE-23710 and HBASE-26714 when configuring for system coprocessor, we accept
149+
// an optional format of className|priority|path
150+
String[] classNameToken = className.split("\\|");
148151
boolean hasPriorityOverride = false;
149-
className = classNameAndPriority[0];
152+
boolean hasPath = false;
153+
className = classNameToken[0];
150154
int overridePriority = Coprocessor.PRIORITY_SYSTEM;
151-
if (classNameAndPriority.length > 1){
152-
overridePriority = Integer.parseInt(classNameAndPriority[1]);
155+
Path path = null;
156+
if (classNameToken.length > 1 && !Strings.isNullOrEmpty(classNameToken[1])) {
157+
overridePriority = Integer.parseInt(classNameToken[1]);
153158
hasPriorityOverride = true;
154159
}
160+
if (classNameToken.length > 2 && !Strings.isNullOrEmpty(classNameToken[2])) {
161+
path = new Path(classNameToken[2].trim());
162+
hasPath = true;
163+
}
155164
className = className.trim();
156165
if (findCoprocessor(className) != null) {
157166
// If already loaded will just continue
158167
LOG.warn("Attempted duplicate loading of " + className + "; skipped");
159168
continue;
160169
}
161170
ClassLoader cl = this.getClass().getClassLoader();
171+
// override the class loader if a path for the system coprocessor is provided.
172+
if (hasPath) {
173+
try {
174+
cl = CoprocessorClassLoader.getClassLoader(path, this.getClass().getClassLoader(),
175+
pathPrefix, conf);
176+
} catch (IOException ioe) {
177+
// if system coprocessors cannot be obtained, we also abort the region server
178+
LOG.error("Cannot fetch external system coprocessor class {} with path {}", className,
179+
path);
180+
abortServer(className, ioe);
181+
}
182+
}
162183
Thread.currentThread().setContextClassLoader(cl);
163184
try {
164185
implClass = cl.loadClass(className);

hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorHost.java

Lines changed: 115 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@
2020
import static org.junit.Assert.assertEquals;
2121
import static org.junit.Assert.assertNotNull;
2222

23+
import java.io.File;
2324
import java.lang.reflect.InvocationTargetException;
2425

2526
import org.apache.hadoop.conf.Configuration;
2627
import org.apache.hadoop.hbase.Abortable;
2728
import org.apache.hadoop.hbase.Coprocessor;
2829
import org.apache.hadoop.hbase.CoprocessorEnvironment;
2930
import org.apache.hadoop.hbase.HBaseClassTestRule;
31+
import org.apache.hadoop.hbase.HBaseCommonTestingUtil;
3032
import org.apache.hadoop.hbase.HBaseConfiguration;
3133
import org.apache.hadoop.hbase.testclassification.SmallTests;
34+
import org.apache.hadoop.hbase.util.ClassLoaderTestHelper;
3235
import org.junit.Assert;
3336
import org.junit.ClassRule;
3437
import org.junit.Test;
@@ -41,6 +44,8 @@ public class TestCoprocessorHost {
4144
public static final HBaseClassTestRule CLASS_RULE =
4245
HBaseClassTestRule.forClass(TestCoprocessorHost.class);
4346

47+
private static final HBaseCommonTestingUtil TEST_UTIL = new HBaseCommonTestingUtil();
48+
4449
/**
4550
* An {@link Abortable} implementation for tests.
4651
*/
@@ -50,7 +55,7 @@ private static class TestAbortable implements Abortable {
5055
@Override
5156
public void abort(String why, Throwable e) {
5257
this.aborted = true;
53-
Assert.fail();
58+
Assert.fail(e.getMessage());
5459
}
5560

5661
@Override
@@ -97,6 +102,108 @@ public void testDoubleLoadingAndPriorityValue() {
97102
assertEquals(overridePriority, simpleEnv_v3.getPriority());
98103
}
99104

105+
@Test
106+
public void testLoadSystemCoprocessorWithPath() throws Exception {
107+
Configuration conf = TEST_UTIL.getConfiguration();
108+
final String key = "KEY";
109+
final String testClassName = "TestSystemCoprocessor";
110+
final String testClassNameWithPriorityAndPath = testClassName + "PriorityAndPath";
111+
112+
File jarFile = buildCoprocessorJar(testClassName);
113+
File jarFileWithPriorityAndPath = buildCoprocessorJar(testClassNameWithPriorityAndPath);
114+
115+
try {
116+
CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host;
117+
host = new CoprocessorHostForTest<>(conf);
118+
119+
// make a string of coprocessor with only priority
120+
int overridePriority = Integer.MAX_VALUE - 1;
121+
final String coprocessorWithPriority =
122+
SimpleRegionObserverV3.class.getName() + "|" + overridePriority;
123+
// make a string of coprocessor with path but no priority
124+
final String coprocessorWithPath =
125+
String.format("%s|%s|%s", testClassName, "", jarFile.getAbsolutePath());
126+
// make a string of coprocessor with priority and path
127+
final String coprocessorWithPriorityAndPath = String
128+
.format("%s|%s|%s", testClassNameWithPriorityAndPath, (overridePriority - 1),
129+
jarFileWithPriorityAndPath.getAbsolutePath());
130+
131+
// Try and load a system coprocessors
132+
conf.setStrings(key, SimpleRegionObserverV2.class.getName(), coprocessorWithPriority,
133+
coprocessorWithPath, coprocessorWithPriorityAndPath);
134+
host.loadSystemCoprocessors(conf, key);
135+
136+
// first loaded system coprocessor with default priority
137+
CoprocessorEnvironment<?> simpleEnv =
138+
host.findCoprocessorEnvironment(SimpleRegionObserverV2.class.getName());
139+
assertNotNull(simpleEnv);
140+
assertEquals(Coprocessor.PRIORITY_SYSTEM, simpleEnv.getPriority());
141+
142+
// external system coprocessor with default priority
143+
CoprocessorEnvironment<?> coprocessorEnvironmentWithPath =
144+
host.findCoprocessorEnvironment(testClassName);
145+
assertNotNull(coprocessorEnvironmentWithPath);
146+
assertEquals(Coprocessor.PRIORITY_SYSTEM + 1, coprocessorEnvironmentWithPath.getPriority());
147+
148+
// system coprocessor with configured priority
149+
CoprocessorEnvironment<?> coprocessorEnvironmentWithPriority =
150+
host.findCoprocessorEnvironment(SimpleRegionObserverV3.class.getName());
151+
assertNotNull(coprocessorEnvironmentWithPriority);
152+
assertEquals(overridePriority, coprocessorEnvironmentWithPriority.getPriority());
153+
154+
// external system coprocessor with override priority
155+
CoprocessorEnvironment<?> coprocessorEnvironmentWithPriorityAndPath =
156+
host.findCoprocessorEnvironment(testClassNameWithPriorityAndPath);
157+
assertNotNull(coprocessorEnvironmentWithPriorityAndPath);
158+
assertEquals(overridePriority - 1, coprocessorEnvironmentWithPriorityAndPath.getPriority());
159+
} finally {
160+
if (jarFile.exists()) {
161+
jarFile.delete();
162+
}
163+
if (jarFileWithPriorityAndPath.exists()) {
164+
jarFileWithPriorityAndPath.delete();
165+
}
166+
}
167+
}
168+
169+
@Test(expected = AssertionError.class)
170+
public void testLoadSystemCoprocessorWithPathDoesNotExist() throws Exception {
171+
Configuration conf = TEST_UTIL.getConfiguration();
172+
final String key = "KEY";
173+
final String testClassName = "TestSystemCoprocessor";
174+
175+
CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host;
176+
host = new CoprocessorHostForTest<>(conf);
177+
178+
// make a string of coprocessor with path but no priority
179+
final String coprocessorWithPath = testClassName + "||" + testClassName + ".jar";
180+
181+
// Try and load a system coprocessors
182+
conf.setStrings(key, coprocessorWithPath);
183+
// when loading non-exist with CoprocessorHostForTest host, it aborts with AssertionError
184+
host.loadSystemCoprocessors(conf, key);
185+
}
186+
187+
@Test(expected = AssertionError.class)
188+
public void testLoadSystemCoprocessorWithPathDoesNotExistAndPriority() throws Exception {
189+
Configuration conf = TEST_UTIL.getConfiguration();
190+
final String key = "KEY";
191+
final String testClassName = "TestSystemCoprocessor";
192+
193+
CoprocessorHost<RegionCoprocessor, CoprocessorEnvironment<RegionCoprocessor>> host;
194+
host = new CoprocessorHostForTest<>(conf);
195+
196+
int overridePriority = Integer.MAX_VALUE - 1;
197+
// make a string of coprocessor with path and priority
198+
final String coprocessor =
199+
testClassName + "|" + overridePriority + "|" + testClassName + ".jar";
200+
201+
// Try and load a system coprocessors
202+
conf.setStrings(key, coprocessor);
203+
// when loading non-exist coprocessor, it aborts with AssertionError
204+
host.loadSystemCoprocessors(conf, key);
205+
}
206+
100207
public static class SimpleRegionObserverV2 extends SimpleRegionObserver { }
101208

102209
public static class SimpleRegionObserverV3 extends SimpleRegionObserver {
@@ -128,4 +235,11 @@ public CoprocessorEnvironment<E> createEnvironment(final E instance, final int p
128235
return new BaseEnvironment<>(instance, priority, 0, cpHostConf);
129236
}
130237
}
238+
239+
private File buildCoprocessorJar(String className) throws Exception {
240+
String dataTestDir = TEST_UTIL.getDataTestDir().toString();
241+
String code = String.format("import org.apache.hadoop.hbase.coprocessor.*; public class %s"
242+
+ " implements RegionCoprocessor {}", className);
243+
return ClassLoaderTestHelper.buildJar(dataTestDir, className, code);
244+
}
131245
}

0 commit comments

Comments
 (0)