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.regionserver;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.NavigableSet;
27  import java.util.concurrent.ConcurrentHashMap;
28  import java.util.concurrent.ConcurrentMap;
29  import java.util.regex.Matcher;
30  
31  import org.apache.commons.collections.map.AbstractReferenceMap;
32  import org.apache.commons.collections.map.ReferenceMap;
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.fs.FileSystem;
37  import org.apache.hadoop.fs.Path;
38  import org.apache.hadoop.hbase.Cell;
39  import org.apache.hadoop.hbase.Coprocessor;
40  import org.apache.hadoop.hbase.CoprocessorEnvironment;
41  import org.apache.hadoop.hbase.HBaseConfiguration;
42  import org.apache.hadoop.hbase.HConstants;
43  import org.apache.hadoop.hbase.HRegionInfo;
44  import org.apache.hadoop.hbase.client.Append;
45  import org.apache.hadoop.hbase.client.Delete;
46  import org.apache.hadoop.hbase.client.Durability;
47  import org.apache.hadoop.hbase.client.Get;
48  import org.apache.hadoop.hbase.client.Increment;
49  import org.apache.hadoop.hbase.client.Mutation;
50  import org.apache.hadoop.hbase.client.Put;
51  import org.apache.hadoop.hbase.client.Result;
52  import org.apache.hadoop.hbase.client.Scan;
53  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
54  import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
55  import org.apache.hadoop.hbase.coprocessor.EndpointObserver;
56  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
57  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
58  import org.apache.hadoop.hbase.coprocessor.RegionObserver;
59  import org.apache.hadoop.hbase.coprocessor.RegionObserver.MutationType;
60  import org.apache.hadoop.hbase.filter.ByteArrayComparable;
61  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
62  import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
63  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
64  import org.apache.hadoop.hbase.io.Reference;
65  import org.apache.hadoop.hbase.io.hfile.CacheConfig;
66  import org.apache.hadoop.hbase.regionserver.HRegion.Operation;
67  import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
68  import org.apache.hadoop.hbase.regionserver.wal.HLogKey;
69  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
70  import org.apache.hadoop.hbase.util.Bytes;
71  import org.apache.hadoop.hbase.util.Pair;
72  
73  import com.google.common.collect.ImmutableList;
74  import com.google.protobuf.Message;
75  import com.google.protobuf.Service;
76  
77  /**
78   * Implements the coprocessor environment and runtime support for coprocessors
79   * loaded within a {@link HRegion}.
80   */
81  public class RegionCoprocessorHost
82      extends CoprocessorHost<RegionCoprocessorHost.RegionEnvironment> {
83  
84    private static final Log LOG = LogFactory.getLog(RegionCoprocessorHost.class);
85    // The shared data map
86    private static ReferenceMap sharedDataMap =
87        new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK);
88  
89    /**
90     * Encapsulation of the environment of each coprocessor
91     */
92    static class RegionEnvironment extends CoprocessorHost.Environment
93        implements RegionCoprocessorEnvironment {
94  
95      private HRegion region;
96      private RegionServerServices rsServices;
97      ConcurrentMap<String, Object> sharedData;
98  
99      /**
100      * Constructor
101      * @param impl the coprocessor instance
102      * @param priority chaining priority
103      */
104     public RegionEnvironment(final Coprocessor impl, final int priority,
105         final int seq, final Configuration conf, final HRegion region,
106         final RegionServerServices services, final ConcurrentMap<String, Object> sharedData) {
107       super(impl, priority, seq, conf);
108       this.region = region;
109       this.rsServices = services;
110       this.sharedData = sharedData;
111     }
112 
113     /** @return the region */
114     @Override
115     public HRegion getRegion() {
116       return region;
117     }
118 
119     /** @return reference to the region server services */
120     @Override
121     public RegionServerServices getRegionServerServices() {
122       return rsServices;
123     }
124 
125     public void shutdown() {
126       super.shutdown();
127     }
128 
129     @Override
130     public ConcurrentMap<String, Object> getSharedData() {
131       return sharedData;
132     }
133   }
134 
135   /** The region server services */
136   RegionServerServices rsServices;
137   /** The region */
138   HRegion region;
139 
140   /**
141    * Constructor
142    * @param region the region
143    * @param rsServices interface to available region server functionality
144    * @param conf the configuration
145    */
146   public RegionCoprocessorHost(final HRegion region,
147       final RegionServerServices rsServices, final Configuration conf) {
148     super(rsServices);
149     this.conf = conf;
150     this.rsServices = rsServices;
151     this.region = region;
152     this.pathPrefix = Integer.toString(this.region.getRegionInfo().hashCode());
153 
154     // load system default cp's from configuration.
155     loadSystemCoprocessors(conf, REGION_COPROCESSOR_CONF_KEY);
156 
157     // load system default cp's for user tables from configuration.
158     if (!region.getRegionInfo().getTable().isSystemTable()) {
159       loadSystemCoprocessors(conf, USER_REGION_COPROCESSOR_CONF_KEY);
160     }
161 
162     // load Coprocessor From HDFS
163     loadTableCoprocessors(conf);
164   }
165 
166   void loadTableCoprocessors(final Configuration conf) {
167     // scan the table attributes for coprocessor load specifications
168     // initialize the coprocessors
169     List<RegionEnvironment> configured = new ArrayList<RegionEnvironment>();
170     for (Map.Entry<ImmutableBytesWritable,ImmutableBytesWritable> e:
171         region.getTableDesc().getValues().entrySet()) {
172       String key = Bytes.toString(e.getKey().get()).trim();
173       String spec = Bytes.toString(e.getValue().get()).trim();
174       if (HConstants.CP_HTD_ATTR_KEY_PATTERN.matcher(key).matches()) {
175         // found one
176         try {
177           Matcher matcher = HConstants.CP_HTD_ATTR_VALUE_PATTERN.matcher(spec);
178           if (matcher.matches()) {
179             // jar file path can be empty if the cp class can be loaded
180             // from class loader.
181             Path path = matcher.group(1).trim().isEmpty() ?
182                 null : new Path(matcher.group(1).trim());
183             String className = matcher.group(2).trim();
184             int priority = matcher.group(3).trim().isEmpty() ?
185                 Coprocessor.PRIORITY_USER : Integer.valueOf(matcher.group(3));
186             String cfgSpec = null;
187             try {
188               cfgSpec = matcher.group(4);
189             } catch (IndexOutOfBoundsException ex) {
190               // ignore
191             }
192             Configuration ourConf;
193             if (cfgSpec != null) {
194               cfgSpec = cfgSpec.substring(cfgSpec.indexOf('|') + 1);
195               // do an explicit deep copy of the passed configuration
196               ourConf = new Configuration(false);
197               HBaseConfiguration.merge(ourConf, conf);
198               Matcher m = HConstants.CP_HTD_ATTR_VALUE_PARAM_PATTERN.matcher(cfgSpec);
199               while (m.find()) {
200                 ourConf.set(m.group(1), m.group(2));
201               }
202             } else {
203               ourConf = conf;
204             }
205             // Load encompasses classloading and coprocessor initialization
206             try {
207               RegionEnvironment env = load(path, className, priority, ourConf);
208               configured.add(env);
209               LOG.info("Loaded coprocessor " + className + " from HTD of " +
210                 region.getTableDesc().getTableName().getNameAsString() + " successfully.");
211             } catch (Throwable t) {
212               // Coprocessor failed to load, do we abort on error?
213               if (conf.getBoolean(ABORT_ON_ERROR_KEY, DEFAULT_ABORT_ON_ERROR)) {
214                 abortServer(className, t);
215               } else {
216                 LOG.error("Failed to load coprocessor " + className, t);
217               }
218             }
219           } else {
220             LOG.error("Malformed table coprocessor specification: key=" + key +
221               ", spec: " + spec);
222           }
223         } catch (Exception ioe) {
224           LOG.error("Malformed table coprocessor specification: key=" + key +
225             ", spec: " + spec);
226         }
227       }
228     }
229     // add together to coprocessor set for COW efficiency
230     coprocessors.addAll(configured);
231   }
232 
233   @Override
234   public RegionEnvironment createEnvironment(Class<?> implClass,
235       Coprocessor instance, int priority, int seq, Configuration conf) {
236     // Check if it's an Endpoint.
237     // Due to current dynamic protocol design, Endpoint
238     // uses a different way to be registered and executed.
239     // It uses a visitor pattern to invoke registered Endpoint
240     // method.
241     for (Class<?> c : implClass.getInterfaces()) {
242       if (CoprocessorService.class.isAssignableFrom(c)) {
243         region.registerService( ((CoprocessorService)instance).getService() );
244       }
245     }
246     ConcurrentMap<String, Object> classData;
247     // make sure only one thread can add maps
248     synchronized (sharedDataMap) {
249       // as long as at least one RegionEnvironment holds on to its classData it will
250       // remain in this map
251       classData = (ConcurrentMap<String, Object>)sharedDataMap.get(implClass.getName());
252       if (classData == null) {
253         classData = new ConcurrentHashMap<String, Object>();
254         sharedDataMap.put(implClass.getName(), classData);
255       }
256     }
257     return new RegionEnvironment(instance, priority, seq, conf, region,
258         rsServices, classData);
259   }
260 
261   /**
262    * HBASE-4014 : This is used by coprocessor hooks which are not declared to throw exceptions.
263    *
264    * For example, {@link
265    * org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost#preOpen()} and
266    * {@link org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost#postOpen()} are such hooks.
267    *
268    * See also
269    * {@link org.apache.hadoop.hbase.master.MasterCoprocessorHost#handleCoprocessorThrowable(
270    *    CoprocessorEnvironment, Throwable)}
271    * @param env The coprocessor that threw the exception.
272    * @param e The exception that was thrown.
273    */
274   private void handleCoprocessorThrowableNoRethrow(
275       final CoprocessorEnvironment env, final Throwable e) {
276     try {
277       handleCoprocessorThrowable(env,e);
278     } catch (IOException ioe) {
279       // We cannot throw exceptions from the caller hook, so ignore.
280       LOG.warn(
281         "handleCoprocessorThrowable() threw an IOException while attempting to handle Throwable " +
282         e + ". Ignoring.",e);
283     }
284   }
285 
286   /**
287    * Invoked before a region open.
288    *
289    * @throws IOException Signals that an I/O exception has occurred.
290    */
291   public void preOpen() throws IOException {
292     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
293     for (RegionEnvironment env: coprocessors) {
294       if (env.getInstance() instanceof RegionObserver) {
295         ctx = ObserverContext.createAndPrepare(env, ctx);
296         Thread currentThread = Thread.currentThread();
297         ClassLoader cl = currentThread.getContextClassLoader();
298         try {
299           currentThread.setContextClassLoader(env.getClassLoader());
300           ((RegionObserver) env.getInstance()).preOpen(ctx);
301         } catch (Throwable e) {
302           handleCoprocessorThrowable(env, e);
303         } finally {
304           currentThread.setContextClassLoader(cl);
305         }
306         if (ctx.shouldComplete()) {
307           break;
308         }
309       }
310     }
311   }
312 
313   /**
314    * Invoked after a region open
315    */
316   public void postOpen() {
317     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
318     for (RegionEnvironment env: coprocessors) {
319       if (env.getInstance() instanceof RegionObserver) {
320         ctx = ObserverContext.createAndPrepare(env, ctx);
321         Thread currentThread = Thread.currentThread();
322         ClassLoader cl = currentThread.getContextClassLoader();
323         try {
324           currentThread.setContextClassLoader(env.getClassLoader());
325           ((RegionObserver) env.getInstance()).postOpen(ctx);
326         } catch (Throwable e) {
327           handleCoprocessorThrowableNoRethrow(env, e);
328         } finally {
329           currentThread.setContextClassLoader(cl);
330         }
331         if (ctx.shouldComplete()) {
332           break;
333         }
334       }
335     }
336   }
337 
338   /**
339    * Invoked after log replay on region
340    */
341   public void postLogReplay() {
342     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
343     for (RegionEnvironment env: coprocessors) {
344       if (env.getInstance() instanceof RegionObserver) {
345         ctx = ObserverContext.createAndPrepare(env, ctx);
346         Thread currentThread = Thread.currentThread();
347         ClassLoader cl = currentThread.getContextClassLoader();
348         try {
349           currentThread.setContextClassLoader(env.getClassLoader());
350           ((RegionObserver) env.getInstance()).postLogReplay(ctx);
351         } catch (Throwable e) {
352           handleCoprocessorThrowableNoRethrow(env, e);
353         } finally {
354           currentThread.setContextClassLoader(cl);
355         }
356         if (ctx.shouldComplete()) {
357           break;
358         }
359       }
360     }
361   }
362 
363   /**
364    * Invoked before a region is closed
365    * @param abortRequested true if the server is aborting
366    */
367   public void preClose(final boolean abortRequested) throws IOException {
368     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
369     for (RegionEnvironment env: coprocessors) {
370       if (env.getInstance() instanceof RegionObserver) {
371         ctx = ObserverContext.createAndPrepare(env, ctx);
372         Thread currentThread = Thread.currentThread();
373         ClassLoader cl = currentThread.getContextClassLoader();
374         try {
375           currentThread.setContextClassLoader(env.getClassLoader());
376           ((RegionObserver) env.getInstance()).preClose(ctx, abortRequested);
377         } catch (Throwable e) {
378           handleCoprocessorThrowable(env, e);
379         } finally {
380           currentThread.setContextClassLoader(cl);
381         }
382       }
383     }
384   }
385 
386   /**
387    * Invoked after a region is closed
388    * @param abortRequested true if the server is aborting
389    */
390   public void postClose(final boolean abortRequested) {
391     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
392     for (RegionEnvironment env: coprocessors) {
393       if (env.getInstance() instanceof RegionObserver) {
394         ctx = ObserverContext.createAndPrepare(env, ctx);
395         Thread currentThread = Thread.currentThread();
396         ClassLoader cl = currentThread.getContextClassLoader();
397         try {
398           currentThread.setContextClassLoader(env.getClassLoader());
399           ((RegionObserver) env.getInstance()).postClose(ctx, abortRequested);
400         } catch (Throwable e) {
401           handleCoprocessorThrowableNoRethrow(env, e);
402         } finally {
403           currentThread.setContextClassLoader(cl);
404         }
405       }
406       shutdown(env);
407     }
408   }
409 
410   /**
411    * See
412    * {@link RegionObserver#preCompactScannerOpen(ObserverContext, Store, List, ScanType, long, InternalScanner, CompactionRequest)}
413    */
414   public InternalScanner preCompactScannerOpen(final Store store,
415       final List<StoreFileScanner> scanners, final ScanType scanType, final long earliestPutTs,
416       final CompactionRequest request) throws IOException {
417     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
418     InternalScanner s = null;
419     for (RegionEnvironment env: coprocessors) {
420       if (env.getInstance() instanceof RegionObserver) {
421         ctx = ObserverContext.createAndPrepare(env, ctx);
422         Thread currentThread = Thread.currentThread();
423         ClassLoader cl = currentThread.getContextClassLoader();
424         try {
425           currentThread.setContextClassLoader(env.getClassLoader());
426           s = ((RegionObserver) env.getInstance()).preCompactScannerOpen(ctx, store,
427             scanners, scanType, earliestPutTs, s, request);
428         } catch (Throwable e) {
429           handleCoprocessorThrowable(env,e);
430         } finally {
431           currentThread.setContextClassLoader(cl);
432         }
433         if (ctx.shouldComplete()) {
434           break;
435         }
436       }
437     }
438     return s;
439   }
440 
441   /**
442    * Called prior to selecting the {@link StoreFile}s for compaction from the list of currently
443    * available candidates.
444    * @param store The store where compaction is being requested
445    * @param candidates The currently available store files
446    * @param request custom compaction request
447    * @return If {@code true}, skip the normal selection process and use the current list
448    * @throws IOException
449    */
450   public boolean preCompactSelection(final Store store, final List<StoreFile> candidates,
451       final CompactionRequest request) throws IOException {
452     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
453     boolean bypass = false;
454     for (RegionEnvironment env: coprocessors) {
455       if (env.getInstance() instanceof RegionObserver) {
456         ctx = ObserverContext.createAndPrepare(env, ctx);
457         Thread currentThread = Thread.currentThread();
458         ClassLoader cl = currentThread.getContextClassLoader();
459         try {
460           currentThread.setContextClassLoader(env.getClassLoader());
461           ((RegionObserver) env.getInstance()).preCompactSelection(ctx, store, candidates,
462             request);
463         } catch (Throwable e) {
464           handleCoprocessorThrowable(env,e);
465         } finally {
466           currentThread.setContextClassLoader(cl);
467         }
468         bypass |= ctx.shouldBypass();
469         if (ctx.shouldComplete()) {
470           break;
471         }
472       }
473     }
474     return bypass;
475   }
476 
477   /**
478    * Called after the {@link StoreFile}s to be compacted have been selected from the available
479    * candidates.
480    * @param store The store where compaction is being requested
481    * @param selected The store files selected to compact
482    * @param request custom compaction
483    */
484   public void postCompactSelection(final Store store, final ImmutableList<StoreFile> selected,
485       final CompactionRequest request) {
486     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
487     for (RegionEnvironment env: coprocessors) {
488       if (env.getInstance() instanceof RegionObserver) {
489         ctx = ObserverContext.createAndPrepare(env, ctx);
490         Thread currentThread = Thread.currentThread();
491         ClassLoader cl = currentThread.getContextClassLoader();
492         try {
493           currentThread.setContextClassLoader(env.getClassLoader());
494           ((RegionObserver) env.getInstance()).postCompactSelection(ctx, store, selected,
495             request);
496         } catch (Throwable e) {
497           handleCoprocessorThrowableNoRethrow(env,e);
498         } finally {
499           currentThread.setContextClassLoader(cl);
500         }
501         if (ctx.shouldComplete()) {
502           break;
503         }
504       }
505     }
506   }
507 
508   /**
509    * Called prior to rewriting the store files selected for compaction
510    * @param store the store being compacted
511    * @param scanner the scanner used to read store data during compaction
512    * @param scanType type of Scan
513    * @param request the compaction that will be executed
514    * @throws IOException
515    */
516   public InternalScanner preCompact(final Store store, final InternalScanner scanner,
517       final ScanType scanType, final CompactionRequest request) throws IOException {
518     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
519     boolean bypass = false;
520     InternalScanner s = scanner;
521     for (RegionEnvironment env: coprocessors) {
522       if (env.getInstance() instanceof RegionObserver) {
523         ctx = ObserverContext.createAndPrepare(env, ctx);
524         Thread currentThread = Thread.currentThread();
525         ClassLoader cl = currentThread.getContextClassLoader();
526         try {
527           currentThread.setContextClassLoader(env.getClassLoader());
528           s = ((RegionObserver) env.getInstance()).preCompact(ctx, store, s, scanType,
529             request);
530         } catch (Throwable e) {
531           handleCoprocessorThrowable(env,e);
532         } finally {
533           currentThread.setContextClassLoader(cl);
534         }
535         bypass |= ctx.shouldBypass();
536         if (ctx.shouldComplete()) {
537           break;
538         }
539       }
540     }
541     return bypass ? null : s;
542   }
543 
544   /**
545    * Called after the store compaction has completed.
546    * @param store the store being compacted
547    * @param resultFile the new store file written during compaction
548    * @param request the compaction that is being executed
549    * @throws IOException
550    */
551   public void postCompact(final Store store, final StoreFile resultFile,
552       final CompactionRequest request) throws IOException {
553     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
554     for (RegionEnvironment env: coprocessors) {
555       if (env.getInstance() instanceof RegionObserver) {
556         ctx = ObserverContext.createAndPrepare(env, ctx);
557         Thread currentThread = Thread.currentThread();
558         ClassLoader cl = currentThread.getContextClassLoader();
559         try {
560           currentThread.setContextClassLoader(env.getClassLoader());
561           ((RegionObserver) env.getInstance()).postCompact(ctx, store, resultFile, request);
562         } catch (Throwable e) {
563           handleCoprocessorThrowable(env, e);
564         } finally {
565           currentThread.setContextClassLoader(cl);
566         }
567         if (ctx.shouldComplete()) {
568           break;
569         }
570       }
571     }
572   }
573 
574   /**
575    * Invoked before a memstore flush
576    * @throws IOException
577    */
578   public InternalScanner preFlush(final Store store, final InternalScanner scanner) throws IOException {
579     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
580     boolean bypass = false;
581     InternalScanner s = scanner;
582     for (RegionEnvironment env: coprocessors) {
583       if (env.getInstance() instanceof RegionObserver) {
584         ctx = ObserverContext.createAndPrepare(env, ctx);
585         Thread currentThread = Thread.currentThread();
586         ClassLoader cl = currentThread.getContextClassLoader();
587         try {
588           currentThread.setContextClassLoader(env.getClassLoader());
589           s = ((RegionObserver)env.getInstance()).preFlush(ctx, store, s);
590         } catch (Throwable e) {
591           handleCoprocessorThrowable(env,e);
592         } finally {
593           currentThread.setContextClassLoader(cl);
594         }
595         bypass |= ctx.shouldBypass();
596         if (ctx.shouldComplete()) {
597           break;
598         }
599       }
600     }
601     return bypass ? null : s;
602   }
603 
604   /**
605    * Invoked before a memstore flush
606    * @throws IOException
607    */
608   public void preFlush() throws IOException {
609     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
610     for (RegionEnvironment env: coprocessors) {
611       if (env.getInstance() instanceof RegionObserver) {
612         ctx = ObserverContext.createAndPrepare(env, ctx);
613         Thread currentThread = Thread.currentThread();
614         ClassLoader cl = currentThread.getContextClassLoader();
615         try {
616           currentThread.setContextClassLoader(env.getClassLoader());
617           ((RegionObserver)env.getInstance()).preFlush(ctx);
618         } catch (Throwable e) {
619           handleCoprocessorThrowable(env, e);
620         } finally {
621           currentThread.setContextClassLoader(cl);
622         }
623         if (ctx.shouldComplete()) {
624           break;
625         }
626       }
627     }
628   }
629 
630   /**
631    * See
632    * {@link RegionObserver#preFlushScannerOpen(ObserverContext,
633    *    Store, KeyValueScanner, InternalScanner)}
634    */
635   public InternalScanner preFlushScannerOpen(final Store store,
636       final KeyValueScanner memstoreScanner) throws IOException {
637     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
638     InternalScanner s = null;
639     for (RegionEnvironment env : coprocessors) {
640       if (env.getInstance() instanceof RegionObserver) {
641         ctx = ObserverContext.createAndPrepare(env, ctx);
642         Thread currentThread = Thread.currentThread();
643         ClassLoader cl = currentThread.getContextClassLoader();
644         try {
645           currentThread.setContextClassLoader(env.getClassLoader());
646           s = ((RegionObserver) env.getInstance()).preFlushScannerOpen(ctx, store,
647             memstoreScanner, s);
648         } catch (Throwable e) {
649           handleCoprocessorThrowable(env, e);
650         } finally {
651           currentThread.setContextClassLoader(cl);
652         }
653         if (ctx.shouldComplete()) {
654           break;
655         }
656       }
657     }
658     return s;
659   }
660 
661   /**
662    * Invoked after a memstore flush
663    * @throws IOException
664    */
665   public void postFlush() throws IOException {
666     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
667     for (RegionEnvironment env: coprocessors) {
668       if (env.getInstance() instanceof RegionObserver) {
669         ctx = ObserverContext.createAndPrepare(env, ctx);
670         Thread currentThread = Thread.currentThread();
671         ClassLoader cl = currentThread.getContextClassLoader();
672         try {
673           currentThread.setContextClassLoader(env.getClassLoader());
674           ((RegionObserver)env.getInstance()).postFlush(ctx);
675         } catch (Throwable e) {
676           handleCoprocessorThrowable(env, e);
677         } finally {
678           currentThread.setContextClassLoader(cl);
679         }
680         if (ctx.shouldComplete()) {
681           break;
682         }
683       }
684     }
685   }
686 
687   /**
688    * Invoked after a memstore flush
689    * @throws IOException
690    */
691   public void postFlush(final Store store, final StoreFile storeFile) throws IOException {
692     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
693     for (RegionEnvironment env: coprocessors) {
694       if (env.getInstance() instanceof RegionObserver) {
695         ctx = ObserverContext.createAndPrepare(env, ctx);
696         Thread currentThread = Thread.currentThread();
697         ClassLoader cl = currentThread.getContextClassLoader();
698         try {
699           currentThread.setContextClassLoader(env.getClassLoader());
700           ((RegionObserver)env.getInstance()).postFlush(ctx, store, storeFile);
701         } catch (Throwable e) {
702           handleCoprocessorThrowable(env, e);
703         } finally {
704           currentThread.setContextClassLoader(cl);
705         }
706         if (ctx.shouldComplete()) {
707           break;
708         }
709       }
710     }
711   }
712 
713   /**
714    * Invoked just before a split
715    * @throws IOException
716    */
717   public void preSplit() throws IOException {
718     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
719     for (RegionEnvironment env: coprocessors) {
720       if (env.getInstance() instanceof RegionObserver) {
721         ctx = ObserverContext.createAndPrepare(env, ctx);
722         Thread currentThread = Thread.currentThread();
723         ClassLoader cl = currentThread.getContextClassLoader();
724         try {
725           currentThread.setContextClassLoader(env.getClassLoader());
726           ((RegionObserver)env.getInstance()).preSplit(ctx);
727         } catch (Throwable e) {
728           handleCoprocessorThrowable(env, e);
729         } finally {
730           currentThread.setContextClassLoader(cl);
731         }
732         if (ctx.shouldComplete()) {
733           break;
734         }
735       }
736     }
737   }
738 
739   /**
740    * Invoked just before a split
741    * @throws IOException
742    */
743   public void preSplit(final byte[] splitRow) throws IOException {
744     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
745     for (RegionEnvironment env: coprocessors) {
746       if (env.getInstance() instanceof RegionObserver) {
747         ctx = ObserverContext.createAndPrepare(env, ctx);
748         Thread currentThread = Thread.currentThread();
749         ClassLoader cl = currentThread.getContextClassLoader();
750         try {
751           currentThread.setContextClassLoader(env.getClassLoader());
752           ((RegionObserver)env.getInstance()).preSplit(ctx, splitRow);
753         } catch (Throwable e) {
754           handleCoprocessorThrowable(env, e);
755         } finally {
756           currentThread.setContextClassLoader(cl);
757         }
758         if (ctx.shouldComplete()) {
759           break;
760         }
761       }
762     }
763   }
764 
765   /**
766    * Invoked just after a split
767    * @param l the new left-hand daughter region
768    * @param r the new right-hand daughter region
769    * @throws IOException
770    */
771   public void postSplit(final HRegion l, final HRegion r) throws IOException {
772     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
773     for (RegionEnvironment env: coprocessors) {
774       if (env.getInstance() instanceof RegionObserver) {
775         ctx = ObserverContext.createAndPrepare(env, ctx);
776         Thread currentThread = Thread.currentThread();
777         ClassLoader cl = currentThread.getContextClassLoader();
778         try {
779           currentThread.setContextClassLoader(env.getClassLoader());
780           ((RegionObserver)env.getInstance()).postSplit(ctx, l, r);
781         } catch (Throwable e) {
782           handleCoprocessorThrowable(env, e);
783         } finally {
784           currentThread.setContextClassLoader(cl);
785         }
786         if (ctx.shouldComplete()) {
787           break;
788         }
789       }
790     }
791   }
792 
793   public boolean preSplitBeforePONR(final byte[] splitKey, 
794       final List<Mutation> metaEntries) throws IOException {
795     boolean bypass = false;
796     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
797     for (RegionEnvironment env : coprocessors) {
798       if (env.getInstance() instanceof RegionObserver) {
799         ctx = ObserverContext.createAndPrepare(env, ctx);
800         Thread currentThread = Thread.currentThread();
801         ClassLoader cl = currentThread.getContextClassLoader();
802         try {
803           currentThread.setContextClassLoader(env.getClassLoader());
804           ((RegionObserver) env.getInstance()).preSplitBeforePONR(ctx, splitKey, metaEntries);
805         } catch (Throwable e) {
806           handleCoprocessorThrowable(env, e);
807         } finally {
808           currentThread.setContextClassLoader(cl);
809         }
810         bypass |= ctx.shouldBypass();
811         if (ctx.shouldComplete()) {
812           break;
813         }
814       }
815     }
816     return bypass;
817   }
818 
819   public void preSplitAfterPONR() throws IOException {
820     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
821     for (RegionEnvironment env : coprocessors) {
822       if (env.getInstance() instanceof RegionObserver) {
823         ctx = ObserverContext.createAndPrepare(env, ctx);
824         Thread currentThread = Thread.currentThread();
825         ClassLoader cl = currentThread.getContextClassLoader();
826         try {
827           currentThread.setContextClassLoader(env.getClassLoader());
828           ((RegionObserver) env.getInstance()).preSplitAfterPONR(ctx);
829         } catch (Throwable e) {
830           handleCoprocessorThrowable(env, e);
831         } finally {
832           currentThread.setContextClassLoader(cl);
833         }
834         if (ctx.shouldComplete()) {
835           break;
836         }
837       }
838     }
839   }
840 
841   /**
842    * Invoked just before the rollback of a failed split is started
843    * @throws IOException
844    */
845   public void preRollBackSplit() throws IOException {
846     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
847     for (RegionEnvironment env : coprocessors) {
848       if (env.getInstance() instanceof RegionObserver) {
849         ctx = ObserverContext.createAndPrepare(env, ctx);
850         Thread currentThread = Thread.currentThread();
851         ClassLoader cl = currentThread.getContextClassLoader();
852         try {
853           currentThread.setContextClassLoader(env.getClassLoader());
854           ((RegionObserver) env.getInstance()).preRollBackSplit(ctx);
855         } catch (Throwable e) {
856           handleCoprocessorThrowable(env, e);
857         } finally {
858           currentThread.setContextClassLoader(cl);
859         }
860         if (ctx.shouldComplete()) {
861           break;
862         }
863       }
864     }
865   }
866 
867   /**
868    * Invoked just after the rollback of a failed split is done
869    * @throws IOException
870    */
871   public void postRollBackSplit() throws IOException {
872     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
873     for (RegionEnvironment env : coprocessors) {
874       if (env.getInstance() instanceof RegionObserver) {
875         ctx = ObserverContext.createAndPrepare(env, ctx);
876         Thread currentThread = Thread.currentThread();
877         ClassLoader cl = currentThread.getContextClassLoader();
878         try {
879           currentThread.setContextClassLoader(env.getClassLoader());
880           ((RegionObserver) env.getInstance()).postRollBackSplit(ctx);
881         } catch (Throwable e) {
882           handleCoprocessorThrowable(env, e);
883         } finally {
884           currentThread.setContextClassLoader(cl);
885         }
886         if (ctx.shouldComplete()) {
887           break;
888         }
889       }
890     }
891   }
892 
893   /**
894    * Invoked after a split is completed irrespective of a failure or success.
895    * @throws IOException
896    */
897   public void postCompleteSplit() throws IOException {
898     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
899     for (RegionEnvironment env : coprocessors) {
900       if (env.getInstance() instanceof RegionObserver) {
901         ctx = ObserverContext.createAndPrepare(env, ctx);
902         Thread currentThread = Thread.currentThread();
903         ClassLoader cl = currentThread.getContextClassLoader();
904         try {
905           currentThread.setContextClassLoader(env.getClassLoader());
906           ((RegionObserver) env.getInstance()).postCompleteSplit(ctx);
907         } catch (Throwable e) {
908           handleCoprocessorThrowable(env, e);
909         } finally {
910           currentThread.setContextClassLoader(cl);
911         }
912         if (ctx.shouldComplete()) {
913           break;
914         }
915       }
916     }
917   }
918 
919   // RegionObserver support
920 
921   /**
922    * @param row the row key
923    * @param family the family
924    * @param result the result set from the region
925    * @return true if default processing should be bypassed
926    * @exception IOException Exception
927    */
928   public boolean preGetClosestRowBefore(final byte[] row, final byte[] family,
929       final Result result) throws IOException {
930     boolean bypass = false;
931     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
932     for (RegionEnvironment env: coprocessors) {
933       if (env.getInstance() instanceof RegionObserver) {
934         ctx = ObserverContext.createAndPrepare(env, ctx);
935         Thread currentThread = Thread.currentThread();
936         ClassLoader cl = currentThread.getContextClassLoader();
937         try {
938           currentThread.setContextClassLoader(env.getClassLoader());
939           ((RegionObserver)env.getInstance()).preGetClosestRowBefore(ctx, row, family, result);
940         } catch (Throwable e) {
941           handleCoprocessorThrowable(env, e);
942         } finally {
943           currentThread.setContextClassLoader(cl);
944         }
945         bypass |= ctx.shouldBypass();
946         if (ctx.shouldComplete()) {
947           break;
948         }
949       }
950     }
951     return bypass;
952   }
953 
954   /**
955    * @param row the row key
956    * @param family the family
957    * @param result the result set from the region
958    * @exception IOException Exception
959    */
960   public void postGetClosestRowBefore(final byte[] row, final byte[] family,
961       final Result result) throws IOException {
962     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
963     for (RegionEnvironment env: coprocessors) {
964       if (env.getInstance() instanceof RegionObserver) {
965         ctx = ObserverContext.createAndPrepare(env, ctx);
966         Thread currentThread = Thread.currentThread();
967         ClassLoader cl = currentThread.getContextClassLoader();
968         try {
969           currentThread.setContextClassLoader(env.getClassLoader());
970           ((RegionObserver)env.getInstance()).postGetClosestRowBefore(ctx, row, family, result);
971         } catch (Throwable e) {
972           handleCoprocessorThrowable(env, e);
973         } finally {
974           currentThread.setContextClassLoader(cl);
975         }
976         if (ctx.shouldComplete()) {
977           break;
978         }
979       }
980     }
981   }
982 
983   /**
984    * @param get the Get request
985    * @return true if default processing should be bypassed
986    * @exception IOException Exception
987    */
988   public boolean preGet(final Get get, final List<Cell> results)
989       throws IOException {
990     boolean bypass = false;
991     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
992     for (RegionEnvironment env: coprocessors) {
993       if (env.getInstance() instanceof RegionObserver) {
994         ctx = ObserverContext.createAndPrepare(env, ctx);
995         Thread currentThread = Thread.currentThread();
996         ClassLoader cl = currentThread.getContextClassLoader();
997         try {
998           currentThread.setContextClassLoader(env.getClassLoader());
999           ((RegionObserver)env.getInstance()).preGetOp(ctx, get, results);
1000         } catch (Throwable e) {
1001           handleCoprocessorThrowable(env, e);
1002         } finally {
1003           currentThread.setContextClassLoader(cl);
1004         }
1005         bypass |= ctx.shouldBypass();
1006         if (ctx.shouldComplete()) {
1007           break;
1008         }
1009       }
1010     }
1011     return bypass;
1012   }
1013 
1014   /**
1015    * @param get the Get request
1016    * @param results the result sett
1017    * @exception IOException Exception
1018    */
1019   public void postGet(final Get get, final List<Cell> results)
1020   throws IOException {
1021     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1022     for (RegionEnvironment env: coprocessors) {
1023       if (env.getInstance() instanceof RegionObserver) {
1024         ctx = ObserverContext.createAndPrepare(env, ctx);
1025         Thread currentThread = Thread.currentThread();
1026         ClassLoader cl = currentThread.getContextClassLoader();
1027         try {
1028           currentThread.setContextClassLoader(env.getClassLoader());
1029           ((RegionObserver)env.getInstance()).postGetOp(ctx, get, results);
1030         } catch (Throwable e) {
1031           handleCoprocessorThrowable(env, e);
1032         } finally {
1033           currentThread.setContextClassLoader(cl);
1034         }
1035         if (ctx.shouldComplete()) {
1036           break;
1037         }
1038       }
1039     }
1040   }
1041 
1042   /**
1043    * @param get the Get request
1044    * @return true or false to return to client if bypassing normal operation,
1045    * or null otherwise
1046    * @exception IOException Exception
1047    */
1048   public Boolean preExists(final Get get) throws IOException {
1049     boolean bypass = false;
1050     boolean exists = false;
1051     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1052     for (RegionEnvironment env: coprocessors) {
1053       if (env.getInstance() instanceof RegionObserver) {
1054         ctx = ObserverContext.createAndPrepare(env, ctx);
1055         Thread currentThread = Thread.currentThread();
1056         ClassLoader cl = currentThread.getContextClassLoader();
1057         try {
1058           currentThread.setContextClassLoader(env.getClassLoader());
1059           exists = ((RegionObserver)env.getInstance()).preExists(ctx, get, exists);
1060         } catch (Throwable e) {
1061           handleCoprocessorThrowable(env, e);
1062         } finally {
1063           currentThread.setContextClassLoader(cl);
1064         }
1065         bypass |= ctx.shouldBypass();
1066         if (ctx.shouldComplete()) {
1067           break;
1068         }
1069       }
1070     }
1071     return bypass ? exists : null;
1072   }
1073 
1074   /**
1075    * @param get the Get request
1076    * @param exists the result returned by the region server
1077    * @return the result to return to the client
1078    * @exception IOException Exception
1079    */
1080   public boolean postExists(final Get get, boolean exists)
1081       throws IOException {
1082     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1083     for (RegionEnvironment env: coprocessors) {
1084       if (env.getInstance() instanceof RegionObserver) {
1085         ctx = ObserverContext.createAndPrepare(env, ctx);
1086         Thread currentThread = Thread.currentThread();
1087         ClassLoader cl = currentThread.getContextClassLoader();
1088         try {
1089           currentThread.setContextClassLoader(env.getClassLoader());
1090           exists = ((RegionObserver)env.getInstance()).postExists(ctx, get, exists);
1091         } catch (Throwable e) {
1092           handleCoprocessorThrowable(env, e);
1093         } finally {
1094           currentThread.setContextClassLoader(cl);
1095         }
1096         if (ctx.shouldComplete()) {
1097           break;
1098         }
1099       }
1100     }
1101     return exists;
1102   }
1103 
1104   /**
1105    * @param put The Put object
1106    * @param edit The WALEdit object.
1107    * @param durability The durability used
1108    * @return true if default processing should be bypassed
1109    * @exception IOException Exception
1110    */
1111   public boolean prePut(final Put put, final WALEdit edit, final Durability durability)
1112       throws IOException {
1113     boolean bypass = false;
1114     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1115     for (RegionEnvironment env: coprocessors) {
1116       if (env.getInstance() instanceof RegionObserver) {
1117         ctx = ObserverContext.createAndPrepare(env, ctx);
1118         Thread currentThread = Thread.currentThread();
1119         ClassLoader cl = currentThread.getContextClassLoader();
1120         try {
1121           currentThread.setContextClassLoader(env.getClassLoader());
1122           ((RegionObserver)env.getInstance()).prePut(ctx, put, edit, durability);
1123         } catch (Throwable e) {
1124           handleCoprocessorThrowable(env, e);
1125         } finally {
1126           currentThread.setContextClassLoader(cl);
1127         }
1128         bypass |= ctx.shouldBypass();
1129         if (ctx.shouldComplete()) {
1130           break;
1131         }
1132       }
1133     }
1134     return bypass;
1135   }
1136 
1137   /**
1138    * @param mutation - the current mutation
1139    * @param kv - the current cell
1140    * @param byteNow - current timestamp in bytes
1141    * @param get - the get that could be used
1142    * Note that the get only does not specify the family and qualifier that should be used
1143    * @return true if default processing should be bypassed
1144    * @exception IOException
1145    *              Exception
1146    */
1147   public boolean prePrepareTimeStampForDeleteVersion(Mutation mutation,
1148       Cell kv, byte[] byteNow, Get get) throws IOException {
1149     boolean bypass = false;
1150     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1151     for (RegionEnvironment env : coprocessors) {
1152       if (env.getInstance() instanceof RegionObserver) {
1153         ctx = ObserverContext.createAndPrepare(env, ctx);
1154         Thread currentThread = Thread.currentThread();
1155         ClassLoader cl = currentThread.getContextClassLoader();
1156         try {
1157           currentThread.setContextClassLoader(env.getClassLoader());
1158           ((RegionObserver) env.getInstance())
1159               .prePrepareTimeStampForDeleteVersion(ctx, mutation, kv,
1160                   byteNow, get);
1161         } catch (Throwable e) {
1162           handleCoprocessorThrowable(env, e);
1163         } finally {
1164           currentThread.setContextClassLoader(cl);
1165         }
1166         bypass |= ctx.shouldBypass();
1167         if (ctx.shouldComplete()) {
1168           break;
1169         }
1170       }
1171     }
1172     return bypass;
1173   }
1174 
1175   /**
1176    * @param put The Put object
1177    * @param edit The WALEdit object.
1178    * @param durability The durability used
1179    * @exception IOException Exception
1180    */
1181   public void postPut(final Put put, final WALEdit edit, final Durability durability)
1182       throws IOException {
1183     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1184     for (RegionEnvironment env: coprocessors) {
1185       if (env.getInstance() instanceof RegionObserver) {
1186         ctx = ObserverContext.createAndPrepare(env, ctx);
1187         Thread currentThread = Thread.currentThread();
1188         ClassLoader cl = currentThread.getContextClassLoader();
1189         try {
1190           currentThread.setContextClassLoader(env.getClassLoader());
1191           ((RegionObserver)env.getInstance()).postPut(ctx, put, edit, durability);
1192         } catch (Throwable e) {
1193           handleCoprocessorThrowable(env, e);
1194         } finally {
1195           currentThread.setContextClassLoader(cl);
1196         }
1197         if (ctx.shouldComplete()) {
1198           break;
1199         }
1200       }
1201     }
1202   }
1203 
1204   /**
1205    * @param delete The Delete object
1206    * @param edit The WALEdit object.
1207    * @param durability The durability used
1208    * @return true if default processing should be bypassed
1209    * @exception IOException Exception
1210    */
1211   public boolean preDelete(final Delete delete, final WALEdit edit, final Durability durability)
1212       throws IOException {
1213     boolean bypass = false;
1214     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1215     for (RegionEnvironment env: coprocessors) {
1216       if (env.getInstance() instanceof RegionObserver) {
1217         ctx = ObserverContext.createAndPrepare(env, ctx);
1218         Thread currentThread = Thread.currentThread();
1219         ClassLoader cl = currentThread.getContextClassLoader();
1220         try {
1221           currentThread.setContextClassLoader(env.getClassLoader());
1222           ((RegionObserver)env.getInstance()).preDelete(ctx, delete, edit, durability);
1223         } catch (Throwable e) {
1224           handleCoprocessorThrowable(env, e);
1225         } finally {
1226           currentThread.setContextClassLoader(cl);
1227         }
1228         bypass |= ctx.shouldBypass();
1229         if (ctx.shouldComplete()) {
1230           break;
1231         }
1232       }
1233     }
1234     return bypass;
1235   }
1236 
1237   /**
1238    * @param delete The Delete object
1239    * @param edit The WALEdit object.
1240    * @param durability The durability used
1241    * @exception IOException Exception
1242    */
1243   public void postDelete(final Delete delete, final WALEdit edit, final Durability durability)
1244       throws IOException {
1245     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1246     for (RegionEnvironment env: coprocessors) {
1247       if (env.getInstance() instanceof RegionObserver) {
1248         ctx = ObserverContext.createAndPrepare(env, ctx);
1249         Thread currentThread = Thread.currentThread();
1250         ClassLoader cl = currentThread.getContextClassLoader();
1251         try {
1252           currentThread.setContextClassLoader(env.getClassLoader());
1253           ((RegionObserver)env.getInstance()).postDelete(ctx, delete, edit, durability);
1254         } catch (Throwable e) {
1255           handleCoprocessorThrowable(env, e);
1256         } finally {
1257           currentThread.setContextClassLoader(cl);
1258         }
1259         if (ctx.shouldComplete()) {
1260           break;
1261         }
1262       }
1263     }
1264   }
1265   
1266   /**
1267    * @param miniBatchOp
1268    * @return true if default processing should be bypassed
1269    * @throws IOException
1270    */
1271   public boolean preBatchMutate(
1272       final MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
1273     boolean bypass = false;
1274     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1275     for (RegionEnvironment env : coprocessors) {
1276       if (env.getInstance() instanceof RegionObserver) {
1277         ctx = ObserverContext.createAndPrepare(env, ctx);
1278         Thread currentThread = Thread.currentThread();
1279         ClassLoader cl = currentThread.getContextClassLoader();
1280         try {
1281           currentThread.setContextClassLoader(env.getClassLoader());
1282           ((RegionObserver) env.getInstance()).preBatchMutate(ctx, miniBatchOp);
1283         } catch (Throwable e) {
1284           handleCoprocessorThrowable(env, e);
1285         } finally {
1286           currentThread.setContextClassLoader(cl);
1287         }
1288         bypass |= ctx.shouldBypass();
1289         if (ctx.shouldComplete()) {
1290           break;
1291         }
1292       }
1293     }
1294     return bypass;
1295   }
1296 
1297   /**
1298    * @param miniBatchOp
1299    * @throws IOException
1300    */
1301   public void postBatchMutate(
1302       final MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
1303     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1304     for (RegionEnvironment env : coprocessors) {
1305       if (env.getInstance() instanceof RegionObserver) {
1306         ctx = ObserverContext.createAndPrepare(env, ctx);
1307         Thread currentThread = Thread.currentThread();
1308         ClassLoader cl = currentThread.getContextClassLoader();
1309         try {
1310           currentThread.setContextClassLoader(env.getClassLoader());
1311           ((RegionObserver) env.getInstance()).postBatchMutate(ctx, miniBatchOp);
1312         } catch (Throwable e) {
1313           handleCoprocessorThrowable(env, e);
1314         } finally {
1315           currentThread.setContextClassLoader(cl);
1316         }
1317         if (ctx.shouldComplete()) {
1318           break;
1319         }
1320       }
1321     }
1322   }
1323 
1324   public void postBatchMutateIndispensably(
1325       final MiniBatchOperationInProgress<Mutation> miniBatchOp, final boolean success)
1326       throws IOException {
1327     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1328     for (RegionEnvironment env : coprocessors) {
1329       if (env.getInstance() instanceof RegionObserver) {
1330         ctx = ObserverContext.createAndPrepare(env, ctx);
1331         Thread currentThread = Thread.currentThread();
1332         ClassLoader cl = currentThread.getContextClassLoader();
1333         try {
1334           currentThread.setContextClassLoader(env.getClassLoader());
1335           ((RegionObserver) env.getInstance()).postBatchMutateIndispensably(ctx, miniBatchOp,
1336             success);
1337         } catch (Throwable e) {
1338           handleCoprocessorThrowable(env, e);
1339         } finally {
1340           currentThread.setContextClassLoader(cl);
1341         }
1342         if (ctx.shouldComplete()) {
1343           break;
1344         }
1345       }
1346     }
1347   }
1348 
1349   /**
1350    * @param row row to check
1351    * @param family column family
1352    * @param qualifier column qualifier
1353    * @param compareOp the comparison operation
1354    * @param comparator the comparator
1355    * @param put data to put if check succeeds
1356    * @return true or false to return to client if default processing should
1357    * be bypassed, or null otherwise
1358    * @throws IOException e
1359    */
1360   public Boolean preCheckAndPut(final byte [] row, final byte [] family,
1361       final byte [] qualifier, final CompareOp compareOp,
1362       final ByteArrayComparable comparator, final Put put)
1363     throws IOException {
1364     boolean bypass = false;
1365     boolean result = false;
1366     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1367     for (RegionEnvironment env: coprocessors) {
1368       if (env.getInstance() instanceof RegionObserver) {
1369         ctx = ObserverContext.createAndPrepare(env, ctx);
1370         Thread currentThread = Thread.currentThread();
1371         ClassLoader cl = currentThread.getContextClassLoader();
1372         try {
1373           currentThread.setContextClassLoader(env.getClassLoader());
1374           result = ((RegionObserver)env.getInstance()).preCheckAndPut(ctx, row, family, qualifier,
1375             compareOp, comparator, put, result);
1376         } catch (Throwable e) {
1377           handleCoprocessorThrowable(env, e);
1378         } finally {
1379           currentThread.setContextClassLoader(cl);
1380         }
1381         bypass |= ctx.shouldBypass();
1382         if (ctx.shouldComplete()) {
1383           break;
1384         }
1385       }
1386     }
1387     return bypass ? result : null;
1388   }
1389 
1390   /**
1391    * @param row row to check
1392    * @param family column family
1393    * @param qualifier column qualifier
1394    * @param compareOp the comparison operation
1395    * @param comparator the comparator
1396    * @param put data to put if check succeeds
1397    * @return true or false to return to client if default processing should
1398    * be bypassed, or null otherwise
1399    * @throws IOException e
1400    */
1401   public Boolean preCheckAndPutAfterRowLock(final byte[] row, final byte[] family,
1402       final byte[] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator,
1403       final Put put) throws IOException {
1404     boolean bypass = false;
1405     boolean result = false;
1406     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1407     for (RegionEnvironment env : coprocessors) {
1408       if (env.getInstance() instanceof RegionObserver) {
1409         ctx = ObserverContext.createAndPrepare(env, ctx);
1410         Thread currentThread = Thread.currentThread();
1411         ClassLoader cl = currentThread.getContextClassLoader();
1412         try {
1413           currentThread.setContextClassLoader(env.getClassLoader());
1414           result = ((RegionObserver) env.getInstance()).preCheckAndPutAfterRowLock(ctx, row,
1415               family, qualifier, compareOp, comparator, put, result);
1416         } catch (Throwable e) {
1417           handleCoprocessorThrowable(env, e);
1418         } finally {
1419           currentThread.setContextClassLoader(cl);
1420         }
1421         bypass |= ctx.shouldBypass();
1422         if (ctx.shouldComplete()) {
1423           break;
1424         }
1425       }
1426     }
1427     return bypass ? result : null;
1428   }
1429 
1430   /**
1431    * @param row row to check
1432    * @param family column family
1433    * @param qualifier column qualifier
1434    * @param compareOp the comparison operation
1435    * @param comparator the comparator
1436    * @param put data to put if check succeeds
1437    * @throws IOException e
1438    */
1439   public boolean postCheckAndPut(final byte [] row, final byte [] family,
1440       final byte [] qualifier, final CompareOp compareOp,
1441       final ByteArrayComparable comparator, final Put put,
1442       boolean result)
1443     throws IOException {
1444     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1445     for (RegionEnvironment env: coprocessors) {
1446       if (env.getInstance() instanceof RegionObserver) {
1447         ctx = ObserverContext.createAndPrepare(env, ctx);
1448         Thread currentThread = Thread.currentThread();
1449         ClassLoader cl = currentThread.getContextClassLoader();
1450         try {
1451           currentThread.setContextClassLoader(env.getClassLoader());
1452           result = ((RegionObserver)env.getInstance()).postCheckAndPut(ctx, row, family,
1453             qualifier, compareOp, comparator, put, result);
1454         } catch (Throwable e) {
1455           handleCoprocessorThrowable(env, e);
1456         } finally {
1457           currentThread.setContextClassLoader(cl);
1458         }
1459         if (ctx.shouldComplete()) {
1460           break;
1461         }
1462       }
1463     }
1464     return result;
1465   }
1466 
1467   /**
1468    * @param row row to check
1469    * @param family column family
1470    * @param qualifier column qualifier
1471    * @param compareOp the comparison operation
1472    * @param comparator the comparator
1473    * @param delete delete to commit if check succeeds
1474    * @return true or false to return to client if default processing should
1475    * be bypassed, or null otherwise
1476    * @throws IOException e
1477    */
1478   public Boolean preCheckAndDelete(final byte [] row, final byte [] family,
1479       final byte [] qualifier, final CompareOp compareOp,
1480       final ByteArrayComparable comparator, final Delete delete)
1481       throws IOException {
1482     boolean bypass = false;
1483     boolean result = false;
1484     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1485     for (RegionEnvironment env: coprocessors) {
1486       if (env.getInstance() instanceof RegionObserver) {
1487         ctx = ObserverContext.createAndPrepare(env, ctx);
1488         Thread currentThread = Thread.currentThread();
1489         ClassLoader cl = currentThread.getContextClassLoader();
1490         try {
1491           currentThread.setContextClassLoader(env.getClassLoader());
1492           result = ((RegionObserver)env.getInstance()).preCheckAndDelete(ctx, row, family,
1493             qualifier, compareOp, comparator, delete, result);
1494         } catch (Throwable e) {
1495           handleCoprocessorThrowable(env, e);
1496         } finally {
1497           currentThread.setContextClassLoader(cl);
1498         }
1499         bypass |= ctx.shouldBypass();
1500         if (ctx.shouldComplete()) {
1501           break;
1502         }
1503       }
1504     }
1505     return bypass ? result : null;
1506   }
1507 
1508   /**
1509    * @param row row to check
1510    * @param family column family
1511    * @param qualifier column qualifier
1512    * @param compareOp the comparison operation
1513    * @param comparator the comparator
1514    * @param delete delete to commit if check succeeds
1515    * @return true or false to return to client if default processing should
1516    * be bypassed, or null otherwise
1517    * @throws IOException e
1518    */
1519   public Boolean preCheckAndDeleteAfterRowLock(final byte[] row, final byte[] family,
1520       final byte[] qualifier, final CompareOp compareOp, final ByteArrayComparable comparator,
1521       final Delete delete) throws IOException {
1522     boolean bypass = false;
1523     boolean result = false;
1524     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1525     for (RegionEnvironment env : coprocessors) {
1526       if (env.getInstance() instanceof RegionObserver) {
1527         ctx = ObserverContext.createAndPrepare(env, ctx);
1528         Thread currentThread = Thread.currentThread();
1529         ClassLoader cl = currentThread.getContextClassLoader();
1530         try {
1531           currentThread.setContextClassLoader(env.getClassLoader());
1532           result = ((RegionObserver) env.getInstance()).preCheckAndDeleteAfterRowLock(ctx, row,
1533               family, qualifier, compareOp, comparator, delete, result);
1534         } catch (Throwable e) {
1535           handleCoprocessorThrowable(env, e);
1536         } finally {
1537           currentThread.setContextClassLoader(cl);
1538         }
1539         bypass |= ctx.shouldBypass();
1540         if (ctx.shouldComplete()) {
1541           break;
1542         }
1543       }
1544     }
1545     return bypass ? result : null;
1546   }
1547 
1548   /**
1549    * @param row row to check
1550    * @param family column family
1551    * @param qualifier column qualifier
1552    * @param compareOp the comparison operation
1553    * @param comparator the comparator
1554    * @param delete delete to commit if check succeeds
1555    * @throws IOException e
1556    */
1557   public boolean postCheckAndDelete(final byte [] row, final byte [] family,
1558       final byte [] qualifier, final CompareOp compareOp,
1559       final ByteArrayComparable comparator, final Delete delete,
1560       boolean result) throws IOException {
1561     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1562     for (RegionEnvironment env: coprocessors) {
1563       if (env.getInstance() instanceof RegionObserver) {
1564         ctx = ObserverContext.createAndPrepare(env, ctx);
1565         Thread currentThread = Thread.currentThread();
1566         ClassLoader cl = currentThread.getContextClassLoader();
1567         try {
1568           currentThread.setContextClassLoader(env.getClassLoader());
1569           result = ((RegionObserver)env.getInstance()).postCheckAndDelete(ctx, row, family,
1570             qualifier, compareOp, comparator, delete, result);
1571         } catch (Throwable e) {
1572           handleCoprocessorThrowable(env, e);
1573         } finally {
1574           currentThread.setContextClassLoader(cl);
1575         }
1576         if (ctx.shouldComplete()) {
1577           break;
1578         }
1579       }
1580     }
1581     return result;
1582   }
1583 
1584   /**
1585    * @param append append object
1586    * @return result to return to client if default operation should be
1587    * bypassed, null otherwise
1588    * @throws IOException if an error occurred on the coprocessor
1589    */
1590   public Result preAppend(final Append append) throws IOException {
1591     boolean bypass = false;
1592     Result result = null;
1593     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1594     for (RegionEnvironment env: coprocessors) {
1595       if (env.getInstance() instanceof RegionObserver) {
1596         ctx = ObserverContext.createAndPrepare(env, ctx);
1597         Thread currentThread = Thread.currentThread();
1598         ClassLoader cl = currentThread.getContextClassLoader();
1599         try {
1600           currentThread.setContextClassLoader(env.getClassLoader());
1601           result = ((RegionObserver)env.getInstance()).preAppend(ctx, append);
1602         } catch (Throwable e) {
1603           handleCoprocessorThrowable(env, e);
1604         } finally {
1605           currentThread.setContextClassLoader(cl);
1606         }
1607         bypass |= ctx.shouldBypass();
1608         if (ctx.shouldComplete()) {
1609           break;
1610         }
1611       }
1612     }
1613     return bypass ? result : null;
1614   }
1615 
1616   /**
1617    * @param append append object
1618    * @return result to return to client if default operation should be
1619    * bypassed, null otherwise
1620    * @throws IOException if an error occurred on the coprocessor
1621    */
1622   public Result preAppendAfterRowLock(final Append append) throws IOException {
1623     boolean bypass = false;
1624     Result result = null;
1625     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1626     for (RegionEnvironment env : coprocessors) {
1627       if (env.getInstance() instanceof RegionObserver) {
1628         ctx = ObserverContext.createAndPrepare(env, ctx);
1629         Thread currentThread = Thread.currentThread();
1630         ClassLoader cl = currentThread.getContextClassLoader();
1631         try {
1632           currentThread.setContextClassLoader(env.getClassLoader());
1633           result = ((RegionObserver) env.getInstance()).preAppendAfterRowLock(ctx, append);
1634         } catch (Throwable e) {
1635           handleCoprocessorThrowable(env, e);
1636         } finally {
1637           currentThread.setContextClassLoader(cl);
1638         }
1639         bypass |= ctx.shouldBypass();
1640         if (ctx.shouldComplete()) {
1641           break;
1642         }
1643       }
1644     }
1645     return bypass ? result : null;
1646   }
1647 
1648   /**
1649    * @param increment increment object
1650    * @return result to return to client if default operation should be
1651    * bypassed, null otherwise
1652    * @throws IOException if an error occurred on the coprocessor
1653    */
1654   public Result preIncrement(final Increment increment) throws IOException {
1655     boolean bypass = false;
1656     Result result = null;
1657     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1658     for (RegionEnvironment env: coprocessors) {
1659       if (env.getInstance() instanceof RegionObserver) {
1660         ctx = ObserverContext.createAndPrepare(env, ctx);
1661         Thread currentThread = Thread.currentThread();
1662         ClassLoader cl = currentThread.getContextClassLoader();
1663         try {
1664           currentThread.setContextClassLoader(env.getClassLoader());
1665           result = ((RegionObserver)env.getInstance()).preIncrement(ctx, increment);
1666         } catch (Throwable e) {
1667           handleCoprocessorThrowable(env, e);
1668         } finally {
1669           currentThread.setContextClassLoader(cl);
1670         }
1671         bypass |= ctx.shouldBypass();
1672         if (ctx.shouldComplete()) {
1673           break;
1674         }
1675       }
1676     }
1677     return bypass ? result : null;
1678   }
1679 
1680   /**
1681    * @param increment increment object
1682    * @return result to return to client if default operation should be
1683    * bypassed, null otherwise
1684    * @throws IOException if an error occurred on the coprocessor
1685    */
1686   public Result preIncrementAfterRowLock(final Increment increment) throws IOException {
1687     boolean bypass = false;
1688     Result result = null;
1689     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1690     for (RegionEnvironment env : coprocessors) {
1691       if (env.getInstance() instanceof RegionObserver) {
1692         ctx = ObserverContext.createAndPrepare(env, ctx);
1693         Thread currentThread = Thread.currentThread();
1694         ClassLoader cl = currentThread.getContextClassLoader();
1695         try {
1696           currentThread.setContextClassLoader(env.getClassLoader());
1697           result = ((RegionObserver) env.getInstance()).preIncrementAfterRowLock(ctx, increment);
1698         } catch (Throwable e) {
1699           handleCoprocessorThrowable(env, e);
1700         } finally {
1701           currentThread.setContextClassLoader(cl);
1702         }
1703         bypass |= ctx.shouldBypass();
1704         if (ctx.shouldComplete()) {
1705           break;
1706         }
1707       }
1708     }
1709     return bypass ? result : null;
1710   }
1711 
1712   /**
1713    * @param append Append object
1714    * @param result the result returned by the append
1715    * @throws IOException if an error occurred on the coprocessor
1716    */
1717   public void postAppend(final Append append, final Result result) throws IOException {
1718     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1719     for (RegionEnvironment env: coprocessors) {
1720       if (env.getInstance() instanceof RegionObserver) {
1721         ctx = ObserverContext.createAndPrepare(env, ctx);
1722         Thread currentThread = Thread.currentThread();
1723         ClassLoader cl = currentThread.getContextClassLoader();
1724         try {
1725           currentThread.setContextClassLoader(env.getClassLoader());
1726           ((RegionObserver)env.getInstance()).postAppend(ctx, append, result);
1727         } catch (Throwable e) {
1728           handleCoprocessorThrowable(env, e);
1729         } finally {
1730           currentThread.setContextClassLoader(cl);
1731         }
1732         if (ctx.shouldComplete()) {
1733           break;
1734         }
1735       }
1736     }
1737   }
1738 
1739   /**
1740    * @param increment increment object
1741    * @param result the result returned by postIncrement
1742    * @throws IOException if an error occurred on the coprocessor
1743    */
1744   public Result postIncrement(final Increment increment, Result result) throws IOException {
1745     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1746     for (RegionEnvironment env: coprocessors) {
1747       if (env.getInstance() instanceof RegionObserver) {
1748         ctx = ObserverContext.createAndPrepare(env, ctx);
1749         Thread currentThread = Thread.currentThread();
1750         ClassLoader cl = currentThread.getContextClassLoader();
1751         try {
1752           currentThread.setContextClassLoader(env.getClassLoader());
1753           result = ((RegionObserver)env.getInstance()).postIncrement(ctx, increment, result);
1754         } catch (Throwable e) {
1755           handleCoprocessorThrowable(env, e);
1756         } finally {
1757           currentThread.setContextClassLoader(cl);
1758         }
1759         if (ctx.shouldComplete()) {
1760           break;
1761         }
1762       }
1763     }
1764     return result;
1765   }
1766 
1767   /**
1768    * @param scan the Scan specification
1769    * @return scanner id to return to client if default operation should be
1770    * bypassed, false otherwise
1771    * @exception IOException Exception
1772    */
1773   public RegionScanner preScannerOpen(final Scan scan) throws IOException {
1774     boolean bypass = false;
1775     RegionScanner s = null;
1776     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1777     for (RegionEnvironment env: coprocessors) {
1778       if (env.getInstance() instanceof RegionObserver) {
1779         ctx = ObserverContext.createAndPrepare(env, ctx);
1780         Thread currentThread = Thread.currentThread();
1781         ClassLoader cl = currentThread.getContextClassLoader();
1782         try {
1783           currentThread.setContextClassLoader(env.getClassLoader());
1784           s = ((RegionObserver)env.getInstance()).preScannerOpen(ctx, scan, s);
1785         } catch (Throwable e) {
1786           handleCoprocessorThrowable(env, e);
1787         } finally {
1788           currentThread.setContextClassLoader(cl);
1789         }
1790         bypass |= ctx.shouldBypass();
1791         if (ctx.shouldComplete()) {
1792           break;
1793         }
1794       }
1795     }
1796     return bypass ? s : null;
1797   }
1798 
1799   /**
1800    * See
1801    * {@link RegionObserver#preStoreScannerOpen(ObserverContext,
1802    *    Store, Scan, NavigableSet, KeyValueScanner)}
1803    */
1804   public KeyValueScanner preStoreScannerOpen(final Store store, final Scan scan,
1805       final NavigableSet<byte[]> targetCols) throws IOException {
1806     KeyValueScanner s = null;
1807     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1808     for (RegionEnvironment env: coprocessors) {
1809       if (env.getInstance() instanceof RegionObserver) {
1810         ctx = ObserverContext.createAndPrepare(env, ctx);
1811         Thread currentThread = Thread.currentThread();
1812         ClassLoader cl = currentThread.getContextClassLoader();
1813         try {
1814           currentThread.setContextClassLoader(env.getClassLoader());
1815           s = ((RegionObserver) env.getInstance()).preStoreScannerOpen(ctx, store, scan,
1816             targetCols, s);
1817         } catch (Throwable e) {
1818           handleCoprocessorThrowable(env, e);
1819         } finally {
1820           currentThread.setContextClassLoader(cl);
1821         }
1822         if (ctx.shouldComplete()) {
1823           break;
1824         }
1825       }
1826     }
1827     return s;
1828   }
1829 
1830   /**
1831    * @param scan the Scan specification
1832    * @param s the scanner
1833    * @return the scanner instance to use
1834    * @exception IOException Exception
1835    */
1836   public RegionScanner postScannerOpen(final Scan scan, RegionScanner s) throws IOException {
1837     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1838     for (RegionEnvironment env: coprocessors) {
1839       if (env.getInstance() instanceof RegionObserver) {
1840         ctx = ObserverContext.createAndPrepare(env, ctx);
1841         Thread currentThread = Thread.currentThread();
1842         ClassLoader cl = currentThread.getContextClassLoader();
1843         try {
1844           currentThread.setContextClassLoader(env.getClassLoader());
1845           s = ((RegionObserver)env.getInstance()).postScannerOpen(ctx, scan, s);
1846         } catch (Throwable e) {
1847           handleCoprocessorThrowable(env, e);
1848         } finally {
1849           currentThread.setContextClassLoader(cl);
1850         }
1851         if (ctx.shouldComplete()) {
1852           break;
1853         }
1854       }
1855     }
1856     return s;
1857   }
1858 
1859   /**
1860    * @param s the scanner
1861    * @param results the result set returned by the region server
1862    * @param limit the maximum number of results to return
1863    * @return 'has next' indication to client if bypassing default behavior, or
1864    * null otherwise
1865    * @exception IOException Exception
1866    */
1867   public Boolean preScannerNext(final InternalScanner s,
1868       final List<Result> results, final int limit) throws IOException {
1869     boolean bypass = false;
1870     boolean hasNext = false;
1871     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1872     for (RegionEnvironment env: coprocessors) {
1873       if (env.getInstance() instanceof RegionObserver) {
1874         ctx = ObserverContext.createAndPrepare(env, ctx);
1875         Thread currentThread = Thread.currentThread();
1876         ClassLoader cl = currentThread.getContextClassLoader();
1877         try {
1878           currentThread.setContextClassLoader(env.getClassLoader());
1879           hasNext = ((RegionObserver)env.getInstance()).preScannerNext(ctx, s, results, limit,
1880             hasNext);
1881         } catch (Throwable e) {
1882           handleCoprocessorThrowable(env, e);
1883         } finally {
1884           currentThread.setContextClassLoader(cl);
1885         }
1886         bypass |= ctx.shouldBypass();
1887         if (ctx.shouldComplete()) {
1888           break;
1889         }
1890       }
1891     }
1892     return bypass ? hasNext : null;
1893   }
1894 
1895   /**
1896    * @param s the scanner
1897    * @param results the result set returned by the region server
1898    * @param limit the maximum number of results to return
1899    * @param hasMore
1900    * @return 'has more' indication to give to client
1901    * @exception IOException Exception
1902    */
1903   public boolean postScannerNext(final InternalScanner s,
1904       final List<Result> results, final int limit, boolean hasMore)
1905       throws IOException {
1906     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1907     for (RegionEnvironment env: coprocessors) {
1908       if (env.getInstance() instanceof RegionObserver) {
1909         ctx = ObserverContext.createAndPrepare(env, ctx);
1910         Thread currentThread = Thread.currentThread();
1911         ClassLoader cl = currentThread.getContextClassLoader();
1912         try {
1913           currentThread.setContextClassLoader(env.getClassLoader());
1914           hasMore = ((RegionObserver)env.getInstance()).postScannerNext(ctx, s, results, limit,
1915             hasMore);
1916         } catch (Throwable e) {
1917           handleCoprocessorThrowable(env, e);
1918         } finally {
1919           currentThread.setContextClassLoader(cl);
1920         }
1921         if (ctx.shouldComplete()) {
1922           break;
1923         }
1924       }
1925     }
1926     return hasMore;
1927   }
1928 
1929   /**
1930    * This will be called by the scan flow when the current scanned row is being filtered out by the
1931    * filter.
1932    * @param s the scanner
1933    * @param currentRow The current rowkey which got filtered out
1934    * @param offset offset to rowkey
1935    * @param length length of rowkey
1936    * @return whether more rows are available for the scanner or not
1937    * @throws IOException
1938    */
1939   public boolean postScannerFilterRow(final InternalScanner s, final byte[] currentRow,
1940       final int offset, final short length) throws IOException {
1941     boolean hasMore = true; // By default assume more rows there.
1942     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1943     for (RegionEnvironment env : coprocessors) {
1944       if (env.getInstance() instanceof RegionObserver) {
1945         ctx = ObserverContext.createAndPrepare(env, ctx);
1946         Thread currentThread = Thread.currentThread();
1947         ClassLoader cl = currentThread.getContextClassLoader();
1948         try {
1949           currentThread.setContextClassLoader(env.getClassLoader());
1950           hasMore = ((RegionObserver) env.getInstance()).postScannerFilterRow(ctx, s, currentRow,
1951             offset, length, hasMore);
1952         } catch (Throwable e) {
1953           handleCoprocessorThrowable(env, e);
1954         } finally {
1955           currentThread.setContextClassLoader(cl);
1956         }
1957         if (ctx.shouldComplete()) {
1958           break;
1959         }
1960       }
1961     }
1962     return hasMore;
1963   }
1964   
1965   /**
1966    * @param s the scanner
1967    * @return true if default behavior should be bypassed, false otherwise
1968    * @exception IOException Exception
1969    */
1970   public boolean preScannerClose(final InternalScanner s) throws IOException {
1971     boolean bypass = false;
1972     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1973     for (RegionEnvironment env: coprocessors) {
1974       if (env.getInstance() instanceof RegionObserver) {
1975         ctx = ObserverContext.createAndPrepare(env, ctx);
1976         Thread currentThread = Thread.currentThread();
1977         ClassLoader cl = currentThread.getContextClassLoader();
1978         try {
1979           currentThread.setContextClassLoader(env.getClassLoader());
1980           ((RegionObserver)env.getInstance()).preScannerClose(ctx, s);
1981         } catch (Throwable e) {
1982           handleCoprocessorThrowable(env, e);
1983         } finally {
1984           currentThread.setContextClassLoader(cl);
1985         }
1986         bypass |= ctx.shouldBypass();
1987         if (ctx.shouldComplete()) {
1988           break;
1989         }
1990       }
1991     }
1992     return bypass;
1993   }
1994 
1995   /**
1996    * @param s the scanner
1997    * @exception IOException Exception
1998    */
1999   public void postScannerClose(final InternalScanner s) throws IOException {
2000     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2001     for (RegionEnvironment env: coprocessors) {
2002       if (env.getInstance() instanceof RegionObserver) {
2003         ctx = ObserverContext.createAndPrepare(env, ctx);
2004         Thread currentThread = Thread.currentThread();
2005         ClassLoader cl = currentThread.getContextClassLoader();
2006         try {
2007           currentThread.setContextClassLoader(env.getClassLoader());
2008           ((RegionObserver)env.getInstance()).postScannerClose(ctx, s);
2009         } catch (Throwable e) {
2010           handleCoprocessorThrowable(env, e);
2011         } finally {
2012           currentThread.setContextClassLoader(cl);
2013         }
2014         if (ctx.shouldComplete()) {
2015           break;
2016         }
2017       }
2018     }
2019   }
2020 
2021   /**
2022    * @param info
2023    * @param logKey
2024    * @param logEdit
2025    * @return true if default behavior should be bypassed, false otherwise
2026    * @throws IOException
2027    */
2028   public boolean preWALRestore(final HRegionInfo info, final HLogKey logKey,
2029       final WALEdit logEdit) throws IOException {
2030     boolean bypass = false;
2031     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2032     for (RegionEnvironment env: coprocessors) {
2033       if (env.getInstance() instanceof RegionObserver) {
2034         ctx = ObserverContext.createAndPrepare(env, ctx);
2035         Thread currentThread = Thread.currentThread();
2036         ClassLoader cl = currentThread.getContextClassLoader();
2037         try {
2038           currentThread.setContextClassLoader(env.getClassLoader());
2039           ((RegionObserver)env.getInstance()).preWALRestore(ctx, info, logKey, logEdit);
2040         } catch (Throwable e) {
2041           handleCoprocessorThrowable(env, e);
2042         } finally {
2043           currentThread.setContextClassLoader(cl);
2044         }
2045         bypass |= ctx.shouldBypass();
2046         if (ctx.shouldComplete()) {
2047           break;
2048         }
2049       }
2050     }
2051     return bypass;
2052   }
2053 
2054   /**
2055    * @param info
2056    * @param logKey
2057    * @param logEdit
2058    * @throws IOException
2059    */
2060   public void postWALRestore(final HRegionInfo info, final HLogKey logKey, final WALEdit logEdit)
2061       throws IOException {
2062     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2063     for (RegionEnvironment env: coprocessors) {
2064       if (env.getInstance() instanceof RegionObserver) {
2065         ctx = ObserverContext.createAndPrepare(env, ctx);
2066         Thread currentThread = Thread.currentThread();
2067         ClassLoader cl = currentThread.getContextClassLoader();
2068         try {
2069           currentThread.setContextClassLoader(env.getClassLoader());
2070           ((RegionObserver)env.getInstance()).postWALRestore(ctx, info, logKey, logEdit);
2071         } catch (Throwable e) {
2072           handleCoprocessorThrowable(env, e);
2073         } finally {
2074           currentThread.setContextClassLoader(cl);
2075         }
2076         if (ctx.shouldComplete()) {
2077           break;
2078         }
2079       }
2080     }
2081   }
2082 
2083   /**
2084    * @param familyPaths pairs of { CF, file path } submitted for bulk load
2085    * @return true if the default operation should be bypassed
2086    * @throws IOException
2087    */
2088   public boolean preBulkLoadHFile(final List<Pair<byte[], String>> familyPaths) throws IOException {
2089     boolean bypass = false;
2090     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2091     for (RegionEnvironment env: coprocessors) {
2092       if (env.getInstance() instanceof RegionObserver) {
2093         ctx = ObserverContext.createAndPrepare(env, ctx);
2094         Thread currentThread = Thread.currentThread();
2095         ClassLoader cl = currentThread.getContextClassLoader();
2096         try {
2097           currentThread.setContextClassLoader(env.getClassLoader());
2098           ((RegionObserver)env.getInstance()).preBulkLoadHFile(ctx, familyPaths);
2099         } catch (Throwable e) {
2100           handleCoprocessorThrowable(env, e);
2101         } finally {
2102           currentThread.setContextClassLoader(cl);
2103         }
2104         bypass |= ctx.shouldBypass();
2105         if (ctx.shouldComplete()) {
2106           break;
2107         }
2108       }
2109     }
2110     return bypass;
2111   }
2112 
2113   /**
2114    * @param familyPaths pairs of { CF, file path } submitted for bulk load
2115    * @param hasLoaded whether load was successful or not
2116    * @return the possibly modified value of hasLoaded
2117    * @throws IOException
2118    */
2119   public boolean postBulkLoadHFile(final List<Pair<byte[], String>> familyPaths,
2120       boolean hasLoaded) throws IOException {
2121     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2122     for (RegionEnvironment env: coprocessors) {
2123       if (env.getInstance() instanceof RegionObserver) {
2124         ctx = ObserverContext.createAndPrepare(env, ctx);
2125         Thread currentThread = Thread.currentThread();
2126         ClassLoader cl = currentThread.getContextClassLoader();
2127         try {
2128           currentThread.setContextClassLoader(env.getClassLoader());
2129           hasLoaded = ((RegionObserver)env.getInstance()).postBulkLoadHFile(ctx, familyPaths,
2130             hasLoaded);
2131         } catch (Throwable e) {
2132           handleCoprocessorThrowable(env, e);
2133         } finally {
2134           currentThread.setContextClassLoader(cl);
2135         }
2136         if (ctx.shouldComplete()) {
2137           break;
2138         }
2139       }
2140     }
2141     return hasLoaded;
2142   }
2143 
2144   public void postStartRegionOperation(final Operation op) throws IOException {
2145     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2146     for (RegionEnvironment env : coprocessors) {
2147       if (env.getInstance() instanceof RegionObserver) {
2148         ctx = ObserverContext.createAndPrepare(env, ctx);
2149         Thread currentThread = Thread.currentThread();
2150         ClassLoader cl = currentThread.getContextClassLoader();
2151         try {
2152           currentThread.setContextClassLoader(env.getClassLoader());
2153           ((RegionObserver) env.getInstance()).postStartRegionOperation(ctx, op);
2154         } catch (Throwable e) {
2155           handleCoprocessorThrowable(env, e);
2156         } finally {
2157           currentThread.setContextClassLoader(cl);
2158         }
2159         if (ctx.shouldComplete()) {
2160           break;
2161         }
2162       }
2163     }
2164   }
2165 
2166   public void postCloseRegionOperation(final Operation op) throws IOException {
2167     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2168     for (RegionEnvironment env : coprocessors) {
2169       if (env.getInstance() instanceof RegionObserver) {
2170         ctx = ObserverContext.createAndPrepare(env, ctx);
2171         Thread currentThread = Thread.currentThread();
2172         ClassLoader cl = currentThread.getContextClassLoader();
2173         try {
2174           currentThread.setContextClassLoader(env.getClassLoader());
2175           ((RegionObserver) env.getInstance()).postCloseRegionOperation(ctx, op);
2176         } catch (Throwable e) {
2177           handleCoprocessorThrowable(env, e);
2178         } finally {
2179           currentThread.setContextClassLoader(cl);
2180         }
2181         if (ctx.shouldComplete()) {
2182           break;
2183         }
2184       }
2185     }
2186   }
2187 
2188   /**
2189    * @param fs fileystem to read from
2190    * @param p path to the file
2191    * @param in {@link FSDataInputStreamWrapper}
2192    * @param size Full size of the file
2193    * @param cacheConf
2194    * @param r original reference file. This will be not null only when reading a split file.
2195    * @return a Reader instance to use instead of the base reader if overriding
2196    * default behavior, null otherwise
2197    * @throws IOException
2198    */
2199   public StoreFile.Reader preStoreFileReaderOpen(final FileSystem fs, final Path p,
2200       final FSDataInputStreamWrapper in, final long size, final CacheConfig cacheConf,
2201       final Reference r) throws IOException {
2202     StoreFile.Reader reader = null;
2203     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2204     for (RegionEnvironment env : coprocessors) {
2205       if (env.getInstance() instanceof RegionObserver) {
2206         ctx = ObserverContext.createAndPrepare(env, ctx);
2207         Thread currentThread = Thread.currentThread();
2208         ClassLoader cl = currentThread.getContextClassLoader();
2209         try {
2210           currentThread.setContextClassLoader(env.getClassLoader());
2211           reader = ((RegionObserver) env.getInstance()).preStoreFileReaderOpen(ctx, fs, p, in,
2212             size, cacheConf, r, reader);
2213         } catch (Throwable e) {
2214           handleCoprocessorThrowable(env, e);
2215         } finally {
2216           currentThread.setContextClassLoader(cl);
2217         }
2218         if (ctx.shouldComplete()) {
2219           break;
2220         }
2221       }
2222     }
2223     return reader;
2224   }
2225 
2226   /**
2227    * @param fs fileystem to read from
2228    * @param p path to the file
2229    * @param in {@link FSDataInputStreamWrapper}
2230    * @param size Full size of the file
2231    * @param cacheConf
2232    * @param r original reference file. This will be not null only when reading a split file.
2233    * @param reader the base reader instance
2234    * @return The reader to use
2235    * @throws IOException
2236    */
2237   public StoreFile.Reader postStoreFileReaderOpen(final FileSystem fs, final Path p,
2238       final FSDataInputStreamWrapper in, final long size, final CacheConfig cacheConf,
2239       final Reference r, StoreFile.Reader reader) throws IOException {
2240     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2241     for (RegionEnvironment env : coprocessors) {
2242       if (env.getInstance() instanceof RegionObserver) {
2243         ctx = ObserverContext.createAndPrepare(env, ctx);
2244         Thread currentThread = Thread.currentThread();
2245         ClassLoader cl = currentThread.getContextClassLoader();
2246         try {
2247           currentThread.setContextClassLoader(env.getClassLoader());
2248           reader = ((RegionObserver) env.getInstance()).postStoreFileReaderOpen(ctx, fs, p, in,
2249             size, cacheConf, r, reader);
2250         } catch (Throwable e) {
2251           handleCoprocessorThrowable(env, e);
2252         } finally {
2253           currentThread.setContextClassLoader(cl);
2254         }
2255         if (ctx.shouldComplete()) {
2256           break;
2257         }
2258       }
2259     }
2260     return reader;
2261   }
2262 
2263   public Cell postMutationBeforeWAL(final MutationType opType, final Mutation mutation,
2264       final Cell oldCell, Cell newCell) throws IOException {
2265     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2266     for (RegionEnvironment env : coprocessors) {
2267       if (env.getInstance() instanceof RegionObserver) {
2268         ctx = ObserverContext.createAndPrepare(env, ctx);
2269         Thread currentThread = Thread.currentThread();
2270         ClassLoader cl = currentThread.getContextClassLoader();
2271         try {
2272           currentThread.setContextClassLoader(env.getClassLoader());
2273           newCell = ((RegionObserver) env.getInstance()).postMutationBeforeWAL(ctx, opType,
2274             mutation, oldCell, newCell);
2275         } catch (Throwable e) {
2276           handleCoprocessorThrowable(env, e);
2277         } finally {
2278           currentThread.setContextClassLoader(cl);
2279         }
2280         if (ctx.shouldComplete()) {
2281           break;
2282         }
2283       }
2284     }
2285     return newCell;
2286   }
2287 
2288   public Message preEndpointInvocation(final Service service, final String methodName,
2289       Message request) throws IOException {
2290     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2291     for (RegionEnvironment env : coprocessors) {
2292       if (env.getInstance() instanceof EndpointObserver) {
2293         ctx = ObserverContext.createAndPrepare(env, ctx);
2294         Thread currentThread = Thread.currentThread();
2295         ClassLoader cl = currentThread.getContextClassLoader();
2296         try {
2297           currentThread.setContextClassLoader(env.getClassLoader());
2298           request = ((EndpointObserver) env.getInstance()).preEndpointInvocation(ctx, service,
2299             methodName, request);
2300         } catch (Throwable e) {
2301           handleCoprocessorThrowable(env, e);
2302         } finally {
2303           currentThread.setContextClassLoader(cl);
2304         }
2305         if (ctx.shouldComplete()) {
2306           break;
2307         }
2308       }
2309     }
2310     return request;
2311   }
2312 
2313   public void postEndpointInvocation(final Service service, final String methodName,
2314       final Message request, final Message.Builder responseBuilder) throws IOException {
2315     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2316     for (RegionEnvironment env : coprocessors) {
2317       if (env.getInstance() instanceof EndpointObserver) {
2318         ctx = ObserverContext.createAndPrepare(env, ctx);
2319         Thread currentThread = Thread.currentThread();
2320         ClassLoader cl = currentThread.getContextClassLoader();
2321         try {
2322           currentThread.setContextClassLoader(env.getClassLoader());
2323           ((EndpointObserver) env.getInstance()).postEndpointInvocation(ctx, service,
2324             methodName, request, responseBuilder);
2325         } catch (Throwable e) {
2326           handleCoprocessorThrowable(env, e);
2327         } finally {
2328           currentThread.setContextClassLoader(cl);
2329         }
2330         if (ctx.shouldComplete()) {
2331           break;
2332         }
2333       }
2334     }
2335   }
2336 
2337   public DeleteTracker postInstantiateDeleteTracker(DeleteTracker tracker) throws IOException {
2338     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
2339     for (RegionEnvironment env : coprocessors) {
2340       if (env.getInstance() instanceof RegionObserver) {
2341         ctx = ObserverContext.createAndPrepare(env, ctx);
2342         Thread currentThread = Thread.currentThread();
2343         ClassLoader cl = currentThread.getContextClassLoader();
2344         try {
2345           currentThread.setContextClassLoader(env.getClassLoader());
2346           tracker = ((RegionObserver) env.getInstance()).postInstantiateDeleteTracker(ctx, 
2347               tracker);
2348         } catch (Throwable e) {
2349           handleCoprocessorThrowable(env, e);
2350         } finally {
2351           currentThread.setContextClassLoader(cl);
2352         }
2353         if (ctx.shouldComplete()) {
2354           break;
2355         }
2356       }
2357     }
2358     return tracker;
2359   }
2360 }