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