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  
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.hadoop.classification.InterfaceAudience;
38  import org.apache.hadoop.classification.InterfaceStability;
39  import org.apache.hadoop.conf.Configuration;
40  import org.apache.hadoop.fs.Path;
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.Server;
47  import org.apache.hadoop.hbase.TableName;
48  import org.apache.hadoop.hbase.client.Append;
49  import org.apache.hadoop.hbase.client.CoprocessorHConnection;
50  import org.apache.hadoop.hbase.client.Delete;
51  import org.apache.hadoop.hbase.client.Durability;
52  import org.apache.hadoop.hbase.client.Get;
53  import org.apache.hadoop.hbase.client.HConnection;
54  import org.apache.hadoop.hbase.client.HTable;
55  import org.apache.hadoop.hbase.client.HTableInterface;
56  import org.apache.hadoop.hbase.client.Increment;
57  import org.apache.hadoop.hbase.client.Put;
58  import org.apache.hadoop.hbase.client.Result;
59  import org.apache.hadoop.hbase.client.ResultScanner;
60  import org.apache.hadoop.hbase.client.Row;
61  import org.apache.hadoop.hbase.client.RowMutations;
62  import org.apache.hadoop.hbase.client.Scan;
63  import org.apache.hadoop.hbase.client.coprocessor.Batch;
64  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
65  import org.apache.hadoop.hbase.util.Bytes;
66  import org.apache.hadoop.hbase.util.CoprocessorClassLoader;
67  import org.apache.hadoop.hbase.util.SortedCopyOnWriteSet;
68  import org.apache.hadoop.hbase.util.VersionInfo;
69  import org.apache.hadoop.io.MultipleIOException;
70  
71  import com.google.protobuf.Service;
72  import com.google.protobuf.ServiceException;
73  
74  /**
75   * Provides the common setup framework and runtime services for coprocessor
76   * invocation from HBase services.
77   * @param <E> the specific environment extension that a concrete implementation
78   * provides
79   */
80  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
81  @InterfaceStability.Evolving
82  public abstract class CoprocessorHost<E extends CoprocessorEnvironment> {
83    public static final String REGION_COPROCESSOR_CONF_KEY =
84        "hbase.coprocessor.region.classes";
85    public static final String REGIONSERVER_COPROCESSOR_CONF_KEY =
86        "hbase.coprocessor.regionserver.classes";
87    public static final String USER_REGION_COPROCESSOR_CONF_KEY =
88        "hbase.coprocessor.user.region.classes";
89    public static final String MASTER_COPROCESSOR_CONF_KEY =
90        "hbase.coprocessor.master.classes";
91    public static final String WAL_COPROCESSOR_CONF_KEY =
92      "hbase.coprocessor.wal.classes";
93  
94    private static final Log LOG = LogFactory.getLog(CoprocessorHost.class);
95    /** Ordered set of loaded coprocessors with lock */
96    protected SortedSet<E> coprocessors =
97        new SortedCopyOnWriteSet<E>(new EnvironmentPriorityComparator());
98    protected Configuration conf;
99    // unique file prefix to use for local copies of jars when classloading
100   protected String pathPrefix;
101   protected volatile int loadSequence;
102 
103   public CoprocessorHost() {
104     pathPrefix = UUID.randomUUID().toString();
105   }
106 
107   /**
108    * Not to be confused with the per-object _coprocessors_ (above),
109    * coprocessorNames is static and stores the set of all coprocessors ever
110    * loaded by any thread in this JVM. It is strictly additive: coprocessors are
111    * added to coprocessorNames, by loadInstance() but are never removed, since
112    * the intention is to preserve a history of all loaded coprocessors for
113    * diagnosis in case of server crash (HBASE-4014).
114    */
115   private static Set<String> coprocessorNames =
116       Collections.synchronizedSet(new HashSet<String>());
117   public static Set<String> getLoadedCoprocessors() {
118       return coprocessorNames;
119   }
120 
121   /**
122    * Used to create a parameter to the HServerLoad constructor so that
123    * HServerLoad can provide information about the coprocessors loaded by this
124    * regionserver.
125    * (HBASE-4070: Improve region server metrics to report loaded coprocessors
126    * to master).
127    */
128   public Set<String> getCoprocessors() {
129     Set<String> returnValue = new TreeSet<String>();
130     for(CoprocessorEnvironment e: coprocessors) {
131       returnValue.add(e.getInstance().getClass().getSimpleName());
132     }
133     return returnValue;
134   }
135 
136   /**
137    * Load system coprocessors. Read the class names from configuration.
138    * Called by constructor.
139    */
140   protected void loadSystemCoprocessors(Configuration conf, String confKey) {
141     Class<?> implClass = null;
142 
143     // load default coprocessors from configure file
144     String[] defaultCPClasses = conf.getStrings(confKey);
145     if (defaultCPClasses == null || defaultCPClasses.length == 0)
146       return;
147 
148     int priority = Coprocessor.PRIORITY_SYSTEM;
149     List<E> configured = new ArrayList<E>();
150     for (String className : defaultCPClasses) {
151       className = className.trim();
152       if (findCoprocessor(className) != null) {
153         continue;
154       }
155       ClassLoader cl = this.getClass().getClassLoader();
156       Thread.currentThread().setContextClassLoader(cl);
157       try {
158         implClass = cl.loadClass(className);
159         configured.add(loadInstance(implClass, Coprocessor.PRIORITY_SYSTEM, conf));
160         LOG.info("System coprocessor " + className + " was loaded " +
161             "successfully with priority (" + priority++ + ").");
162       } catch (ClassNotFoundException e) {
163         LOG.warn("Class " + className + " cannot be found. " +
164             e.getMessage());
165       } catch (IOException e) {
166         LOG.warn("Load coprocessor " + className + " failed. " +
167             e.getMessage());
168       }
169     }
170 
171     // add entire set to the collection for COW efficiency
172     coprocessors.addAll(configured);
173   }
174 
175   /**
176    * Load a coprocessor implementation into the host
177    * @param path path to implementation jar
178    * @param className the main class name
179    * @param priority chaining priority
180    * @param conf configuration for coprocessor
181    * @throws java.io.IOException Exception
182    */
183   public E load(Path path, String className, int priority,
184       Configuration conf) throws IOException {
185     Class<?> implClass = null;
186     LOG.debug("Loading coprocessor class " + className + " with path " +
187         path + " and priority " + priority);
188 
189     ClassLoader cl = null;
190     if (path == null) {
191       try {
192         implClass = getClass().getClassLoader().loadClass(className);
193       } catch (ClassNotFoundException e) {
194         throw new IOException("No jar path specified for " + className);
195       }
196     } else {
197       cl = CoprocessorClassLoader.getClassLoader(
198         path, getClass().getClassLoader(), pathPrefix, conf);
199       try {
200         implClass = cl.loadClass(className);
201       } catch (ClassNotFoundException e) {
202         throw new IOException("Cannot load external coprocessor class " + className, e);
203       }
204     }
205 
206     //load custom code for coprocessor
207     Thread currentThread = Thread.currentThread();
208     ClassLoader hostClassLoader = currentThread.getContextClassLoader();
209     try{
210       // switch temporarily to the thread classloader for custom CP
211       currentThread.setContextClassLoader(cl);
212       E cpInstance = loadInstance(implClass, priority, conf);
213       return cpInstance;
214     } finally {
215       // restore the fresh (host) classloader
216       currentThread.setContextClassLoader(hostClassLoader);
217     }
218   }
219 
220   /**
221    * @param implClass Implementation class
222    * @param priority priority
223    * @param conf configuration
224    * @throws java.io.IOException Exception
225    */
226   public void load(Class<?> implClass, int priority, Configuration conf)
227       throws IOException {
228     E env = loadInstance(implClass, priority, conf);
229     coprocessors.add(env);
230   }
231 
232   /**
233    * @param implClass Implementation class
234    * @param priority priority
235    * @param conf configuration
236    * @throws java.io.IOException Exception
237    */
238   public E loadInstance(Class<?> implClass, int priority, Configuration conf)
239       throws IOException {
240     if (!Coprocessor.class.isAssignableFrom(implClass)) {
241       throw new IOException("Configured class " + implClass.getName() + " must implement "
242           + Coprocessor.class.getName() + " interface ");
243     }
244 
245     // create the instance
246     Coprocessor impl;
247     Object o = null;
248     try {
249       o = implClass.newInstance();
250       impl = (Coprocessor)o;
251     } catch (InstantiationException e) {
252       throw new IOException(e);
253     } catch (IllegalAccessException e) {
254       throw new IOException(e);
255     }
256     // create the environment
257     E env = createEnvironment(implClass, impl, priority, ++loadSequence, conf);
258     if (env instanceof Environment) {
259       ((Environment)env).startup();
260     }
261     // HBASE-4014: maintain list of loaded coprocessors for later crash analysis
262     // if server (master or regionserver) aborts.
263     coprocessorNames.add(implClass.getName());
264     return env;
265   }
266 
267   /**
268    * Called when a new Coprocessor class is loaded
269    */
270   public abstract E createEnvironment(Class<?> implClass, Coprocessor instance,
271       int priority, int sequence, Configuration conf);
272 
273   public void shutdown(CoprocessorEnvironment e) {
274     if (e instanceof Environment) {
275       ((Environment)e).shutdown();
276     } else {
277       LOG.warn("Shutdown called on unknown environment: "+
278           e.getClass().getName());
279     }
280   }
281 
282   /**
283    * Find a coprocessor implementation by class name
284    * @param className the class name
285    * @return the coprocessor, or null if not found
286    */
287   public Coprocessor findCoprocessor(String className) {
288     // initialize the coprocessors
289     for (E env: coprocessors) {
290       if (env.getInstance().getClass().getName().equals(className) ||
291           env.getInstance().getClass().getSimpleName().equals(className)) {
292         return env.getInstance();
293       }
294     }
295     return null;
296   }
297 
298   /**
299    * Retrieves the set of classloaders used to instantiate Coprocessor classes defined in external
300    * jar files.
301    * @return A set of ClassLoader instances
302    */
303   Set<ClassLoader> getExternalClassLoaders() {
304     Set<ClassLoader> externalClassLoaders = new HashSet<ClassLoader>();
305     final ClassLoader systemClassLoader = this.getClass().getClassLoader();
306     for (E env : coprocessors) {
307       ClassLoader cl = env.getInstance().getClass().getClassLoader();
308       if (cl != systemClassLoader ){
309         //do not include system classloader
310         externalClassLoaders.add(cl);
311       }
312     }
313     return externalClassLoaders;
314   }
315 
316   /**
317    * Find a coprocessor environment by class name
318    * @param className the class name
319    * @return the coprocessor, or null if not found
320    */
321   public CoprocessorEnvironment findCoprocessorEnvironment(String className) {
322     // initialize the coprocessors
323     for (E env: coprocessors) {
324       if (env.getInstance().getClass().getName().equals(className) ||
325           env.getInstance().getClass().getSimpleName().equals(className)) {
326         return env;
327       }
328     }
329     return null;
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       @Override
526       public Object[] batch(List<? extends Row> actions)
527           throws IOException, InterruptedException {
528         return table.batch(actions);
529       }
530 
531       @Override
532       public <R> void batchCallback(List<? extends Row> actions, Object[] results,
533           Batch.Callback<R> callback) throws IOException, InterruptedException {
534         table.batchCallback(actions, results, callback);
535       }
536 
537       @Override
538       public <R> Object[] batchCallback(List<? extends Row> actions,
539           Batch.Callback<R> callback) throws IOException, InterruptedException {
540         return table.batchCallback(actions, callback);
541       }
542 
543       @Override
544       public Result[] get(List<Get> gets) throws IOException {
545         return table.get(gets);
546       }
547 
548       @Override
549       public CoprocessorRpcChannel coprocessorService(byte[] row) {
550         return table.coprocessorService(row);
551       }
552 
553       @Override
554       public <T extends Service, R> Map<byte[], R> coprocessorService(Class<T> service,
555           byte[] startKey, byte[] endKey, Batch.Call<T, R> callable)
556           throws ServiceException, Throwable {
557         return table.coprocessorService(service, startKey, endKey, callable);
558       }
559 
560       @Override
561       public <T extends Service, R> void coprocessorService(Class<T> service,
562           byte[] startKey, byte[] endKey, Batch.Call<T, R> callable, Batch.Callback<R> callback)
563           throws ServiceException, Throwable {
564         table.coprocessorService(service, startKey, endKey, callable, callback);
565       }
566 
567       @Override
568       public void mutateRow(RowMutations rm) throws IOException {
569         table.mutateRow(rm);
570       }
571 
572       @Override
573       public void setAutoFlush(boolean autoFlush) {
574         table.setAutoFlush(autoFlush, autoFlush);
575       }
576 
577       @Override
578       public void setAutoFlush(boolean autoFlush, boolean clearBufferOnFail) {
579         table.setAutoFlush(autoFlush, clearBufferOnFail);
580       }
581 
582       @Override
583       public void setAutoFlushTo(boolean autoFlush) {
584         table.setAutoFlushTo(autoFlush);
585       }
586 
587       @Override
588       public long getWriteBufferSize() {
589          return table.getWriteBufferSize();
590       }
591 
592       @Override
593       public void setWriteBufferSize(long writeBufferSize) throws IOException {
594         table.setWriteBufferSize(writeBufferSize);
595       }
596 
597       @Override
598       public long incrementColumnValue(byte[] row, byte[] family,
599           byte[] qualifier, long amount, boolean writeToWAL) throws IOException {
600         return table.incrementColumnValue(row, family, qualifier, amount, writeToWAL);
601       }
602     }
603 
604     /** The coprocessor */
605     public Coprocessor impl;
606     /** Chaining priority */
607     protected int priority = Coprocessor.PRIORITY_USER;
608     /** Current coprocessor state */
609     Coprocessor.State state = Coprocessor.State.UNINSTALLED;
610     /** Accounting for tables opened by the coprocessor */
611     protected List<HTableInterface> openTables =
612       Collections.synchronizedList(new ArrayList<HTableInterface>());
613     private int seq;
614     private Configuration conf;
615 
616     /**
617      * Constructor
618      * @param impl the coprocessor instance
619      * @param priority chaining priority
620      */
621     public Environment(final Coprocessor impl, final int priority,
622         final int seq, final Configuration conf) {
623       this.impl = impl;
624       this.priority = priority;
625       this.state = Coprocessor.State.INSTALLED;
626       this.seq = seq;
627       this.conf = conf;
628     }
629 
630     /** Initialize the environment */
631     public void startup() {
632       if (state == Coprocessor.State.INSTALLED ||
633           state == Coprocessor.State.STOPPED) {
634         state = Coprocessor.State.STARTING;
635         try {
636           impl.start(this);
637           state = Coprocessor.State.ACTIVE;
638         } catch (IOException ioe) {
639           LOG.error("Error starting coprocessor "+impl.getClass().getName(), ioe);
640         }
641       } else {
642         LOG.warn("Not starting coprocessor "+impl.getClass().getName()+
643             " because not inactive (state="+state.toString()+")");
644       }
645     }
646 
647     /** Clean up the environment */
648     protected void shutdown() {
649       if (state == Coprocessor.State.ACTIVE) {
650         state = Coprocessor.State.STOPPING;
651         try {
652           impl.stop(this);
653           state = Coprocessor.State.STOPPED;
654         } catch (IOException ioe) {
655           LOG.error("Error stopping coprocessor "+impl.getClass().getName(), ioe);
656         }
657       } else {
658         LOG.warn("Not stopping coprocessor "+impl.getClass().getName()+
659             " because not active (state="+state.toString()+")");
660       }
661       // clean up any table references
662       for (HTableInterface table: openTables) {
663         try {
664           ((HTableWrapper)table).internalClose();
665         } catch (IOException e) {
666           // nothing can be done here
667           LOG.warn("Failed to close " +
668               Bytes.toStringBinary(table.getTableName()), e);
669         }
670       }
671     }
672 
673     @Override
674     public Coprocessor getInstance() {
675       return impl;
676     }
677 
678     @Override
679     public int getPriority() {
680       return priority;
681     }
682 
683     @Override
684     public int getLoadSequence() {
685       return seq;
686     }
687 
688     /** @return the coprocessor environment version */
689     @Override
690     public int getVersion() {
691       return Coprocessor.VERSION;
692     }
693 
694     /** @return the HBase release */
695     @Override
696     public String getHBaseVersion() {
697       return VersionInfo.getVersion();
698     }
699 
700     @Override
701     public Configuration getConfiguration() {
702       return conf;
703     }
704 
705     /**
706      * Open a table from within the Coprocessor environment
707      * @param tableName the table name
708      * @return an interface for manipulating the table
709      * @exception java.io.IOException Exception
710      */
711     @Override
712     public HTableInterface getTable(TableName tableName) throws IOException {
713       return this.getTable(tableName, HTable.getDefaultExecutor(getConfiguration()));
714     }
715 
716     /**
717      * Open a table from within the Coprocessor environment
718      * @param tableName the table name
719      * @return an interface for manipulating the table
720      * @exception java.io.IOException Exception
721      */
722     @Override
723     public HTableInterface getTable(TableName tableName, ExecutorService pool) throws IOException {
724       return new HTableWrapper(tableName, CoprocessorHConnection.getConnectionForEnvironment(this),
725           pool);
726     }
727   }
728 
729   protected void abortServer(final String service,
730       final Server server,
731       final CoprocessorEnvironment environment,
732       final Throwable e) {
733     String coprocessorName = (environment.getInstance()).toString();
734     server.abort("Aborting service: " + service + " running on : "
735             + server.getServerName() + " because coprocessor: "
736             + coprocessorName + " threw an exception.", e);
737   }
738 
739   protected void abortServer(final CoprocessorEnvironment environment,
740                              final Throwable e) {
741     String coprocessorName = (environment.getInstance()).toString();
742     LOG.error("The coprocessor: " + coprocessorName + " threw an unexpected " +
743         "exception: " + e + ", but there's no specific implementation of " +
744         " abortServer() for this coprocessor's environment.");
745   }
746 
747 
748   /**
749    * This is used by coprocessor hooks which are declared to throw IOException
750    * (or its subtypes). For such hooks, we should handle throwable objects
751    * depending on the Throwable's type. Those which are instances of
752    * IOException should be passed on to the client. This is in conformance with
753    * the HBase idiom regarding IOException: that it represents a circumstance
754    * that should be passed along to the client for its own handling. For
755    * example, a coprocessor that implements access controls would throw a
756    * subclass of IOException, such as AccessDeniedException, in its preGet()
757    * method to prevent an unauthorized client's performing a Get on a particular
758    * table.
759    * @param env Coprocessor Environment
760    * @param e Throwable object thrown by coprocessor.
761    * @exception IOException Exception
762    */
763   protected void handleCoprocessorThrowable(final CoprocessorEnvironment env,
764                                             final Throwable e)
765       throws IOException {
766     if (e instanceof IOException) {
767       throw (IOException)e;
768     }
769     // If we got here, e is not an IOException. A loaded coprocessor has a
770     // fatal bug, and the server (master or regionserver) should remove the
771     // faulty coprocessor from its set of active coprocessors. Setting
772     // 'hbase.coprocessor.abortonerror' to true will cause abortServer(),
773     // which may be useful in development and testing environments where
774     // 'failing fast' for error analysis is desired.
775     if (env.getConfiguration().getBoolean("hbase.coprocessor.abortonerror",false)) {
776       // server is configured to abort.
777       abortServer(env, e);
778     } else {
779       LOG.error("Removing coprocessor '" + env.toString() + "' from " +
780           "environment because it threw:  " + e,e);
781       coprocessors.remove(env);
782       try {
783         shutdown(env);
784       } catch (Exception x) {
785         LOG.error("Uncaught exception when shutting down coprocessor '"
786             + env.toString() + "'", x);
787       }
788       throw new DoNotRetryIOException("Coprocessor: '" + env.toString() +
789           "' threw: '" + e + "' and has been removed from the active " +
790           "coprocessor set.", e);
791     }
792   }
793 }
794 
795