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