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.Path;
37  import org.apache.hadoop.hbase.Cell;
38  import org.apache.hadoop.hbase.Coprocessor;
39  import org.apache.hadoop.hbase.CoprocessorEnvironment;
40  import org.apache.hadoop.hbase.HBaseConfiguration;
41  import org.apache.hadoop.hbase.HConstants;
42  import org.apache.hadoop.hbase.HRegionInfo;
43  import org.apache.hadoop.hbase.client.Append;
44  import org.apache.hadoop.hbase.client.Delete;
45  import org.apache.hadoop.hbase.client.Durability;
46  import org.apache.hadoop.hbase.client.Get;
47  import org.apache.hadoop.hbase.client.Increment;
48  import org.apache.hadoop.hbase.client.Mutation;
49  import org.apache.hadoop.hbase.client.Put;
50  import org.apache.hadoop.hbase.client.Result;
51  import org.apache.hadoop.hbase.client.Scan;
52  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
53  import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
54  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
55  import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
56  import org.apache.hadoop.hbase.coprocessor.RegionObserver;
57  import org.apache.hadoop.hbase.filter.ByteArrayComparable;
58  import org.apache.hadoop.hbase.filter.CompareFilter.CompareOp;
59  import org.apache.hadoop.hbase.io.ImmutableBytesWritable;
60  import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
61  import org.apache.hadoop.hbase.regionserver.wal.HLogKey;
62  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
63  import org.apache.hadoop.hbase.util.Bytes;
64  import org.apache.hadoop.hbase.util.Pair;
65  import org.apache.hadoop.util.StringUtils;
66  
67  import com.google.common.collect.ImmutableList;
68  
69  /**
70   * Implements the coprocessor environment and runtime support for coprocessors
71   * loaded within a {@link HRegion}.
72   */
73  public class RegionCoprocessorHost
74      extends CoprocessorHost<RegionCoprocessorHost.RegionEnvironment> {
75  
76    private static final Log LOG = LogFactory.getLog(RegionCoprocessorHost.class);
77    // The shared data map
78    private static ReferenceMap sharedDataMap =
79        new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK);
80  
81    /**
82     * Encapsulation of the environment of each coprocessor
83     */
84    static class RegionEnvironment extends CoprocessorHost.Environment
85        implements RegionCoprocessorEnvironment {
86  
87      private HRegion region;
88      private RegionServerServices rsServices;
89      ConcurrentMap<String, Object> sharedData;
90  
91      /**
92       * Constructor
93       * @param impl the coprocessor instance
94       * @param priority chaining priority
95       */
96      public RegionEnvironment(final Coprocessor impl, final int priority,
97          final int seq, final Configuration conf, final HRegion region,
98          final RegionServerServices services, final ConcurrentMap<String, Object> sharedData) {
99        super(impl, priority, seq, conf);
100       this.region = region;
101       this.rsServices = services;
102       this.sharedData = sharedData;
103     }
104 
105     /** @return the region */
106     @Override
107     public HRegion getRegion() {
108       return region;
109     }
110 
111     /** @return reference to the region server services */
112     @Override
113     public RegionServerServices getRegionServerServices() {
114       return rsServices;
115     }
116 
117     public void shutdown() {
118       super.shutdown();
119     }
120 
121     @Override
122     public ConcurrentMap<String, Object> getSharedData() {
123       return sharedData;
124     }
125   }
126 
127   /** The region server services */
128   RegionServerServices rsServices;
129   /** The region */
130   HRegion region;
131 
132   /**
133    * Constructor
134    * @param region the region
135    * @param rsServices interface to available region server functionality
136    * @param conf the configuration
137    */
138   public RegionCoprocessorHost(final HRegion region,
139       final RegionServerServices rsServices, final Configuration conf) {
140     this.conf = conf;
141     this.rsServices = rsServices;
142     this.region = region;
143     this.pathPrefix = Integer.toString(this.region.getRegionInfo().hashCode());
144 
145     // load system default cp's from configuration.
146     loadSystemCoprocessors(conf, REGION_COPROCESSOR_CONF_KEY);
147 
148     // load system default cp's for user tables from configuration.
149     if (!region.getRegionInfo().getTable().isSystemTable()) {
150       loadSystemCoprocessors(conf, USER_REGION_COPROCESSOR_CONF_KEY);
151     }
152 
153     // load Coprocessor From HDFS
154     loadTableCoprocessors(conf);
155   }
156 
157   void loadTableCoprocessors(final Configuration conf) {
158     // scan the table attributes for coprocessor load specifications
159     // initialize the coprocessors
160     List<RegionEnvironment> configured = new ArrayList<RegionEnvironment>();
161     for (Map.Entry<ImmutableBytesWritable,ImmutableBytesWritable> e:
162         region.getTableDesc().getValues().entrySet()) {
163       String key = Bytes.toString(e.getKey().get()).trim();
164       String spec = Bytes.toString(e.getValue().get()).trim();
165       if (HConstants.CP_HTD_ATTR_KEY_PATTERN.matcher(key).matches()) {
166         // found one
167         try {
168           Matcher matcher = HConstants.CP_HTD_ATTR_VALUE_PATTERN.matcher(spec);
169           if (matcher.matches()) {
170             // jar file path can be empty if the cp class can be loaded
171             // from class loader.
172             Path path = matcher.group(1).trim().isEmpty() ?
173                 null : new Path(matcher.group(1).trim());
174             String className = matcher.group(2).trim();
175             int priority = matcher.group(3).trim().isEmpty() ?
176                 Coprocessor.PRIORITY_USER : Integer.valueOf(matcher.group(3));
177             String cfgSpec = null;
178             try {
179               cfgSpec = matcher.group(4);
180             } catch (IndexOutOfBoundsException ex) {
181               // ignore
182             }
183             if (cfgSpec != null) {
184               cfgSpec = cfgSpec.substring(cfgSpec.indexOf('|') + 1);
185               // do an explicit deep copy of the passed configuration
186               Configuration newConf = new Configuration(false);
187               HBaseConfiguration.merge(newConf, conf);
188               Matcher m = HConstants.CP_HTD_ATTR_VALUE_PARAM_PATTERN.matcher(cfgSpec);
189               while (m.find()) {
190                 newConf.set(m.group(1), m.group(2));
191               }
192               configured.add(load(path, className, priority, newConf));
193             } else {
194               configured.add(load(path, className, priority, conf));
195             }
196             LOG.info("Load coprocessor " + className + " from HTD of " +
197               region.getTableDesc().getTableName().getNameAsString() +
198                 " successfully.");
199           } else {
200             throw new RuntimeException("specification does not match pattern");
201           }
202         } catch (Exception ex) {
203           LOG.warn("attribute '" + key +
204             "' has invalid coprocessor specification '" + spec + "'");
205           LOG.warn(StringUtils.stringifyException(ex));
206         }
207       }
208     }
209     // add together to coprocessor set for COW efficiency
210     coprocessors.addAll(configured);
211   }
212 
213   @Override
214   public RegionEnvironment createEnvironment(Class<?> implClass,
215       Coprocessor instance, int priority, int seq, Configuration conf) {
216     // Check if it's an Endpoint.
217     // Due to current dynamic protocol design, Endpoint
218     // uses a different way to be registered and executed.
219     // It uses a visitor pattern to invoke registered Endpoint
220     // method.
221     for (Class c : implClass.getInterfaces()) {
222       if (CoprocessorService.class.isAssignableFrom(c)) {
223         region.registerService( ((CoprocessorService)instance).getService() );
224       }
225     }
226     ConcurrentMap<String, Object> classData;
227     // make sure only one thread can add maps
228     synchronized (sharedDataMap) {
229       // as long as at least one RegionEnvironment holds on to its classData it will
230       // remain in this map
231       classData = (ConcurrentMap<String, Object>)sharedDataMap.get(implClass.getName());
232       if (classData == null) {
233         classData = new ConcurrentHashMap<String, Object>();
234         sharedDataMap.put(implClass.getName(), classData);
235       }
236     }
237     return new RegionEnvironment(instance, priority, seq, conf, region,
238         rsServices, classData);
239   }
240 
241   @Override
242   protected void abortServer(final CoprocessorEnvironment env, final Throwable e) {
243     abortServer("regionserver", rsServices, env, e);
244   }
245 
246   /**
247    * HBASE-4014 : This is used by coprocessor hooks which are not declared to throw exceptions.
248    *
249    * For example, {@link
250    * org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost#preOpen()} and
251    * {@link org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost#postOpen()} are such hooks.
252    *
253    * See also
254    * {@link org.apache.hadoop.hbase.master.MasterCoprocessorHost#handleCoprocessorThrowable(
255    *    CoprocessorEnvironment, Throwable)}
256    * @param env The coprocessor that threw the exception.
257    * @param e The exception that was thrown.
258    */
259   private void handleCoprocessorThrowableNoRethrow(
260       final CoprocessorEnvironment env, final Throwable e) {
261     try {
262       handleCoprocessorThrowable(env,e);
263     } catch (IOException ioe) {
264       // We cannot throw exceptions from the caller hook, so ignore.
265       LOG.warn(
266         "handleCoprocessorThrowable() threw an IOException while attempting to handle Throwable " +
267         e + ". Ignoring.",e);
268     }
269   }
270 
271   /**
272    * Invoked before a region open.
273    *
274    * @throws IOException Signals that an I/O exception has occurred.
275    */
276   public void preOpen() throws IOException {
277     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
278     for (RegionEnvironment env: coprocessors) {
279       if (env.getInstance() instanceof RegionObserver) {
280         ctx = ObserverContext.createAndPrepare(env, ctx);
281          try {
282           ((RegionObserver) env.getInstance()).preOpen(ctx);
283          } catch (Throwable e) {
284            handleCoprocessorThrowable(env, e);
285          }
286         if (ctx.shouldComplete()) {
287           break;
288         }
289       }
290     }
291   }
292 
293   /**
294    * Invoked after a region open
295    */
296   public void postOpen() {
297     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
298     for (RegionEnvironment env: coprocessors) {
299       if (env.getInstance() instanceof RegionObserver) {
300         ctx = ObserverContext.createAndPrepare(env, ctx);
301         try {
302           ((RegionObserver) env.getInstance()).postOpen(ctx);
303         } catch (Throwable e) {
304           handleCoprocessorThrowableNoRethrow(env, e);
305         }
306         if (ctx.shouldComplete()) {
307           break;
308         }
309       }
310     }
311   }
312 
313   /**
314    * Invoked after log replay on region
315    */
316   public void postLogReplay() {
317     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
318     for (RegionEnvironment env: coprocessors) {
319       if (env.getInstance() instanceof RegionObserver) {
320         ctx = ObserverContext.createAndPrepare(env, ctx);
321         try {
322           ((RegionObserver) env.getInstance()).postLogReplay(ctx);
323         } catch (Throwable e) {
324           handleCoprocessorThrowableNoRethrow(env, e);
325         }
326         if (ctx.shouldComplete()) {
327           break;
328         }
329       }
330     }
331   }
332 
333   /**
334    * Invoked before a region is closed
335    * @param abortRequested true if the server is aborting
336    */
337   public void preClose(boolean abortRequested) throws IOException {
338     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
339     for (RegionEnvironment env: coprocessors) {
340       if (env.getInstance() instanceof RegionObserver) {
341         ctx = ObserverContext.createAndPrepare(env, ctx);
342         try {
343           ((RegionObserver) env.getInstance()).preClose(ctx, abortRequested);
344         } catch (Throwable e) {
345           handleCoprocessorThrowable(env, e);
346         }
347       }
348     }
349   }
350 
351   /**
352    * Invoked after a region is closed
353    * @param abortRequested true if the server is aborting
354    */
355   public void postClose(boolean abortRequested) {
356     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
357     for (RegionEnvironment env: coprocessors) {
358       if (env.getInstance() instanceof RegionObserver) {
359         ctx = ObserverContext.createAndPrepare(env, ctx);
360         try {
361           ((RegionObserver) env.getInstance()).postClose(ctx, abortRequested);
362         } catch (Throwable e) {
363           handleCoprocessorThrowableNoRethrow(env, e);
364         }
365 
366       }
367       shutdown(env);
368     }
369   }
370 
371   /**
372    * See
373    * {@link RegionObserver#preCompactScannerOpen(ObserverContext, Store, List, ScanType, long, InternalScanner, CompactionRequest)}
374    */
375   public InternalScanner preCompactScannerOpen(Store store, List<StoreFileScanner> scanners,
376       ScanType scanType, long earliestPutTs, CompactionRequest request) throws IOException {
377     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
378     InternalScanner s = null;
379     for (RegionEnvironment env: coprocessors) {
380       if (env.getInstance() instanceof RegionObserver) {
381         ctx = ObserverContext.createAndPrepare(env, ctx);
382         try {
383           s = ((RegionObserver) env.getInstance()).preCompactScannerOpen(ctx, store, scanners,
384             scanType, earliestPutTs, s, request);
385         } catch (Throwable e) {
386           handleCoprocessorThrowable(env,e);
387         }
388         if (ctx.shouldComplete()) {
389           break;
390         }
391       }
392     }
393     return s;
394   }
395 
396   /**
397    * Called prior to selecting the {@link StoreFile}s for compaction from the list of currently
398    * available candidates.
399    * @param store The store where compaction is being requested
400    * @param candidates The currently available store files
401    * @param request custom compaction request
402    * @return If {@code true}, skip the normal selection process and use the current list
403    * @throws IOException
404    */
405   public boolean preCompactSelection(Store store, List<StoreFile> candidates,
406       CompactionRequest request) throws IOException {
407     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
408     boolean bypass = false;
409     for (RegionEnvironment env: coprocessors) {
410       if (env.getInstance() instanceof RegionObserver) {
411         ctx = ObserverContext.createAndPrepare(env, ctx);
412         try {
413           ((RegionObserver) env.getInstance()).preCompactSelection(ctx, store, candidates, request);
414         } catch (Throwable e) {
415           handleCoprocessorThrowable(env,e);
416 
417         }
418         bypass |= ctx.shouldBypass();
419         if (ctx.shouldComplete()) {
420           break;
421         }
422       }
423     }
424     return bypass;
425   }
426 
427   /**
428    * Called after the {@link StoreFile}s to be compacted have been selected from the available
429    * candidates.
430    * @param store The store where compaction is being requested
431    * @param selected The store files selected to compact
432    * @param request custom compaction
433    */
434   public void postCompactSelection(Store store, ImmutableList<StoreFile> selected,
435       CompactionRequest request) {
436     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
437     for (RegionEnvironment env: coprocessors) {
438       if (env.getInstance() instanceof RegionObserver) {
439         ctx = ObserverContext.createAndPrepare(env, ctx);
440         try {
441           ((RegionObserver) env.getInstance()).postCompactSelection(ctx, store, selected, request);
442         } catch (Throwable e) {
443           handleCoprocessorThrowableNoRethrow(env,e);
444         }
445         if (ctx.shouldComplete()) {
446           break;
447         }
448       }
449     }
450   }
451 
452   /**
453    * Called prior to rewriting the store files selected for compaction
454    * @param store the store being compacted
455    * @param scanner the scanner used to read store data during compaction
456    * @param scanType type of Scan
457    * @param request the compaction that will be executed
458    * @throws IOException
459    */
460   public InternalScanner preCompact(Store store, InternalScanner scanner, ScanType scanType,
461       CompactionRequest request) throws IOException {
462     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
463     boolean bypass = false;
464     for (RegionEnvironment env: coprocessors) {
465       if (env.getInstance() instanceof RegionObserver) {
466         ctx = ObserverContext.createAndPrepare(env, ctx);
467         try {
468           scanner = ((RegionObserver) env.getInstance()).preCompact(ctx, store, scanner, scanType,
469             request);
470         } catch (Throwable e) {
471           handleCoprocessorThrowable(env,e);
472         }
473         bypass |= ctx.shouldBypass();
474         if (ctx.shouldComplete()) {
475           break;
476         }
477       }
478     }
479     return bypass ? null : scanner;
480   }
481 
482   /**
483    * Called after the store compaction has completed.
484    * @param store the store being compacted
485    * @param resultFile the new store file written during compaction
486    * @param request the compaction that is being executed
487    * @throws IOException
488    */
489   public void postCompact(Store store, StoreFile resultFile, CompactionRequest request)
490       throws IOException {
491     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
492     for (RegionEnvironment env: coprocessors) {
493       if (env.getInstance() instanceof RegionObserver) {
494         ctx = ObserverContext.createAndPrepare(env, ctx);
495         try {
496           ((RegionObserver) env.getInstance()).postCompact(ctx, store, resultFile, request);
497         } catch (Throwable e) {
498           handleCoprocessorThrowable(env, e);
499         }
500         if (ctx.shouldComplete()) {
501           break;
502         }
503       }
504     }
505   }
506 
507   /**
508    * Invoked before a memstore flush
509    * @throws IOException
510    */
511   public InternalScanner preFlush(Store store, InternalScanner scanner) throws IOException {
512     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
513     boolean bypass = false;
514     for (RegionEnvironment env: coprocessors) {
515       if (env.getInstance() instanceof RegionObserver) {
516         ctx = ObserverContext.createAndPrepare(env, ctx);
517         try {
518           scanner = ((RegionObserver)env.getInstance()).preFlush(
519               ctx, store, scanner);
520         } catch (Throwable e) {
521           handleCoprocessorThrowable(env,e);
522         }
523         bypass |= ctx.shouldBypass();
524         if (ctx.shouldComplete()) {
525           break;
526         }
527       }
528     }
529     return bypass ? null : scanner;
530   }
531 
532   /**
533    * Invoked before a memstore flush
534    * @throws IOException
535    */
536   public void preFlush() throws IOException {
537     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
538     for (RegionEnvironment env: coprocessors) {
539       if (env.getInstance() instanceof RegionObserver) {
540         ctx = ObserverContext.createAndPrepare(env, ctx);
541         try {
542           ((RegionObserver)env.getInstance()).preFlush(ctx);
543         } catch (Throwable e) {
544           handleCoprocessorThrowable(env, e);
545         }
546         if (ctx.shouldComplete()) {
547           break;
548         }
549       }
550     }
551   }
552 
553   /**
554    * See
555    * {@link RegionObserver#preFlushScannerOpen(ObserverContext,
556    *    Store, KeyValueScanner, InternalScanner)}
557    */
558   public InternalScanner preFlushScannerOpen(Store store, KeyValueScanner memstoreScanner)
559       throws IOException {
560     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
561     InternalScanner s = null;
562     for (RegionEnvironment env : coprocessors) {
563       if (env.getInstance() instanceof RegionObserver) {
564         ctx = ObserverContext.createAndPrepare(env, ctx);
565         try {
566           s = ((RegionObserver) env.getInstance())
567             .preFlushScannerOpen(ctx, store, memstoreScanner, s);
568         } catch (Throwable e) {
569           handleCoprocessorThrowable(env, e);
570         }
571         if (ctx.shouldComplete()) {
572           break;
573         }
574       }
575     }
576     return s;
577   }
578 
579   /**
580    * Invoked after a memstore flush
581    * @throws IOException
582    */
583   public void postFlush() throws IOException {
584     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
585     for (RegionEnvironment env: coprocessors) {
586       if (env.getInstance() instanceof RegionObserver) {
587         ctx = ObserverContext.createAndPrepare(env, ctx);
588         try {
589           ((RegionObserver)env.getInstance()).postFlush(ctx);
590         } catch (Throwable e) {
591           handleCoprocessorThrowable(env, e);
592         }
593         if (ctx.shouldComplete()) {
594           break;
595         }
596       }
597     }
598   }
599 
600   /**
601    * Invoked after a memstore flush
602    * @throws IOException
603    */
604   public void postFlush(final Store store, final StoreFile storeFile) throws IOException {
605     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
606     for (RegionEnvironment env: coprocessors) {
607       if (env.getInstance() instanceof RegionObserver) {
608         ctx = ObserverContext.createAndPrepare(env, ctx);
609         try {
610           ((RegionObserver)env.getInstance()).postFlush(ctx, store, storeFile);
611         } catch (Throwable e) {
612           handleCoprocessorThrowable(env, e);
613         }
614         if (ctx.shouldComplete()) {
615           break;
616         }
617       }
618     }
619   }
620 
621   /**
622    * Invoked just before a split
623    * @throws IOException
624    */
625   public void preSplit() throws IOException {
626     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
627     for (RegionEnvironment env: coprocessors) {
628       if (env.getInstance() instanceof RegionObserver) {
629         ctx = ObserverContext.createAndPrepare(env, ctx);
630         try {
631           ((RegionObserver)env.getInstance()).preSplit(ctx);
632         } catch (Throwable e) {
633           handleCoprocessorThrowable(env, e);
634         }
635         if (ctx.shouldComplete()) {
636           break;
637         }
638       }
639     }
640   }
641 
642   /**
643    * Invoked just before a split
644    * @throws IOException
645    */
646   public void preSplit(byte[] splitRow) throws IOException {
647     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
648     for (RegionEnvironment env: coprocessors) {
649       if (env.getInstance() instanceof RegionObserver) {
650         ctx = ObserverContext.createAndPrepare(env, ctx);
651         try {
652           ((RegionObserver)env.getInstance()).preSplit(ctx, splitRow);
653         } catch (Throwable e) {
654           handleCoprocessorThrowable(env, e);
655         }
656         if (ctx.shouldComplete()) {
657           break;
658         }
659       }
660     }
661   }
662 
663   /**
664    * Invoked just after a split
665    * @param l the new left-hand daughter region
666    * @param r the new right-hand daughter region
667    * @throws IOException
668    */
669   public void postSplit(HRegion l, HRegion r) throws IOException {
670     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
671     for (RegionEnvironment env: coprocessors) {
672       if (env.getInstance() instanceof RegionObserver) {
673         ctx = ObserverContext.createAndPrepare(env, ctx);
674         try {
675           ((RegionObserver)env.getInstance()).postSplit(ctx, l, r);
676         } catch (Throwable e) {
677           handleCoprocessorThrowable(env, e);
678         }
679         if (ctx.shouldComplete()) {
680           break;
681         }
682       }
683     }
684   }
685 
686   /**
687    * Invoked just before the rollback of a failed split is started
688    * @throws IOException
689    */
690   public void preRollBackSplit() throws IOException {
691     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
692     for (RegionEnvironment env : coprocessors) {
693       if (env.getInstance() instanceof RegionObserver) {
694         ctx = ObserverContext.createAndPrepare(env, ctx);
695         try {
696           ((RegionObserver) env.getInstance()).preRollBackSplit(ctx);
697         } catch (Throwable e) {
698           handleCoprocessorThrowable(env, e);
699         }
700         if (ctx.shouldComplete()) {
701           break;
702         }
703       }
704     }
705   }
706 
707   /**
708    * Invoked just after the rollback of a failed split is done
709    * @throws IOException
710    */
711   public void postRollBackSplit() throws IOException {
712     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
713     for (RegionEnvironment env : coprocessors) {
714       if (env.getInstance() instanceof RegionObserver) {
715         ctx = ObserverContext.createAndPrepare(env, ctx);
716         try {
717           ((RegionObserver) env.getInstance()).postRollBackSplit(ctx);
718         } catch (Throwable e) {
719           handleCoprocessorThrowable(env, e);
720         }
721         if (ctx.shouldComplete()) {
722           break;
723         }
724       }
725     }
726   }
727 
728   /**
729    * Invoked after a split is completed irrespective of a failure or success.
730    * @throws IOException
731    */
732   public void postCompleteSplit() throws IOException {
733     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
734     for (RegionEnvironment env : coprocessors) {
735       if (env.getInstance() instanceof RegionObserver) {
736         ctx = ObserverContext.createAndPrepare(env, ctx);
737         try {
738           ((RegionObserver) env.getInstance()).postCompleteSplit(ctx);
739         } catch (Throwable e) {
740           handleCoprocessorThrowable(env, e);
741         }
742         if (ctx.shouldComplete()) {
743           break;
744         }
745       }
746     }
747   }
748   // RegionObserver support
749 
750   /**
751    * @param row the row key
752    * @param family the family
753    * @param result the result set from the region
754    * @return true if default processing should be bypassed
755    * @exception IOException Exception
756    */
757   public boolean preGetClosestRowBefore(final byte[] row, final byte[] family,
758       final Result result) throws IOException {
759     boolean bypass = false;
760     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
761     for (RegionEnvironment env: coprocessors) {
762       if (env.getInstance() instanceof RegionObserver) {
763         ctx = ObserverContext.createAndPrepare(env, ctx);
764         try {
765           ((RegionObserver)env.getInstance()).preGetClosestRowBefore(ctx, row,
766               family, result);
767         } catch (Throwable e) {
768           handleCoprocessorThrowable(env, e);
769         }
770         bypass |= ctx.shouldBypass();
771         if (ctx.shouldComplete()) {
772           break;
773         }
774       }
775     }
776     return bypass;
777   }
778 
779   /**
780    * @param row the row key
781    * @param family the family
782    * @param result the result set from the region
783    * @exception IOException Exception
784    */
785   public void postGetClosestRowBefore(final byte[] row, final byte[] family,
786       final Result result) throws IOException {
787     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
788     for (RegionEnvironment env: coprocessors) {
789       if (env.getInstance() instanceof RegionObserver) {
790         ctx = ObserverContext.createAndPrepare(env, ctx);
791         try {
792           ((RegionObserver)env.getInstance()).postGetClosestRowBefore(ctx, row,
793               family, result);
794         } catch (Throwable e) {
795           handleCoprocessorThrowable(env, e);
796         }
797         if (ctx.shouldComplete()) {
798           break;
799         }
800       }
801     }
802   }
803 
804   /**
805    * @param get the Get request
806    * @return true if default processing should be bypassed
807    * @exception IOException Exception
808    */
809   public boolean preGet(final Get get, final List<Cell> results)
810       throws IOException {
811     boolean bypass = false;
812     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
813     for (RegionEnvironment env: coprocessors) {
814       if (env.getInstance() instanceof RegionObserver) {
815         ctx = ObserverContext.createAndPrepare(env, ctx);
816         try {
817           ((RegionObserver)env.getInstance()).preGetOp(ctx, get, results);
818         } catch (Throwable e) {
819           handleCoprocessorThrowable(env, e);
820         }
821         bypass |= ctx.shouldBypass();
822         if (ctx.shouldComplete()) {
823           break;
824         }
825       }
826     }
827     return bypass;
828   }
829 
830   /**
831    * @param get the Get request
832    * @param results the result sett
833    * @exception IOException Exception
834    */
835   public void postGet(final Get get, final List<Cell> results)
836   throws IOException {
837     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
838     for (RegionEnvironment env: coprocessors) {
839       if (env.getInstance() instanceof RegionObserver) {
840         ctx = ObserverContext.createAndPrepare(env, ctx);
841         try {
842           ((RegionObserver)env.getInstance()).postGetOp(ctx, get, results);
843         } catch (Throwable e) {
844           handleCoprocessorThrowable(env, e);
845         }
846         if (ctx.shouldComplete()) {
847           break;
848         }
849       }
850     }
851   }
852 
853   /**
854    * @param get the Get request
855    * @return true or false to return to client if bypassing normal operation,
856    * or null otherwise
857    * @exception IOException Exception
858    */
859   public Boolean preExists(final Get get) throws IOException {
860     boolean bypass = false;
861     boolean exists = false;
862     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
863     for (RegionEnvironment env: coprocessors) {
864       if (env.getInstance() instanceof RegionObserver) {
865         ctx = ObserverContext.createAndPrepare(env, ctx);
866         try {
867           exists = ((RegionObserver)env.getInstance()).preExists(ctx, get, exists);
868         } catch (Throwable e) {
869           handleCoprocessorThrowable(env, e);
870         }
871         bypass |= ctx.shouldBypass();
872         if (ctx.shouldComplete()) {
873           break;
874         }
875       }
876     }
877     return bypass ? exists : null;
878   }
879 
880   /**
881    * @param get the Get request
882    * @param exists the result returned by the region server
883    * @return the result to return to the client
884    * @exception IOException Exception
885    */
886   public boolean postExists(final Get get, boolean exists)
887       throws IOException {
888     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
889     for (RegionEnvironment env: coprocessors) {
890       if (env.getInstance() instanceof RegionObserver) {
891         ctx = ObserverContext.createAndPrepare(env, ctx);
892         try {
893           exists = ((RegionObserver)env.getInstance()).postExists(ctx, get, exists);
894         } catch (Throwable e) {
895           handleCoprocessorThrowable(env, e);
896         }
897         if (ctx.shouldComplete()) {
898           break;
899         }
900       }
901     }
902     return exists;
903   }
904 
905   /**
906    * @param put The Put object
907    * @param edit The WALEdit object.
908    * @param durability The durability used
909    * @return true if default processing should be bypassed
910    * @exception IOException Exception
911    */
912   public boolean prePut(Put put, WALEdit edit,
913       final Durability durability) throws IOException {
914     boolean bypass = false;
915     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
916     for (RegionEnvironment env: coprocessors) {
917       if (env.getInstance() instanceof RegionObserver) {
918         ctx = ObserverContext.createAndPrepare(env, ctx);
919         try {
920           ((RegionObserver)env.getInstance()).prePut(ctx, put, edit, durability);
921         } catch (Throwable e) {
922           handleCoprocessorThrowable(env, e);
923         }
924         bypass |= ctx.shouldBypass();
925         if (ctx.shouldComplete()) {
926           break;
927         }
928       }
929     }
930     return bypass;
931   }
932 
933   /**
934    * @param put The Put object
935    * @param edit The WALEdit object.
936    * @param durability The durability used
937    * @exception IOException Exception
938    */
939   public void postPut(Put put, WALEdit edit,
940       final Durability durability) throws IOException {
941     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
942     for (RegionEnvironment env: coprocessors) {
943       if (env.getInstance() instanceof RegionObserver) {
944         ctx = ObserverContext.createAndPrepare(env, ctx);
945         try {
946           ((RegionObserver)env.getInstance()).postPut(ctx, put, edit, durability);
947         } catch (Throwable e) {
948           handleCoprocessorThrowable(env, e);
949         }
950         if (ctx.shouldComplete()) {
951           break;
952         }
953       }
954     }
955   }
956 
957   /**
958    * @param delete The Delete object
959    * @param edit The WALEdit object.
960    * @param durability The durability used
961    * @return true if default processing should be bypassed
962    * @exception IOException Exception
963    */
964   public boolean preDelete(Delete delete, WALEdit edit,
965       final Durability durability) throws IOException {
966     boolean bypass = false;
967     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
968     for (RegionEnvironment env: coprocessors) {
969       if (env.getInstance() instanceof RegionObserver) {
970         ctx = ObserverContext.createAndPrepare(env, ctx);
971         try {
972           ((RegionObserver)env.getInstance()).preDelete(ctx, delete, edit, durability);
973         } catch (Throwable e) {
974           handleCoprocessorThrowable(env, e);
975         }
976         bypass |= ctx.shouldBypass();
977         if (ctx.shouldComplete()) {
978           break;
979         }
980       }
981     }
982     return bypass;
983   }
984 
985   /**
986    * @param delete The Delete object
987    * @param edit The WALEdit object.
988    * @param durability The durability used
989    * @exception IOException Exception
990    */
991   public void postDelete(Delete delete, WALEdit edit,
992       final Durability durability) throws IOException {
993     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
994     for (RegionEnvironment env: coprocessors) {
995       if (env.getInstance() instanceof RegionObserver) {
996         ctx = ObserverContext.createAndPrepare(env, ctx);
997         try {
998           ((RegionObserver)env.getInstance()).postDelete(ctx, delete, edit, durability);
999         } catch (Throwable e) {
1000           handleCoprocessorThrowable(env, e);
1001         }
1002         if (ctx.shouldComplete()) {
1003           break;
1004         }
1005       }
1006     }
1007   }
1008   
1009   /**
1010    * @param miniBatchOp
1011    * @return true if default processing should be bypassed
1012    * @throws IOException
1013    */
1014   public boolean preBatchMutate(
1015       final MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
1016     boolean bypass = false;
1017     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1018     for (RegionEnvironment env : coprocessors) {
1019       if (env.getInstance() instanceof RegionObserver) {
1020         ctx = ObserverContext.createAndPrepare(env, ctx);
1021         try {
1022           ((RegionObserver) env.getInstance()).preBatchMutate(ctx, miniBatchOp);
1023         } catch (Throwable e) {
1024           handleCoprocessorThrowable(env, e);
1025         }
1026         bypass |= ctx.shouldBypass();
1027         if (ctx.shouldComplete()) {
1028           break;
1029         }
1030       }
1031     }
1032     return bypass;
1033   }
1034 
1035   /**
1036    * @param miniBatchOp
1037    * @throws IOException
1038    */
1039   public void postBatchMutate(
1040       final MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
1041     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1042     for (RegionEnvironment env : coprocessors) {
1043       if (env.getInstance() instanceof RegionObserver) {
1044         ctx = ObserverContext.createAndPrepare(env, ctx);
1045         try {
1046           ((RegionObserver) env.getInstance()).postBatchMutate(ctx, miniBatchOp);
1047         } catch (Throwable e) {
1048           handleCoprocessorThrowable(env, e);
1049         }
1050         if (ctx.shouldComplete()) {
1051           break;
1052         }
1053       }
1054     }
1055   }
1056 
1057   /**
1058    * @param row row to check
1059    * @param family column family
1060    * @param qualifier column qualifier
1061    * @param compareOp the comparison operation
1062    * @param comparator the comparator
1063    * @param put data to put if check succeeds
1064    * @return true or false to return to client if default processing should
1065    * be bypassed, or null otherwise
1066    * @throws IOException e
1067    */
1068   public Boolean preCheckAndPut(final byte [] row, final byte [] family,
1069       final byte [] qualifier, final CompareOp compareOp,
1070       final ByteArrayComparable comparator, Put put)
1071     throws IOException {
1072     boolean bypass = false;
1073     boolean result = false;
1074     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1075     for (RegionEnvironment env: coprocessors) {
1076       if (env.getInstance() instanceof RegionObserver) {
1077         ctx = ObserverContext.createAndPrepare(env, ctx);
1078         try {
1079           result = ((RegionObserver)env.getInstance()).preCheckAndPut(ctx, row, family,
1080             qualifier, compareOp, comparator, put, result);
1081         } catch (Throwable e) {
1082           handleCoprocessorThrowable(env, e);
1083         }
1084 
1085 
1086         bypass |= ctx.shouldBypass();
1087         if (ctx.shouldComplete()) {
1088           break;
1089         }
1090       }
1091     }
1092     return bypass ? result : null;
1093   }
1094 
1095   /**
1096    * @param row row to check
1097    * @param family column family
1098    * @param qualifier column qualifier
1099    * @param compareOp the comparison operation
1100    * @param comparator the comparator
1101    * @param put data to put if check succeeds
1102    * @throws IOException e
1103    */
1104   public boolean postCheckAndPut(final byte [] row, final byte [] family,
1105       final byte [] qualifier, final CompareOp compareOp,
1106       final ByteArrayComparable comparator, final Put put,
1107       boolean result)
1108     throws IOException {
1109     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1110     for (RegionEnvironment env: coprocessors) {
1111       if (env.getInstance() instanceof RegionObserver) {
1112         ctx = ObserverContext.createAndPrepare(env, ctx);
1113         try {
1114           result = ((RegionObserver)env.getInstance()).postCheckAndPut(ctx, row,
1115             family, qualifier, compareOp, comparator, put, result);
1116         } catch (Throwable e) {
1117           handleCoprocessorThrowable(env, e);
1118         }
1119         if (ctx.shouldComplete()) {
1120           break;
1121         }
1122       }
1123     }
1124     return result;
1125   }
1126 
1127   /**
1128    * @param row row to check
1129    * @param family column family
1130    * @param qualifier column qualifier
1131    * @param compareOp the comparison operation
1132    * @param comparator the comparator
1133    * @param delete delete to commit if check succeeds
1134    * @return true or false to return to client if default processing should
1135    * be bypassed, or null otherwise
1136    * @throws IOException e
1137    */
1138   public Boolean preCheckAndDelete(final byte [] row, final byte [] family,
1139       final byte [] qualifier, final CompareOp compareOp,
1140       final ByteArrayComparable comparator, Delete delete)
1141       throws IOException {
1142     boolean bypass = false;
1143     boolean result = false;
1144     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1145     for (RegionEnvironment env: coprocessors) {
1146       if (env.getInstance() instanceof RegionObserver) {
1147         ctx = ObserverContext.createAndPrepare(env, ctx);
1148         try {
1149           result = ((RegionObserver)env.getInstance()).preCheckAndDelete(ctx, row,
1150             family, qualifier, compareOp, comparator, delete, result);
1151         } catch (Throwable e) {
1152           handleCoprocessorThrowable(env, e);
1153         }
1154         bypass |= ctx.shouldBypass();
1155         if (ctx.shouldComplete()) {
1156           break;
1157         }
1158       }
1159     }
1160     return bypass ? result : null;
1161   }
1162 
1163   /**
1164    * @param row row to check
1165    * @param family column family
1166    * @param qualifier column qualifier
1167    * @param compareOp the comparison operation
1168    * @param comparator the comparator
1169    * @param delete delete to commit if check succeeds
1170    * @throws IOException e
1171    */
1172   public boolean postCheckAndDelete(final byte [] row, final byte [] family,
1173       final byte [] qualifier, final CompareOp compareOp,
1174       final ByteArrayComparable comparator, final Delete delete,
1175       boolean result)
1176     throws IOException {
1177     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1178     for (RegionEnvironment env: coprocessors) {
1179       if (env.getInstance() instanceof RegionObserver) {
1180         ctx = ObserverContext.createAndPrepare(env, ctx);
1181         try {
1182           result = ((RegionObserver)env.getInstance())
1183             .postCheckAndDelete(ctx, row, family, qualifier, compareOp,
1184               comparator, delete, result);
1185         } catch (Throwable e) {
1186           handleCoprocessorThrowable(env, e);
1187         }
1188         if (ctx.shouldComplete()) {
1189           break;
1190         }
1191       }
1192     }
1193     return result;
1194   }
1195 
1196   /**
1197    * @param append append object
1198    * @return result to return to client if default operation should be
1199    * bypassed, null otherwise
1200    * @throws IOException if an error occurred on the coprocessor
1201    */
1202   public Result preAppend(Append append)
1203       throws IOException {
1204     boolean bypass = false;
1205     Result result = null;
1206     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1207     for (RegionEnvironment env: coprocessors) {
1208       if (env.getInstance() instanceof RegionObserver) {
1209         ctx = ObserverContext.createAndPrepare(env, ctx);
1210         try {
1211           result = ((RegionObserver)env.getInstance()).preAppend(ctx, append);
1212         } catch (Throwable e) {
1213           handleCoprocessorThrowable(env, e);
1214         }
1215         bypass |= ctx.shouldBypass();
1216         if (ctx.shouldComplete()) {
1217           break;
1218         }
1219       }
1220     }
1221     return bypass ? result : null;
1222   }
1223 
1224   /**
1225    * @param increment increment object
1226    * @return result to return to client if default operation should be
1227    * bypassed, null otherwise
1228    * @throws IOException if an error occurred on the coprocessor
1229    */
1230   public Result preIncrement(Increment increment)
1231       throws IOException {
1232     boolean bypass = false;
1233     Result result = null;
1234     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1235     for (RegionEnvironment env: coprocessors) {
1236       if (env.getInstance() instanceof RegionObserver) {
1237         ctx = ObserverContext.createAndPrepare(env, ctx);
1238         try {
1239           result = ((RegionObserver)env.getInstance()).preIncrement(ctx, increment);
1240         } catch (Throwable e) {
1241           handleCoprocessorThrowable(env, e);
1242         }
1243         bypass |= ctx.shouldBypass();
1244         if (ctx.shouldComplete()) {
1245           break;
1246         }
1247       }
1248     }
1249     return bypass ? result : null;
1250   }
1251 
1252   /**
1253    * @param append Append object
1254    * @param result the result returned by postAppend
1255    * @throws IOException if an error occurred on the coprocessor
1256    */
1257   public void postAppend(final Append append, Result result)
1258       throws IOException {
1259     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1260     for (RegionEnvironment env: coprocessors) {
1261       if (env.getInstance() instanceof RegionObserver) {
1262         ctx = ObserverContext.createAndPrepare(env, ctx);
1263         try {
1264           ((RegionObserver)env.getInstance()).postAppend(ctx, append, result);
1265         } catch (Throwable e) {
1266           handleCoprocessorThrowable(env, e);
1267         }
1268         if (ctx.shouldComplete()) {
1269           break;
1270         }
1271       }
1272     }
1273   }
1274 
1275   /**
1276    * @param increment increment object
1277    * @param result the result returned by postIncrement
1278    * @throws IOException if an error occurred on the coprocessor
1279    */
1280   public Result postIncrement(final Increment increment, Result result)
1281       throws IOException {
1282     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1283     for (RegionEnvironment env: coprocessors) {
1284       if (env.getInstance() instanceof RegionObserver) {
1285         ctx = ObserverContext.createAndPrepare(env, ctx);
1286         try {
1287           result = ((RegionObserver)env.getInstance()).postIncrement(ctx, increment, result);
1288         } catch (Throwable e) {
1289           handleCoprocessorThrowable(env, e);
1290         }
1291         if (ctx.shouldComplete()) {
1292           break;
1293         }
1294       }
1295     }
1296     return result;
1297   }
1298 
1299   /**
1300    * @param scan the Scan specification
1301    * @return scanner id to return to client if default operation should be
1302    * bypassed, false otherwise
1303    * @exception IOException Exception
1304    */
1305   public RegionScanner preScannerOpen(Scan scan) throws IOException {
1306     boolean bypass = false;
1307     RegionScanner s = null;
1308     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1309     for (RegionEnvironment env: coprocessors) {
1310       if (env.getInstance() instanceof RegionObserver) {
1311         ctx = ObserverContext.createAndPrepare(env, ctx);
1312         try {
1313           s = ((RegionObserver)env.getInstance()).preScannerOpen(ctx, scan, s);
1314         } catch (Throwable e) {
1315           handleCoprocessorThrowable(env, e);
1316         }
1317         bypass |= ctx.shouldBypass();
1318         if (ctx.shouldComplete()) {
1319           break;
1320         }
1321       }
1322     }
1323     return bypass ? s : null;
1324   }
1325 
1326   /**
1327    * See
1328    * {@link RegionObserver#preStoreScannerOpen(ObserverContext,
1329    *    Store, Scan, NavigableSet, KeyValueScanner)}
1330    */
1331   public KeyValueScanner preStoreScannerOpen(Store store, Scan scan,
1332       final NavigableSet<byte[]> targetCols) throws IOException {
1333     KeyValueScanner s = null;
1334     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1335     for (RegionEnvironment env: coprocessors) {
1336       if (env.getInstance() instanceof RegionObserver) {
1337         ctx = ObserverContext.createAndPrepare(env, ctx);
1338         try {
1339           s = ((RegionObserver) env.getInstance()).preStoreScannerOpen(ctx, store, scan,
1340               targetCols, s);
1341         } catch (Throwable e) {
1342           handleCoprocessorThrowable(env, e);
1343         }
1344         if (ctx.shouldComplete()) {
1345           break;
1346         }
1347       }
1348     }
1349     return s;
1350   }
1351 
1352   /**
1353    * @param scan the Scan specification
1354    * @param s the scanner
1355    * @return the scanner instance to use
1356    * @exception IOException Exception
1357    */
1358   public RegionScanner postScannerOpen(final Scan scan, RegionScanner s)
1359       throws IOException {
1360     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1361     for (RegionEnvironment env: coprocessors) {
1362       if (env.getInstance() instanceof RegionObserver) {
1363         ctx = ObserverContext.createAndPrepare(env, ctx);
1364         try {
1365           s = ((RegionObserver)env.getInstance()).postScannerOpen(ctx, scan, s);
1366         } catch (Throwable e) {
1367           handleCoprocessorThrowable(env, e);
1368         }
1369         if (ctx.shouldComplete()) {
1370           break;
1371         }
1372       }
1373     }
1374     return s;
1375   }
1376 
1377   /**
1378    * @param s the scanner
1379    * @param results the result set returned by the region server
1380    * @param limit the maximum number of results to return
1381    * @return 'has next' indication to client if bypassing default behavior, or
1382    * null otherwise
1383    * @exception IOException Exception
1384    */
1385   public Boolean preScannerNext(final InternalScanner s,
1386       final List<Result> results, int limit) throws IOException {
1387     boolean bypass = false;
1388     boolean hasNext = false;
1389     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1390     for (RegionEnvironment env: coprocessors) {
1391       if (env.getInstance() instanceof RegionObserver) {
1392         ctx = ObserverContext.createAndPrepare(env, ctx);
1393         try {
1394           hasNext = ((RegionObserver)env.getInstance()).preScannerNext(ctx, s, results,
1395             limit, hasNext);
1396         } catch (Throwable e) {
1397           handleCoprocessorThrowable(env, e);
1398         }
1399         bypass |= ctx.shouldBypass();
1400         if (ctx.shouldComplete()) {
1401           break;
1402         }
1403       }
1404     }
1405     return bypass ? hasNext : null;
1406   }
1407 
1408   /**
1409    * @param s the scanner
1410    * @param results the result set returned by the region server
1411    * @param limit the maximum number of results to return
1412    * @param hasMore
1413    * @return 'has more' indication to give to client
1414    * @exception IOException Exception
1415    */
1416   public boolean postScannerNext(final InternalScanner s,
1417       final List<Result> results, final int limit, boolean hasMore)
1418       throws IOException {
1419     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1420     for (RegionEnvironment env: coprocessors) {
1421       if (env.getInstance() instanceof RegionObserver) {
1422         ctx = ObserverContext.createAndPrepare(env, ctx);
1423         try {
1424           hasMore = ((RegionObserver)env.getInstance()).postScannerNext(ctx, s,
1425             results, limit, hasMore);
1426         } catch (Throwable e) {
1427           handleCoprocessorThrowable(env, e);
1428         }
1429         if (ctx.shouldComplete()) {
1430           break;
1431         }
1432       }
1433     }
1434     return hasMore;
1435   }
1436 
1437   /**
1438    * This will be called by the scan flow when the current scanned row is being filtered out by the
1439    * filter.
1440    * @param s the scanner
1441    * @param currentRow The current rowkey which got filtered out
1442    * @param offset offset to rowkey
1443    * @param length length of rowkey
1444    * @return whether more rows are available for the scanner or not
1445    * @throws IOException
1446    */
1447   public boolean postScannerFilterRow(final InternalScanner s, final byte[] currentRow, int offset,
1448       short length) throws IOException {
1449     boolean hasMore = true; // By default assume more rows there.
1450     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1451     for (RegionEnvironment env : coprocessors) {
1452       if (env.getInstance() instanceof RegionObserver) {
1453         ctx = ObserverContext.createAndPrepare(env, ctx);
1454         try {
1455           hasMore = ((RegionObserver) env.getInstance()).postScannerFilterRow(ctx, s, currentRow,
1456               offset, length, hasMore);
1457         } catch (Throwable e) {
1458           handleCoprocessorThrowable(env, e);
1459         }
1460         if (ctx.shouldComplete()) {
1461           break;
1462         }
1463       }
1464     }
1465     return hasMore;
1466   }
1467   
1468   /**
1469    * @param s the scanner
1470    * @return true if default behavior should be bypassed, false otherwise
1471    * @exception IOException Exception
1472    */
1473   public boolean preScannerClose(final InternalScanner s)
1474       throws IOException {
1475     boolean bypass = false;
1476     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1477     for (RegionEnvironment env: coprocessors) {
1478       if (env.getInstance() instanceof RegionObserver) {
1479         ctx = ObserverContext.createAndPrepare(env, ctx);
1480         try {
1481           ((RegionObserver)env.getInstance()).preScannerClose(ctx, s);
1482         } catch (Throwable e) {
1483           handleCoprocessorThrowable(env, e);
1484         }
1485         bypass |= ctx.shouldBypass();
1486         if (ctx.shouldComplete()) {
1487           break;
1488         }
1489       }
1490     }
1491     return bypass;
1492   }
1493 
1494   /**
1495    * @param s the scanner
1496    * @exception IOException Exception
1497    */
1498   public void postScannerClose(final InternalScanner s)
1499       throws IOException {
1500     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1501     for (RegionEnvironment env: coprocessors) {
1502       if (env.getInstance() instanceof RegionObserver) {
1503         ctx = ObserverContext.createAndPrepare(env, ctx);
1504         try {
1505           ((RegionObserver)env.getInstance()).postScannerClose(ctx, s);
1506         } catch (Throwable e) {
1507           handleCoprocessorThrowable(env, e);
1508         }
1509         if (ctx.shouldComplete()) {
1510           break;
1511         }
1512       }
1513     }
1514   }
1515 
1516   /**
1517    * @param info
1518    * @param logKey
1519    * @param logEdit
1520    * @return true if default behavior should be bypassed, false otherwise
1521    * @throws IOException
1522    */
1523   public boolean preWALRestore(HRegionInfo info, HLogKey logKey,
1524       WALEdit logEdit) throws IOException {
1525     boolean bypass = false;
1526     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1527     for (RegionEnvironment env: coprocessors) {
1528       if (env.getInstance() instanceof RegionObserver) {
1529         ctx = ObserverContext.createAndPrepare(env, ctx);
1530         try {
1531           ((RegionObserver)env.getInstance()).preWALRestore(ctx, info, logKey,
1532               logEdit);
1533         } catch (Throwable e) {
1534           handleCoprocessorThrowable(env, e);
1535         }
1536         bypass |= ctx.shouldBypass();
1537         if (ctx.shouldComplete()) {
1538           break;
1539         }
1540       }
1541     }
1542     return bypass;
1543   }
1544 
1545   /**
1546    * @param info
1547    * @param logKey
1548    * @param logEdit
1549    * @throws IOException
1550    */
1551   public void postWALRestore(HRegionInfo info, HLogKey logKey,
1552       WALEdit logEdit) throws IOException {
1553     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1554     for (RegionEnvironment env: coprocessors) {
1555       if (env.getInstance() instanceof RegionObserver) {
1556         ctx = ObserverContext.createAndPrepare(env, ctx);
1557         try {
1558           ((RegionObserver)env.getInstance()).postWALRestore(ctx, info,
1559               logKey, logEdit);
1560         } catch (Throwable e) {
1561           handleCoprocessorThrowable(env, e);
1562         }
1563         if (ctx.shouldComplete()) {
1564           break;
1565         }
1566       }
1567     }
1568   }
1569 
1570   /**
1571    * @param familyPaths pairs of { CF, file path } submitted for bulk load
1572    * @return true if the default operation should be bypassed
1573    * @throws IOException
1574    */
1575   public boolean preBulkLoadHFile(List<Pair<byte[], String>> familyPaths) throws IOException {
1576     boolean bypass = false;
1577     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1578     for (RegionEnvironment env: coprocessors) {
1579       if (env.getInstance() instanceof RegionObserver) {
1580         ctx = ObserverContext.createAndPrepare(env, ctx);
1581         try {
1582           ((RegionObserver)env.getInstance()).preBulkLoadHFile(ctx, familyPaths);
1583         } catch (Throwable e) {
1584           handleCoprocessorThrowable(env, e);
1585         }
1586         bypass |= ctx.shouldBypass();
1587         if (ctx.shouldComplete()) {
1588           break;
1589         }
1590       }
1591     }
1592 
1593     return bypass;
1594   }
1595 
1596   /**
1597    * @param familyPaths pairs of { CF, file path } submitted for bulk load
1598    * @param hasLoaded whether load was successful or not
1599    * @return the possibly modified value of hasLoaded
1600    * @throws IOException
1601    */
1602   public boolean postBulkLoadHFile(List<Pair<byte[], String>> familyPaths, boolean hasLoaded)
1603       throws IOException {
1604     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1605     for (RegionEnvironment env: coprocessors) {
1606       if (env.getInstance() instanceof RegionObserver) {
1607         ctx = ObserverContext.createAndPrepare(env, ctx);
1608         try {
1609           hasLoaded = ((RegionObserver)env.getInstance()).postBulkLoadHFile(ctx,
1610             familyPaths, hasLoaded);
1611         } catch (Throwable e) {
1612           handleCoprocessorThrowable(env, e);
1613         }
1614         if (ctx.shouldComplete()) {
1615           break;
1616         }
1617       }
1618     }
1619 
1620     return hasLoaded;
1621   }
1622 
1623 }