View Javadoc

1   /*
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
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   * Provides the common setup framework and runtime services for coprocessor
70   * invocation from HBase services.
71   * @param <E> the specific environment extension that a concrete implementation
72   * provides
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    /** Ordered set of loaded coprocessors with lock */
88    protected SortedSet<E> coprocessors =
89        new SortedCopyOnWriteSet<E>(new EnvironmentPriorityComparator());
90    protected Configuration conf;
91    // unique file prefix to use for local copies of jars when classloading
92    protected String pathPrefix;
93    protected volatile int loadSequence;
94  
95    public CoprocessorHost() {
96      pathPrefix = UUID.randomUUID().toString();
97    }
98  
99    /**
100    * Not to be confused with the per-object _coprocessors_ (above),
101    * coprocessorNames is static and stores the set of all coprocessors ever
102    * loaded by any thread in this JVM. It is strictly additive: coprocessors are
103    * added to coprocessorNames, by loadInstance() but are never removed, since
104    * the intention is to preserve a history of all loaded coprocessors for
105    * diagnosis in case of server crash (HBASE-4014).
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    * Used to create a parameter to the HServerLoad constructor so that
115    * HServerLoad can provide information about the coprocessors loaded by this
116    * regionserver.
117    * (HBASE-4070: Improve region server metrics to report loaded coprocessors
118    * to master).
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    * Load system coprocessors. Read the class names from configuration.
130    * Called by constructor.
131    */
132   protected void loadSystemCoprocessors(Configuration conf, String confKey) {
133     Class<?> implClass = null;
134 
135     // load default coprocessors from configure file    
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     // add entire set to the collection for COW efficiency
164     coprocessors.addAll(configured);
165   }
166 
167   /**
168    * Load a coprocessor implementation into the host
169    * @param path path to implementation jar
170    * @param className the main class name
171    * @param priority chaining priority
172    * @param conf configuration for coprocessor
173    * @throws java.io.IOException Exception
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     //load custom code for coprocessor
200     Thread currentThread = Thread.currentThread();
201     ClassLoader hostClassLoader = currentThread.getContextClassLoader();
202     try{
203       // switch temporarily to the thread classloader for custom CP
204       currentThread.setContextClassLoader(cl);
205       E cpInstance = loadInstance(implClass, priority, conf);
206       return cpInstance;
207     } finally {
208       // restore the fresh (host) classloader
209       currentThread.setContextClassLoader(hostClassLoader);
210     }
211   }
212 
213   /**
214    * @param implClass Implementation class
215    * @param priority priority
216    * @param conf configuration
217    * @throws java.io.IOException Exception
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    * @param implClass Implementation class
227    * @param priority priority
228    * @param conf configuration
229    * @throws java.io.IOException Exception
230    */
231   public E loadInstance(Class<?> implClass, int priority, Configuration conf)
232       throws IOException {
233     // create the instance
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     // create the environment
245     E env = createEnvironment(implClass, impl, priority, ++loadSequence, conf);
246     if (env instanceof Environment) {
247       ((Environment)env).startup();
248     }
249     // HBASE-4014: maintain list of loaded coprocessors for later crash analysis
250     // if server (master or regionserver) aborts.
251     coprocessorNames.add(implClass.getName());
252     return env;
253   }
254 
255   /**
256    * Called when a new Coprocessor class is loaded
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    * Find a coprocessor implementation by class name
275    * @param className the class name
276    * @return the coprocessor, or null if not found
277    */
278   public Coprocessor findCoprocessor(String className) {
279     // initialize the coprocessors
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    * Retrieves the set of classloaders used to instantiate Coprocessor classes defined in external
291    * jar files.
292    * @return A set of ClassLoader instances
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         //do not include system classloader
301         externalClassLoaders.add(cl);
302       }
303     }
304     return externalClassLoaders;
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     // initialize the coprocessors
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    * Environment priority comparator.
325    * Coprocessors are chained in sorted order.
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    * Encapsulation of the environment of each coprocessor
347    */
348   public static class Environment implements CoprocessorEnvironment {
349 
350     /**
351      * A wrapper for HTable. Can be used to restrict privilege.
352      *
353      * Currently it just helps to track tables opened by a Coprocessor and
354      * facilitate close of them if it is aborted.
355      *
356      * We also disallow row locking.
357      *
358      * There is nothing now that will stop a coprocessor from using HTable
359      * objects directly instead of this API, but in the future we intend to
360      * analyze coprocessor implementations as they are loaded and reject those
361      * which attempt to use objects and methods outside the Environment
362      * sandbox.
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           // have to self-manage our connection, as per the HTable contract
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        * @deprecated {@link RowLock} and associated operations are deprecated.
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        * @deprecated {@link RowLock} and associated operations are deprecated.
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     /** The coprocessor */
580     public Coprocessor impl;
581     /** Chaining priority */
582     protected int priority = Coprocessor.PRIORITY_USER;
583     /** Current coprocessor state */
584     Coprocessor.State state = Coprocessor.State.UNINSTALLED;
585     /** Accounting for tables opened by the coprocessor */
586     protected List<HTableInterface> openTables =
587       Collections.synchronizedList(new ArrayList<HTableInterface>());
588     private int seq;
589     private Configuration conf;
590 
591     /**
592      * Constructor
593      * @param impl the coprocessor instance
594      * @param priority chaining priority
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     /** Initialize the environment */
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     /** Clean up the environment */
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       // clean up any table references
637       for (HTableInterface table: openTables) {
638         try {
639           ((HTableWrapper)table).internalClose();
640         } catch (IOException e) {
641           // nothing can be done here
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     /** @return the coprocessor environment version */
664     @Override
665     public int getVersion() {
666       return Coprocessor.VERSION;
667     }
668 
669     /** @return the HBase release */
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      * Open a table from within the Coprocessor environment
682      * @param tableName the table name
683      * @return an interface for manipulating the table
684      * @exception java.io.IOException Exception
685      */
686     @Override
687     public HTableInterface getTable(byte[] tableName) throws IOException {
688       return this.getTable(tableName, HTable.getDefaultExecutor(getConfiguration()));
689     }
690 
691     /**
692      * Open a table from within the Coprocessor environment
693      * @param tableName the table name
694      * @return an interface for manipulating the table
695      * @exception java.io.IOException Exception
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    * This is used by coprocessor hooks which are declared to throw IOException
725    * (or its subtypes). For such hooks, we should handle throwable objects
726    * depending on the Throwable's type. Those which are instances of
727    * IOException should be passed on to the client. This is in conformance with
728    * the HBase idiom regarding IOException: that it represents a circumstance
729    * that should be passed along to the client for its own handling. For
730    * example, a coprocessor that implements access controls would throw a
731    * subclass of IOException, such as AccessDeniedException, in its preGet()
732    * method to prevent an unauthorized client's performing a Get on a particular
733    * table.
734    * @param env Coprocessor Environment
735    * @param e Throwable object thrown by coprocessor.
736    * @exception IOException Exception
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     // If we got here, e is not an IOException. A loaded coprocessor has a
745     // fatal bug, and the server (master or regionserver) should remove the
746     // faulty coprocessor from its set of active coprocessors. Setting
747     // 'hbase.coprocessor.abortonerror' to true will cause abortServer(),
748     // which may be useful in development and testing environments where
749     // 'failing fast' for error analysis is desired.
750     if (env.getConfiguration().getBoolean("hbase.coprocessor.abortonerror",false)) {
751       // server is configured to abort.
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