1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.coprocessor;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.SortedSet;
32 import java.util.TreeSet;
33 import java.util.UUID;
34 import java.util.concurrent.ExecutorService;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.hadoop.conf.Configuration;
39 import org.apache.hadoop.fs.Path;
40 import org.apache.hadoop.hbase.Coprocessor;
41 import org.apache.hadoop.hbase.CoprocessorEnvironment;
42 import org.apache.hadoop.hbase.DoNotRetryIOException;
43 import org.apache.hadoop.hbase.HTableDescriptor;
44 import org.apache.hadoop.hbase.Server;
45 import org.apache.hadoop.hbase.client.Append;
46 import org.apache.hadoop.hbase.client.CoprocessorHConnection;
47 import org.apache.hadoop.hbase.client.Delete;
48 import org.apache.hadoop.hbase.client.Get;
49 import org.apache.hadoop.hbase.client.HConnection;
50 import org.apache.hadoop.hbase.client.HTable;
51 import org.apache.hadoop.hbase.client.HTableInterface;
52 import org.apache.hadoop.hbase.client.Increment;
53 import org.apache.hadoop.hbase.client.Put;
54 import org.apache.hadoop.hbase.client.Result;
55 import org.apache.hadoop.hbase.client.ResultScanner;
56 import org.apache.hadoop.hbase.client.Row;
57 import org.apache.hadoop.hbase.client.RowLock;
58 import org.apache.hadoop.hbase.client.RowMutations;
59 import org.apache.hadoop.hbase.client.Scan;
60 import org.apache.hadoop.hbase.client.coprocessor.Batch;
61 import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
62 import org.apache.hadoop.hbase.util.Bytes;
63 import org.apache.hadoop.hbase.util.CoprocessorClassLoader;
64 import org.apache.hadoop.hbase.util.SortedCopyOnWriteSet;
65 import org.apache.hadoop.hbase.util.VersionInfo;
66 import org.apache.hadoop.io.MultipleIOException;
67
68
69
70
71
72
73
74 public abstract class CoprocessorHost<E extends CoprocessorEnvironment> {
75 public static final String REGION_COPROCESSOR_CONF_KEY =
76 "hbase.coprocessor.region.classes";
77 public static final String REGIONSERVER_COPROCESSOR_CONF_KEY =
78 "hbase.coprocessor.regionserver.classes";
79 public static final String USER_REGION_COPROCESSOR_CONF_KEY =
80 "hbase.coprocessor.user.region.classes";
81 public static final String MASTER_COPROCESSOR_CONF_KEY =
82 "hbase.coprocessor.master.classes";
83 public static final String WAL_COPROCESSOR_CONF_KEY =
84 "hbase.coprocessor.wal.classes";
85
86 private static final Log LOG = LogFactory.getLog(CoprocessorHost.class);
87
88 protected SortedSet<E> coprocessors =
89 new SortedCopyOnWriteSet<E>(new EnvironmentPriorityComparator());
90 protected Configuration conf;
91
92 protected String pathPrefix;
93 protected volatile int loadSequence;
94
95 public CoprocessorHost() {
96 pathPrefix = UUID.randomUUID().toString();
97 }
98
99
100
101
102
103
104
105
106
107 private static Set<String> coprocessorNames =
108 Collections.synchronizedSet(new HashSet<String>());
109 public static Set<String> getLoadedCoprocessors() {
110 return coprocessorNames;
111 }
112
113
114
115
116
117
118
119
120 public Set<String> getCoprocessors() {
121 Set<String> returnValue = new TreeSet<String>();
122 for(CoprocessorEnvironment e: coprocessors) {
123 returnValue.add(e.getInstance().getClass().getSimpleName());
124 }
125 return returnValue;
126 }
127
128
129
130
131
132 protected void loadSystemCoprocessors(Configuration conf, String confKey) {
133 Class<?> implClass = null;
134
135
136 String[] defaultCPClasses = conf.getStrings(confKey);
137 if (defaultCPClasses == null || defaultCPClasses.length == 0)
138 return;
139
140 int priority = Coprocessor.PRIORITY_SYSTEM;
141 List<E> configured = new ArrayList<E>();
142 for (String className : defaultCPClasses) {
143 className = className.trim();
144 if (findCoprocessor(className) != null) {
145 continue;
146 }
147 ClassLoader cl = this.getClass().getClassLoader();
148 Thread.currentThread().setContextClassLoader(cl);
149 try {
150 implClass = cl.loadClass(className);
151 configured.add(loadInstance(implClass, Coprocessor.PRIORITY_SYSTEM, conf));
152 LOG.info("System coprocessor " + className + " was loaded " +
153 "successfully with priority (" + priority++ + ").");
154 } catch (ClassNotFoundException e) {
155 LOG.warn("Class " + className + " cannot be found. " +
156 e.getMessage());
157 } catch (IOException e) {
158 LOG.warn("Load coprocessor " + className + " failed. " +
159 e.getMessage());
160 }
161 }
162
163
164 coprocessors.addAll(configured);
165 }
166
167
168
169
170
171
172
173
174
175 @SuppressWarnings("deprecation")
176 public E load(Path path, String className, int priority,
177 Configuration conf) throws IOException {
178 Class<?> implClass = null;
179 LOG.debug("Loading coprocessor class " + className + " with path " +
180 path + " and priority " + priority);
181
182 ClassLoader cl = null;
183 if (path == null) {
184 try {
185 implClass = getClass().getClassLoader().loadClass(className);
186 } catch (ClassNotFoundException e) {
187 throw new IOException("No jar path specified for " + className);
188 }
189 } else {
190 cl = CoprocessorClassLoader.getClassLoader(
191 path, getClass().getClassLoader(), pathPrefix, conf);
192 try {
193 implClass = cl.loadClass(className);
194 } catch (ClassNotFoundException e) {
195 throw new IOException("Cannot load external coprocessor class " + className, e);
196 }
197 }
198
199
200 Thread currentThread = Thread.currentThread();
201 ClassLoader hostClassLoader = currentThread.getContextClassLoader();
202 try{
203
204 currentThread.setContextClassLoader(cl);
205 E cpInstance = loadInstance(implClass, priority, conf);
206 return cpInstance;
207 } finally {
208
209 currentThread.setContextClassLoader(hostClassLoader);
210 }
211 }
212
213
214
215
216
217
218
219 public void load(Class<?> implClass, int priority, Configuration conf)
220 throws IOException {
221 E env = loadInstance(implClass, priority, conf);
222 coprocessors.add(env);
223 }
224
225
226
227
228
229
230
231 public E loadInstance(Class<?> implClass, int priority, Configuration conf)
232 throws IOException {
233
234 Coprocessor impl;
235 Object o = null;
236 try {
237 o = implClass.newInstance();
238 impl = (Coprocessor)o;
239 } catch (InstantiationException e) {
240 throw new IOException(e);
241 } catch (IllegalAccessException e) {
242 throw new IOException(e);
243 }
244
245 E env = createEnvironment(implClass, impl, priority, ++loadSequence, conf);
246 if (env instanceof Environment) {
247 ((Environment)env).startup();
248 }
249
250
251 coprocessorNames.add(implClass.getName());
252 return env;
253 }
254
255
256
257
258 public abstract E createEnvironment(Class<?> implClass, Coprocessor instance,
259 int priority, int sequence, Configuration conf);
260
261 public void shutdown(CoprocessorEnvironment e) {
262 if (e instanceof Environment) {
263 if (LOG.isDebugEnabled()) {
264 LOG.debug("Stop coprocessor " + e.getInstance().getClass().getName());
265 }
266 ((Environment)e).shutdown();
267 } else {
268 LOG.warn("Shutdown called on unknown environment: "+
269 e.getClass().getName());
270 }
271 }
272
273
274
275
276
277
278 public Coprocessor findCoprocessor(String className) {
279
280 for (E env: coprocessors) {
281 if (env.getInstance().getClass().getName().equals(className) ||
282 env.getInstance().getClass().getSimpleName().equals(className)) {
283 return env.getInstance();
284 }
285 }
286 return null;
287 }
288
289
290
291
292
293
294 Set<ClassLoader> getExternalClassLoaders() {
295 Set<ClassLoader> externalClassLoaders = new HashSet<ClassLoader>();
296 final ClassLoader systemClassLoader = this.getClass().getClassLoader();
297 for (E env : coprocessors) {
298 ClassLoader cl = env.getInstance().getClass().getClassLoader();
299 if (cl != systemClassLoader ){
300
301 externalClassLoaders.add(cl);
302 }
303 }
304 return externalClassLoaders;
305 }
306
307
308
309
310
311
312 public CoprocessorEnvironment findCoprocessorEnvironment(String className) {
313
314 for (E env: coprocessors) {
315 if (env.getInstance().getClass().getName().equals(className) ||
316 env.getInstance().getClass().getSimpleName().equals(className)) {
317 return env;
318 }
319 }
320 return null;
321 }
322
323
324
325
326
327 static class EnvironmentPriorityComparator
328 implements Comparator<CoprocessorEnvironment> {
329 public int compare(final CoprocessorEnvironment env1,
330 final CoprocessorEnvironment env2) {
331 if (env1.getPriority() < env2.getPriority()) {
332 return -1;
333 } else if (env1.getPriority() > env2.getPriority()) {
334 return 1;
335 }
336 if (env1.getLoadSequence() < env2.getLoadSequence()) {
337 return -1;
338 } else if (env1.getLoadSequence() > env2.getLoadSequence()) {
339 return 1;
340 }
341 return 0;
342 }
343 }
344
345
346
347
348 public static class Environment implements CoprocessorEnvironment {
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364 class HTableWrapper implements HTableInterface {
365
366 private byte[] tableName;
367 private HTable table;
368 private HConnection connection;
369
370 public HTableWrapper(byte[] tableName, HConnection connection, ExecutorService executor)
371 throws IOException {
372 this.tableName = tableName;
373 this.table = new HTable(tableName, connection, executor);
374 this.connection = connection;
375 openTables.add(this);
376 }
377
378 void internalClose() throws IOException {
379 List<IOException> exceptions = new ArrayList<IOException>(2);
380 try {
381 table.close();
382 } catch (IOException e) {
383 exceptions.add(e);
384 }
385 try {
386
387 if (this.connection != null) {
388 this.connection.close();
389 }
390 } catch (IOException e) {
391 exceptions.add(e);
392 }
393 if (!exceptions.isEmpty()) {
394 throw MultipleIOException.createIOException(exceptions);
395 }
396 }
397
398 public Configuration getConfiguration() {
399 return table.getConfiguration();
400 }
401
402 public void close() throws IOException {
403 try {
404 internalClose();
405 } finally {
406 openTables.remove(this);
407 }
408 }
409
410 public Result getRowOrBefore(byte[] row, byte[] family)
411 throws IOException {
412 return table.getRowOrBefore(row, family);
413 }
414
415 public Result get(Get get) throws IOException {
416 return table.get(get);
417 }
418
419 public boolean exists(Get get) throws IOException {
420 return table.exists(get);
421 }
422
423 public void put(Put put) throws IOException {
424 table.put(put);
425 }
426
427 public void put(List<Put> puts) throws IOException {
428 table.put(puts);
429 }
430
431 public void delete(Delete delete) throws IOException {
432 table.delete(delete);
433 }
434
435 public void delete(List<Delete> deletes) throws IOException {
436 table.delete(deletes);
437 }
438
439 public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
440 byte[] value, Put put) throws IOException {
441 return table.checkAndPut(row, family, qualifier, value, put);
442 }
443
444 public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
445 byte[] value, Delete delete) throws IOException {
446 return table.checkAndDelete(row, family, qualifier, value, delete);
447 }
448
449 public long incrementColumnValue(byte[] row, byte[] family,
450 byte[] qualifier, long amount) throws IOException {
451 return table.incrementColumnValue(row, family, qualifier, amount);
452 }
453
454 public long incrementColumnValue(byte[] row, byte[] family,
455 byte[] qualifier, long amount, boolean writeToWAL)
456 throws IOException {
457 return table.incrementColumnValue(row, family, qualifier, amount,
458 writeToWAL);
459 }
460
461 @Override
462 public Result append(Append append) throws IOException {
463 return table.append(append);
464 }
465
466 @Override
467 public Result increment(Increment increment) throws IOException {
468 return table.increment(increment);
469 }
470
471 public void flushCommits() throws IOException {
472 table.flushCommits();
473 }
474
475 public boolean isAutoFlush() {
476 return table.isAutoFlush();
477 }
478
479 public ResultScanner getScanner(Scan scan) throws IOException {
480 return table.getScanner(scan);
481 }
482
483 public ResultScanner getScanner(byte[] family) throws IOException {
484 return table.getScanner(family);
485 }
486
487 public ResultScanner getScanner(byte[] family, byte[] qualifier)
488 throws IOException {
489 return table.getScanner(family, qualifier);
490 }
491
492 public HTableDescriptor getTableDescriptor() throws IOException {
493 return table.getTableDescriptor();
494 }
495
496 public byte[] getTableName() {
497 return tableName;
498 }
499
500
501
502
503 public RowLock lockRow(byte[] row) throws IOException {
504 throw new RuntimeException(
505 "row locking is not allowed within the coprocessor environment");
506 }
507
508
509
510
511 public void unlockRow(RowLock rl) throws IOException {
512 throw new RuntimeException(
513 "row locking is not allowed within the coprocessor environment");
514 }
515
516 @Override
517 public void batch(List<? extends Row> actions, Object[] results)
518 throws IOException, InterruptedException {
519 table.batch(actions, results);
520 }
521
522 @Override
523 public Object[] batch(List<? extends Row> actions)
524 throws IOException, InterruptedException {
525 return table.batch(actions);
526 }
527
528 @Override
529 public Result[] get(List<Get> gets) throws IOException {
530 return table.get(gets);
531 }
532
533 @Override
534 public <T extends CoprocessorProtocol, R> void coprocessorExec(Class<T> protocol,
535 byte[] startKey, byte[] endKey, Batch.Call<T, R> callable,
536 Batch.Callback<R> callback) throws IOException, Throwable {
537 table.coprocessorExec(protocol, startKey, endKey, callable, callback);
538 }
539
540 @Override
541 public <T extends CoprocessorProtocol, R> Map<byte[], R> coprocessorExec(
542 Class<T> protocol, byte[] startKey, byte[] endKey, Batch.Call<T, R> callable)
543 throws IOException, Throwable {
544 return table.coprocessorExec(protocol, startKey, endKey, callable);
545 }
546
547 @Override
548 public <T extends CoprocessorProtocol> T coprocessorProxy(Class<T> protocol,
549 byte[] row) {
550 return table.coprocessorProxy(protocol, row);
551 }
552
553 @Override
554 public void mutateRow(RowMutations rm) throws IOException {
555 table.mutateRow(rm);
556 }
557
558 @Override
559 public void setAutoFlush(boolean autoFlush) {
560 table.setAutoFlush(autoFlush);
561 }
562
563 @Override
564 public void setAutoFlush(boolean autoFlush, boolean clearBufferOnFail) {
565 table.setAutoFlush(autoFlush, clearBufferOnFail);
566 }
567
568 @Override
569 public long getWriteBufferSize() {
570 return table.getWriteBufferSize();
571 }
572
573 @Override
574 public void setWriteBufferSize(long writeBufferSize) throws IOException {
575 table.setWriteBufferSize(writeBufferSize);
576 }
577 }
578
579
580 public Coprocessor impl;
581
582 protected int priority = Coprocessor.PRIORITY_USER;
583
584 Coprocessor.State state = Coprocessor.State.UNINSTALLED;
585
586 protected List<HTableInterface> openTables =
587 Collections.synchronizedList(new ArrayList<HTableInterface>());
588 private int seq;
589 private Configuration conf;
590
591
592
593
594
595
596 public Environment(final Coprocessor impl, final int priority,
597 final int seq, final Configuration conf) {
598 this.impl = impl;
599 this.priority = priority;
600 this.state = Coprocessor.State.INSTALLED;
601 this.seq = seq;
602 this.conf = conf;
603 }
604
605
606 public void startup() {
607 if (state == Coprocessor.State.INSTALLED ||
608 state == Coprocessor.State.STOPPED) {
609 state = Coprocessor.State.STARTING;
610 try {
611 impl.start(this);
612 state = Coprocessor.State.ACTIVE;
613 } catch (IOException ioe) {
614 LOG.error("Error starting coprocessor "+impl.getClass().getName(), ioe);
615 }
616 } else {
617 LOG.warn("Not starting coprocessor "+impl.getClass().getName()+
618 " because not inactive (state="+state.toString()+")");
619 }
620 }
621
622
623 protected void shutdown() {
624 if (state == Coprocessor.State.ACTIVE) {
625 state = Coprocessor.State.STOPPING;
626 try {
627 impl.stop(this);
628 state = Coprocessor.State.STOPPED;
629 } catch (IOException ioe) {
630 LOG.error("Error stopping coprocessor "+impl.getClass().getName(), ioe);
631 }
632 } else {
633 LOG.warn("Not stopping coprocessor "+impl.getClass().getName()+
634 " because not active (state="+state.toString()+")");
635 }
636
637 for (HTableInterface table: openTables) {
638 try {
639 ((HTableWrapper)table).internalClose();
640 } catch (IOException e) {
641
642 LOG.warn("Failed to close " +
643 Bytes.toStringBinary(table.getTableName()), e);
644 }
645 }
646 }
647
648 @Override
649 public Coprocessor getInstance() {
650 return impl;
651 }
652
653 @Override
654 public int getPriority() {
655 return priority;
656 }
657
658 @Override
659 public int getLoadSequence() {
660 return seq;
661 }
662
663
664 @Override
665 public int getVersion() {
666 return Coprocessor.VERSION;
667 }
668
669
670 @Override
671 public String getHBaseVersion() {
672 return VersionInfo.getVersion();
673 }
674
675 @Override
676 public Configuration getConfiguration() {
677 return conf;
678 }
679
680
681
682
683
684
685
686 @Override
687 public HTableInterface getTable(byte[] tableName) throws IOException {
688 return this.getTable(tableName, HTable.getDefaultExecutor(getConfiguration()));
689 }
690
691
692
693
694
695
696
697 @Override
698 public HTableInterface getTable(byte[] tableName, ExecutorService pool) throws IOException {
699 return new HTableWrapper(tableName, CoprocessorHConnection.getConnectionForEnvironment(this),
700 pool);
701 }
702 }
703
704 protected void abortServer(final String service,
705 final Server server,
706 final CoprocessorEnvironment environment,
707 final Throwable e) {
708 String coprocessorName = (environment.getInstance()).toString();
709 server.abort("Aborting service: " + service + " running on : "
710 + server.getServerName() + " because coprocessor: "
711 + coprocessorName + " threw an exception.", e);
712 }
713
714 protected void abortServer(final CoprocessorEnvironment environment,
715 final Throwable e) {
716 String coprocessorName = (environment.getInstance()).toString();
717 LOG.error("The coprocessor: " + coprocessorName + " threw an unexpected " +
718 "exception: " + e + ", but there's no specific implementation of " +
719 " abortServer() for this coprocessor's environment.");
720 }
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738 protected void handleCoprocessorThrowable(final CoprocessorEnvironment env,
739 final Throwable e)
740 throws IOException {
741 if (e instanceof IOException) {
742 throw (IOException)e;
743 }
744
745
746
747
748
749
750 if (env.getConfiguration().getBoolean("hbase.coprocessor.abortonerror",false)) {
751
752 abortServer(env, e);
753 } else {
754 LOG.error("Removing coprocessor '" + env.toString() + "' from " +
755 "environment because it threw: " + e,e);
756 coprocessors.remove(env);
757 try {
758 shutdown(env);
759 } catch (Exception x) {
760 LOG.error("Uncaught exception when shutting down coprocessor '"
761 + env.toString() + "'", x);
762 }
763 throw new DoNotRetryIOException("Coprocessor: '" + env.toString() +
764 "' threw: '" + e + "' and has been removed from the active " +
765 "coprocessor set.", e);
766 }
767 }
768 }
769
770