View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.coprocessor;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.HashSet;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.Set;
30  import java.util.SortedSet;
31  import java.util.TreeSet;
32  import java.util.UUID;
33  import java.util.concurrent.ExecutorService;
34  import java.util.concurrent.atomic.AtomicInteger;
35  
36  import com.google.protobuf.Descriptors;
37  
38  import org.apache.commons.logging.Log;
39  import org.apache.commons.logging.LogFactory;
40  import org.apache.hadoop.hbase.classification.InterfaceAudience;
41  import org.apache.hadoop.hbase.classification.InterfaceStability;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.fs.Path;
44  import org.apache.hadoop.hbase.Abortable;
45  import org.apache.hadoop.hbase.Coprocessor;
46  import org.apache.hadoop.hbase.CoprocessorEnvironment;
47  import org.apache.hadoop.hbase.DoNotRetryIOException;
48  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
49  import org.apache.hadoop.hbase.HTableDescriptor;
50  import org.apache.hadoop.hbase.TableName;
51  import org.apache.hadoop.hbase.client.Append;
52  import org.apache.hadoop.hbase.client.CoprocessorHConnection;
53  import org.apache.hadoop.hbase.client.Delete;
54  import org.apache.hadoop.hbase.client.Durability;
55  import org.apache.hadoop.hbase.client.Get;
56  import org.apache.hadoop.hbase.client.HConnection;
57  import org.apache.hadoop.hbase.client.HTable;
58  import org.apache.hadoop.hbase.client.HTableInterface;
59  import org.apache.hadoop.hbase.client.Increment;
60  import org.apache.hadoop.hbase.client.Put;
61  import org.apache.hadoop.hbase.client.Result;
62  import org.apache.hadoop.hbase.client.ResultScanner;
63  import org.apache.hadoop.hbase.client.Row;
64  import org.apache.hadoop.hbase.client.RowMutations;
65  import org.apache.hadoop.hbase.client.Scan;
66  import org.apache.hadoop.hbase.client.coprocessor.Batch;
67  import org.apache.hadoop.hbase.client.coprocessor.Batch.Callback;
68  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
69  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
70  import org.apache.hadoop.hbase.util.Bytes;
71  import org.apache.hadoop.hbase.util.CoprocessorClassLoader;
72  import org.apache.hadoop.hbase.util.SortedCopyOnWriteSet;
73  import org.apache.hadoop.hbase.util.VersionInfo;
74  import org.apache.hadoop.io.MultipleIOException;
75  
76  import com.google.protobuf.Message;
77  import com.google.protobuf.Service;
78  import com.google.protobuf.ServiceException;
79  
80  /**
81   * Provides the common setup framework and runtime services for coprocessor
82   * invocation from HBase services.
83   * @param <E> the specific environment extension that a concrete implementation
84   * provides
85   */
86  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
87  @InterfaceStability.Evolving
88  public abstract class CoprocessorHost<E extends CoprocessorEnvironment> {
89    public static final String REGION_COPROCESSOR_CONF_KEY =
90        "hbase.coprocessor.region.classes";
91    public static final String REGIONSERVER_COPROCESSOR_CONF_KEY =
92        "hbase.coprocessor.regionserver.classes";
93    public static final String USER_REGION_COPROCESSOR_CONF_KEY =
94        "hbase.coprocessor.user.region.classes";
95    public static final String MASTER_COPROCESSOR_CONF_KEY =
96        "hbase.coprocessor.master.classes";
97    public static final String WAL_COPROCESSOR_CONF_KEY =
98      "hbase.coprocessor.wal.classes";
99    public static final String ABORT_ON_ERROR_KEY = "hbase.coprocessor.abortonerror";
100   public static final boolean DEFAULT_ABORT_ON_ERROR = true;
101 
102   private static final Log LOG = LogFactory.getLog(CoprocessorHost.class);
103   protected Abortable abortable;
104   /** Ordered set of loaded coprocessors with lock */
105   protected SortedSet<E> coprocessors =
106       new SortedCopyOnWriteSet<E>(new EnvironmentPriorityComparator());
107   protected Configuration conf;
108   // unique file prefix to use for local copies of jars when classloading
109   protected String pathPrefix;
110   protected AtomicInteger loadSequence = new AtomicInteger();
111 
112   public CoprocessorHost(Abortable abortable) {
113     this.abortable = abortable;
114     this.pathPrefix = UUID.randomUUID().toString();
115   }
116 
117   /**
118    * Not to be confused with the per-object _coprocessors_ (above),
119    * coprocessorNames is static and stores the set of all coprocessors ever
120    * loaded by any thread in this JVM. It is strictly additive: coprocessors are
121    * added to coprocessorNames, by loadInstance() but are never removed, since
122    * the intention is to preserve a history of all loaded coprocessors for
123    * diagnosis in case of server crash (HBASE-4014).
124    */
125   private static Set<String> coprocessorNames =
126       Collections.synchronizedSet(new HashSet<String>());
127   public static Set<String> getLoadedCoprocessors() {
128       return coprocessorNames;
129   }
130 
131   /**
132    * Used to create a parameter to the HServerLoad constructor so that
133    * HServerLoad can provide information about the coprocessors loaded by this
134    * regionserver.
135    * (HBASE-4070: Improve region server metrics to report loaded coprocessors
136    * to master).
137    */
138   public Set<String> getCoprocessors() {
139     Set<String> returnValue = new TreeSet<String>();
140     for(CoprocessorEnvironment e: coprocessors) {
141       returnValue.add(e.getInstance().getClass().getSimpleName());
142     }
143     return returnValue;
144   }
145 
146   /**
147    * Load system coprocessors. Read the class names from configuration.
148    * Called by constructor.
149    */
150   protected void loadSystemCoprocessors(Configuration conf, String confKey) {
151     Class<?> implClass = null;
152 
153     // load default coprocessors from configure file
154     String[] defaultCPClasses = conf.getStrings(confKey);
155     if (defaultCPClasses == null || defaultCPClasses.length == 0)
156       return;
157 
158     int priority = Coprocessor.PRIORITY_SYSTEM;
159     List<E> configured = new ArrayList<E>();
160     for (String className : defaultCPClasses) {
161       className = className.trim();
162       if (findCoprocessor(className) != null) {
163         continue;
164       }
165       ClassLoader cl = this.getClass().getClassLoader();
166       Thread.currentThread().setContextClassLoader(cl);
167       try {
168         implClass = cl.loadClass(className);
169         configured.add(loadInstance(implClass, Coprocessor.PRIORITY_SYSTEM, conf));
170         LOG.info("System coprocessor " + className + " was loaded " +
171             "successfully with priority (" + priority++ + ").");
172       } catch (Throwable t) {
173         // We always abort if system coprocessors cannot be loaded
174         abortServer(className, t);
175       }
176     }
177 
178     // add entire set to the collection for COW efficiency
179     coprocessors.addAll(configured);
180   }
181 
182   /**
183    * Load a coprocessor implementation into the host
184    * @param path path to implementation jar
185    * @param className the main class name
186    * @param priority chaining priority
187    * @param conf configuration for coprocessor
188    * @throws java.io.IOException Exception
189    */
190   public E load(Path path, String className, int priority,
191       Configuration conf) throws IOException {
192     Class<?> implClass = null;
193     LOG.debug("Loading coprocessor class " + className + " with path " +
194         path + " and priority " + priority);
195 
196     ClassLoader cl = null;
197     if (path == null) {
198       try {
199         implClass = getClass().getClassLoader().loadClass(className);
200       } catch (ClassNotFoundException e) {
201         throw new IOException("No jar path specified for " + className);
202       }
203     } else {
204       cl = CoprocessorClassLoader.getClassLoader(
205         path, getClass().getClassLoader(), pathPrefix, conf);
206       try {
207         implClass = cl.loadClass(className);
208       } catch (ClassNotFoundException e) {
209         throw new IOException("Cannot load external coprocessor class " + className, e);
210       }
211     }
212 
213     //load custom code for coprocessor
214     Thread currentThread = Thread.currentThread();
215     ClassLoader hostClassLoader = currentThread.getContextClassLoader();
216     try{
217       // switch temporarily to the thread classloader for custom CP
218       currentThread.setContextClassLoader(cl);
219       E cpInstance = loadInstance(implClass, priority, conf);
220       return cpInstance;
221     } finally {
222       // restore the fresh (host) classloader
223       currentThread.setContextClassLoader(hostClassLoader);
224     }
225   }
226 
227   /**
228    * @param implClass Implementation class
229    * @param priority priority
230    * @param conf configuration
231    * @throws java.io.IOException Exception
232    */
233   public void load(Class<?> implClass, int priority, Configuration conf)
234       throws IOException {
235     E env = loadInstance(implClass, priority, conf);
236     coprocessors.add(env);
237   }
238 
239   /**
240    * @param implClass Implementation class
241    * @param priority priority
242    * @param conf configuration
243    * @throws java.io.IOException Exception
244    */
245   public E loadInstance(Class<?> implClass, int priority, Configuration conf)
246       throws IOException {
247     if (!Coprocessor.class.isAssignableFrom(implClass)) {
248       throw new IOException("Configured class " + implClass.getName() + " must implement "
249           + Coprocessor.class.getName() + " interface ");
250     }
251 
252     // create the instance
253     Coprocessor impl;
254     Object o = null;
255     try {
256       o = implClass.newInstance();
257       impl = (Coprocessor)o;
258     } catch (InstantiationException e) {
259       throw new IOException(e);
260     } catch (IllegalAccessException e) {
261       throw new IOException(e);
262     }
263     // create the environment
264     E env = createEnvironment(implClass, impl, priority, loadSequence.incrementAndGet(), conf);
265     if (env instanceof Environment) {
266       ((Environment)env).startup();
267     }
268     // HBASE-4014: maintain list of loaded coprocessors for later crash analysis
269     // if server (master or regionserver) aborts.
270     coprocessorNames.add(implClass.getName());
271     return env;
272   }
273 
274   /**
275    * Called when a new Coprocessor class is loaded
276    */
277   public abstract E createEnvironment(Class<?> implClass, Coprocessor instance,
278       int priority, int sequence, Configuration conf);
279 
280   public void shutdown(CoprocessorEnvironment e) {
281     if (e instanceof Environment) {
282       if (LOG.isDebugEnabled()) {
283         LOG.debug("Stop coprocessor " + e.getInstance().getClass().getName());
284       }
285       ((Environment)e).shutdown();
286     } else {
287       LOG.warn("Shutdown called on unknown environment: "+
288           e.getClass().getName());
289     }
290   }
291 
292   /**
293    * Find a coprocessor implementation by class name
294    * @param className the class name
295    * @return the coprocessor, or null if not found
296    */
297   public Coprocessor findCoprocessor(String className) {
298     for (E env: coprocessors) {
299       if (env.getInstance().getClass().getName().equals(className) ||
300           env.getInstance().getClass().getSimpleName().equals(className)) {
301         return env.getInstance();
302       }
303     }
304     return null;
305   }
306 
307   /**
308    * Find a coprocessor environment by class name
309    * @param className the class name
310    * @return the coprocessor, or null if not found
311    */
312   public CoprocessorEnvironment findCoprocessorEnvironment(String className) {
313     for (E env: coprocessors) {
314       if (env.getInstance().getClass().getName().equals(className) ||
315           env.getInstance().getClass().getSimpleName().equals(className)) {
316         return env;
317       }
318     }
319     return null;
320   }
321 
322   /**
323    * Retrieves the set of classloaders used to instantiate Coprocessor classes defined in external
324    * jar files.
325    * @return A set of ClassLoader instances
326    */
327   Set<ClassLoader> getExternalClassLoaders() {
328     Set<ClassLoader> externalClassLoaders = new HashSet<ClassLoader>();
329     final ClassLoader systemClassLoader = this.getClass().getClassLoader();
330     for (E env : coprocessors) {
331       ClassLoader cl = env.getInstance().getClass().getClassLoader();
332       if (cl != systemClassLoader ){
333         //do not include system classloader
334         externalClassLoaders.add(cl);
335       }
336     }
337     return externalClassLoaders;
338   }
339 
340   /**
341    * Environment priority comparator.
342    * Coprocessors are chained in sorted order.
343    */
344   static class EnvironmentPriorityComparator
345       implements Comparator<CoprocessorEnvironment> {
346     public int compare(final CoprocessorEnvironment env1,
347         final CoprocessorEnvironment env2) {
348       if (env1.getPriority() < env2.getPriority()) {
349         return -1;
350       } else if (env1.getPriority() > env2.getPriority()) {
351         return 1;
352       }
353       if (env1.getLoadSequence() < env2.getLoadSequence()) {
354         return -1;
355       } else if (env1.getLoadSequence() > env2.getLoadSequence()) {
356         return 1;
357       }
358       return 0;
359     }
360   }
361 
362   /**
363    * Encapsulation of the environment of each coprocessor
364    */
365   public static class Environment implements CoprocessorEnvironment {
366 
367     /**
368      * A wrapper for HTable. Can be used to restrict privilege.
369      *
370      * Currently it just helps to track tables opened by a Coprocessor and
371      * facilitate close of them if it is aborted.
372      *
373      * We also disallow row locking.
374      *
375      * There is nothing now that will stop a coprocessor from using HTable
376      * objects directly instead of this API, but in the future we intend to
377      * analyze coprocessor implementations as they are loaded and reject those
378      * which attempt to use objects and methods outside the Environment
379      * sandbox.
380      */
381     class HTableWrapper implements HTableInterface {
382 
383       private TableName tableName;
384       private HTable table;
385       private HConnection connection;
386 
387       public HTableWrapper(TableName tableName, HConnection connection, ExecutorService pool)
388           throws IOException {
389         this.tableName = tableName;
390         this.table = new HTable(tableName, connection, pool);
391         this.connection = connection;
392         openTables.add(this);
393       }
394 
395       void internalClose() throws IOException {
396         List<IOException> exceptions = new ArrayList<IOException>(2);
397         try {
398         table.close();
399         } catch (IOException e) {
400           exceptions.add(e);
401         }
402         try {
403           // have to self-manage our connection, as per the HTable contract
404           if (this.connection != null) {
405             this.connection.close();
406           }
407         } catch (IOException e) {
408           exceptions.add(e);
409         }
410         if (!exceptions.isEmpty()) {
411           throw MultipleIOException.createIOException(exceptions);
412         }
413       }
414 
415       public Configuration getConfiguration() {
416         return table.getConfiguration();
417       }
418 
419       public void close() throws IOException {
420         try {
421           internalClose();
422         } finally {
423           openTables.remove(this);
424         }
425       }
426 
427       public Result getRowOrBefore(byte[] row, byte[] family)
428           throws IOException {
429         return table.getRowOrBefore(row, family);
430       }
431 
432       public Result get(Get get) throws IOException {
433         return table.get(get);
434       }
435 
436       public boolean exists(Get get) throws IOException {
437         return table.exists(get);
438       }
439 
440       public Boolean[] exists(List<Get> gets) throws IOException{
441         return table.exists(gets);
442       }
443 
444       public void put(Put put) throws IOException {
445         table.put(put);
446       }
447 
448       public void put(List<Put> puts) throws IOException {
449         table.put(puts);
450       }
451 
452       public void delete(Delete delete) throws IOException {
453         table.delete(delete);
454       }
455 
456       public void delete(List<Delete> deletes) throws IOException {
457         table.delete(deletes);
458       }
459 
460       public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
461           byte[] value, Put put) throws IOException {
462         return table.checkAndPut(row, family, qualifier, value, put);
463       }
464 
465       public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
466           byte[] value, Delete delete) throws IOException {
467         return table.checkAndDelete(row, family, qualifier, value, delete);
468       }
469 
470       public long incrementColumnValue(byte[] row, byte[] family,
471           byte[] qualifier, long amount) throws IOException {
472         return table.incrementColumnValue(row, family, qualifier, amount);
473       }
474 
475       public long incrementColumnValue(byte[] row, byte[] family,
476           byte[] qualifier, long amount, Durability durability)
477           throws IOException {
478         return table.incrementColumnValue(row, family, qualifier, amount,
479             durability);
480       }
481 
482       @Override
483       public Result append(Append append) throws IOException {
484         return table.append(append);
485       }
486 
487       @Override
488       public Result increment(Increment increment) throws IOException {
489         return table.increment(increment);
490       }
491 
492       public void flushCommits() throws IOException {
493         table.flushCommits();
494       }
495 
496       public boolean isAutoFlush() {
497         return table.isAutoFlush();
498       }
499 
500       public ResultScanner getScanner(Scan scan) throws IOException {
501         return table.getScanner(scan);
502       }
503 
504       public ResultScanner getScanner(byte[] family) throws IOException {
505         return table.getScanner(family);
506       }
507 
508       public ResultScanner getScanner(byte[] family, byte[] qualifier)
509           throws IOException {
510         return table.getScanner(family, qualifier);
511       }
512 
513       public HTableDescriptor getTableDescriptor() throws IOException {
514         return table.getTableDescriptor();
515       }
516 
517       @Override
518       public byte[] getTableName() {
519         return tableName.getName();
520       }
521 
522       @Override
523       public TableName getName() {
524         return table.getName();
525       }
526 
527       @Override
528       public void batch(List<? extends Row> actions, Object[] results)
529           throws IOException, InterruptedException {
530         table.batch(actions, results);
531       }
532 
533       /**
534        * {@inheritDoc}
535        * @deprecated If any exception is thrown by one of the actions, there is no way to
536        * retrieve the partially executed results. Use {@link #batch(List, Object[])} instead.
537        */
538       @Override
539       public Object[] batch(List<? extends Row> actions)
540           throws IOException, InterruptedException {
541         return table.batch(actions);
542       }
543 
544       @Override
545       public <R> void batchCallback(List<? extends Row> actions, Object[] results,
546           Batch.Callback<R> callback) throws IOException, InterruptedException {
547         table.batchCallback(actions, results, callback);
548       }
549 
550       /**
551        * {@inheritDoc}
552        * @deprecated If any exception is thrown by one of the actions, there is no way to
553        * retrieve the partially executed results. Use 
554        * {@link #batchCallback(List, Object[], org.apache.hadoop.hbase.client.coprocessor.Batch.Callback)}
555        * instead.
556        */
557       @Override
558       public <R> Object[] batchCallback(List<? extends Row> actions,
559           Batch.Callback<R> callback) throws IOException, InterruptedException {
560         return table.batchCallback(actions, callback);
561       }
562 
563       @Override
564       public Result[] get(List<Get> gets) throws IOException {
565         return table.get(gets);
566       }
567 
568       @Override
569       public CoprocessorRpcChannel coprocessorService(byte[] row) {
570         return table.coprocessorService(row);
571       }
572 
573       @Override
574       public <T extends Service, R> Map<byte[], R> coprocessorService(Class<T> service,
575           byte[] startKey, byte[] endKey, Batch.Call<T, R> callable)
576           throws ServiceException, Throwable {
577         return table.coprocessorService(service, startKey, endKey, callable);
578       }
579 
580       @Override
581       public <T extends Service, R> void coprocessorService(Class<T> service,
582           byte[] startKey, byte[] endKey, Batch.Call<T, R> callable, Batch.Callback<R> callback)
583           throws ServiceException, Throwable {
584         table.coprocessorService(service, startKey, endKey, callable, callback);
585       }
586 
587       @Override
588       public void mutateRow(RowMutations rm) throws IOException {
589         table.mutateRow(rm);
590       }
591 
592       @Override
593       public void setAutoFlush(boolean autoFlush) {
594         table.setAutoFlush(autoFlush, autoFlush);
595       }
596 
597       @Override
598       public void setAutoFlush(boolean autoFlush, boolean clearBufferOnFail) {
599         table.setAutoFlush(autoFlush, clearBufferOnFail);
600       }
601 
602       @Override
603       public void setAutoFlushTo(boolean autoFlush) {
604         table.setAutoFlushTo(autoFlush);
605       }
606 
607       @Override
608       public long getWriteBufferSize() {
609          return table.getWriteBufferSize();
610       }
611 
612       @Override
613       public void setWriteBufferSize(long writeBufferSize) throws IOException {
614         table.setWriteBufferSize(writeBufferSize);
615       }
616 
617       @Override
618       public long incrementColumnValue(byte[] row, byte[] family,
619           byte[] qualifier, long amount, boolean writeToWAL) throws IOException {
620         return table.incrementColumnValue(row, family, qualifier, amount, writeToWAL);
621       }
622 
623       @Override
624       public <R extends Message> Map<byte[], R> batchCoprocessorService(
625           Descriptors.MethodDescriptor method, Message request, byte[] startKey,
626           byte[] endKey, R responsePrototype) throws ServiceException, Throwable {
627         return table.batchCoprocessorService(method, request, startKey, endKey, responsePrototype);
628       }
629 
630       @Override
631       public <R extends Message> void batchCoprocessorService(Descriptors.MethodDescriptor method,
632           Message request, byte[] startKey, byte[] endKey, R responsePrototype,
633           Callback<R> callback) throws ServiceException, Throwable {
634         table.batchCoprocessorService(method, request, startKey, endKey, responsePrototype,
635             callback);
636       }
637 
638       @Override
639       public boolean checkAndMutate(byte[] row, byte[] family, byte[] qualifier,
640           CompareOp compareOp, byte[] value, RowMutations mutation) throws IOException {
641         return table.checkAndMutate(row, family, qualifier, compareOp, value, mutation);
642       }
643     }
644 
645     /** The coprocessor */
646     public Coprocessor impl;
647     /** Chaining priority */
648     protected int priority = Coprocessor.PRIORITY_USER;
649     /** Current coprocessor state */
650     Coprocessor.State state = Coprocessor.State.UNINSTALLED;
651     /** Accounting for tables opened by the coprocessor */
652     protected List<HTableInterface> openTables =
653       Collections.synchronizedList(new ArrayList<HTableInterface>());
654     private int seq;
655     private Configuration conf;
656     private ClassLoader classLoader;
657 
658     /**
659      * Constructor
660      * @param impl the coprocessor instance
661      * @param priority chaining priority
662      */
663     public Environment(final Coprocessor impl, final int priority,
664         final int seq, final Configuration conf) {
665       this.impl = impl;
666       this.classLoader = impl.getClass().getClassLoader();
667       this.priority = priority;
668       this.state = Coprocessor.State.INSTALLED;
669       this.seq = seq;
670       this.conf = conf;
671     }
672 
673     /** Initialize the environment */
674     public void startup() throws IOException {
675       if (state == Coprocessor.State.INSTALLED ||
676           state == Coprocessor.State.STOPPED) {
677         state = Coprocessor.State.STARTING;
678         Thread currentThread = Thread.currentThread();
679         ClassLoader hostClassLoader = currentThread.getContextClassLoader();
680         try {
681           currentThread.setContextClassLoader(this.getClassLoader());
682           impl.start(this);
683           state = Coprocessor.State.ACTIVE;
684         } finally {
685           currentThread.setContextClassLoader(hostClassLoader);
686         }
687       } else {
688         LOG.warn("Not starting coprocessor "+impl.getClass().getName()+
689             " because not inactive (state="+state.toString()+")");
690       }
691     }
692 
693     /** Clean up the environment */
694     protected void shutdown() {
695       if (state == Coprocessor.State.ACTIVE) {
696         state = Coprocessor.State.STOPPING;
697         Thread currentThread = Thread.currentThread();
698         ClassLoader hostClassLoader = currentThread.getContextClassLoader();
699         try {
700           currentThread.setContextClassLoader(this.getClassLoader());
701           impl.stop(this);
702           state = Coprocessor.State.STOPPED;
703         } catch (IOException ioe) {
704           LOG.error("Error stopping coprocessor "+impl.getClass().getName(), ioe);
705         } finally {
706           currentThread.setContextClassLoader(hostClassLoader);
707         }
708       } else {
709         LOG.warn("Not stopping coprocessor "+impl.getClass().getName()+
710             " because not active (state="+state.toString()+")");
711       }
712       // clean up any table references
713       for (HTableInterface table: openTables) {
714         try {
715           ((HTableWrapper)table).internalClose();
716         } catch (IOException e) {
717           // nothing can be done here
718           LOG.warn("Failed to close " +
719               Bytes.toStringBinary(table.getTableName()), e);
720         }
721       }
722     }
723 
724     @Override
725     public Coprocessor getInstance() {
726       return impl;
727     }
728 
729     @Override
730     public ClassLoader getClassLoader() {
731       return classLoader;
732     }
733 
734     @Override
735     public int getPriority() {
736       return priority;
737     }
738 
739     @Override
740     public int getLoadSequence() {
741       return seq;
742     }
743 
744     /** @return the coprocessor environment version */
745     @Override
746     public int getVersion() {
747       return Coprocessor.VERSION;
748     }
749 
750     /** @return the HBase release */
751     @Override
752     public String getHBaseVersion() {
753       return VersionInfo.getVersion();
754     }
755 
756     @Override
757     public Configuration getConfiguration() {
758       return conf;
759     }
760 
761     /**
762      * Open a table from within the Coprocessor environment
763      * @param tableName the table name
764      * @return an interface for manipulating the table
765      * @exception java.io.IOException Exception
766      */
767     @Override
768     public HTableInterface getTable(TableName tableName) throws IOException {
769       return this.getTable(tableName, HTable.getDefaultExecutor(getConfiguration()));
770     }
771 
772     /**
773      * Open a table from within the Coprocessor environment
774      * @param tableName the table name
775      * @return an interface for manipulating the table
776      * @exception java.io.IOException Exception
777      */
778     @Override
779     public HTableInterface getTable(TableName tableName, ExecutorService pool) throws IOException {
780       return new HTableWrapper(tableName, CoprocessorHConnection.getConnectionForEnvironment(this),
781           pool);
782     }
783   }
784 
785   protected void abortServer(final CoprocessorEnvironment environment, final Throwable e) {
786     abortServer(environment.getInstance().getClass().getName(), e);
787   }
788 
789   protected void abortServer(final String coprocessorName, final Throwable e) {
790     String message = "The coprocessor " + coprocessorName + " threw " + e.toString();
791     LOG.error(message, e);
792     if (abortable != null) {
793       abortable.abort(message, e);
794     } else {
795       LOG.warn("No available Abortable, process was not aborted");
796     }
797   }
798 
799   /**
800    * This is used by coprocessor hooks which are declared to throw IOException
801    * (or its subtypes). For such hooks, we should handle throwable objects
802    * depending on the Throwable's type. Those which are instances of
803    * IOException should be passed on to the client. This is in conformance with
804    * the HBase idiom regarding IOException: that it represents a circumstance
805    * that should be passed along to the client for its own handling. For
806    * example, a coprocessor that implements access controls would throw a
807    * subclass of IOException, such as AccessDeniedException, in its preGet()
808    * method to prevent an unauthorized client's performing a Get on a particular
809    * table.
810    * @param env Coprocessor Environment
811    * @param e Throwable object thrown by coprocessor.
812    * @exception IOException Exception
813    */
814   protected void handleCoprocessorThrowable(final CoprocessorEnvironment env, final Throwable e)
815       throws IOException {
816     if (e instanceof IOException) {
817       throw (IOException)e;
818     }
819     // If we got here, e is not an IOException. A loaded coprocessor has a
820     // fatal bug, and the server (master or regionserver) should remove the
821     // faulty coprocessor from its set of active coprocessors. Setting
822     // 'hbase.coprocessor.abortonerror' to true will cause abortServer(),
823     // which may be useful in development and testing environments where
824     // 'failing fast' for error analysis is desired.
825     if (env.getConfiguration().getBoolean(ABORT_ON_ERROR_KEY, DEFAULT_ABORT_ON_ERROR)) {
826       // server is configured to abort.
827       abortServer(env, e);
828     } else {
829       LOG.error("Removing coprocessor '" + env.toString() + "' from " +
830           "environment because it threw:  " + e,e);
831       coprocessors.remove(env);
832       try {
833         shutdown(env);
834       } catch (Exception x) {
835         LOG.error("Uncaught exception when shutting down coprocessor '"
836             + env.toString() + "'", x);
837       }
838       throw new DoNotRetryIOException("Coprocessor: '" + env.toString() +
839           "' threw: '" + e + "' and has been removed from the active " +
840           "coprocessor set.", e);
841     }
842   }
843 }