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 before a region is closed
315    * @param abortRequested true if the server is aborting
316    */
317   public void preClose(boolean abortRequested) throws IOException {
318     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
319     for (RegionEnvironment env: coprocessors) {
320       if (env.getInstance() instanceof RegionObserver) {
321         ctx = ObserverContext.createAndPrepare(env, ctx);
322         try {
323           ((RegionObserver) env.getInstance()).preClose(ctx, abortRequested);
324         } catch (Throwable e) {
325           handleCoprocessorThrowable(env, e);
326         }
327       }
328     }
329   }
330 
331   /**
332    * Invoked after a region is closed
333    * @param abortRequested true if the server is aborting
334    */
335   public void postClose(boolean abortRequested) {
336     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
337     for (RegionEnvironment env: coprocessors) {
338       if (env.getInstance() instanceof RegionObserver) {
339         ctx = ObserverContext.createAndPrepare(env, ctx);
340         try {
341           ((RegionObserver) env.getInstance()).postClose(ctx, abortRequested);
342         } catch (Throwable e) {
343           handleCoprocessorThrowableNoRethrow(env, e);
344         }
345 
346       }
347       shutdown(env);
348     }
349   }
350 
351   /**
352    * See
353    * {@link RegionObserver#preCompactScannerOpen(ObserverContext, Store, List, ScanType, long, InternalScanner, CompactionRequest)}
354    */
355   public InternalScanner preCompactScannerOpen(Store store, List<StoreFileScanner> scanners,
356       ScanType scanType, long earliestPutTs, CompactionRequest request) throws IOException {
357     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
358     InternalScanner s = null;
359     for (RegionEnvironment env: coprocessors) {
360       if (env.getInstance() instanceof RegionObserver) {
361         ctx = ObserverContext.createAndPrepare(env, ctx);
362         try {
363           s = ((RegionObserver) env.getInstance()).preCompactScannerOpen(ctx, store, scanners,
364             scanType, earliestPutTs, s, request);
365         } catch (Throwable e) {
366           handleCoprocessorThrowable(env,e);
367         }
368         if (ctx.shouldComplete()) {
369           break;
370         }
371       }
372     }
373     return s;
374   }
375 
376   /**
377    * Called prior to selecting the {@link StoreFile}s for compaction from the list of currently
378    * available candidates.
379    * @param store The store where compaction is being requested
380    * @param candidates The currently available store files
381    * @param request custom compaction request
382    * @return If {@code true}, skip the normal selection process and use the current list
383    * @throws IOException
384    */
385   public boolean preCompactSelection(Store store, List<StoreFile> candidates,
386       CompactionRequest request) throws IOException {
387     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
388     boolean bypass = false;
389     for (RegionEnvironment env: coprocessors) {
390       if (env.getInstance() instanceof RegionObserver) {
391         ctx = ObserverContext.createAndPrepare(env, ctx);
392         try {
393           ((RegionObserver) env.getInstance()).preCompactSelection(ctx, store, candidates, request);
394         } catch (Throwable e) {
395           handleCoprocessorThrowable(env,e);
396 
397         }
398         bypass |= ctx.shouldBypass();
399         if (ctx.shouldComplete()) {
400           break;
401         }
402       }
403     }
404     return bypass;
405   }
406 
407   /**
408    * Called after the {@link StoreFile}s to be compacted have been selected from the available
409    * candidates.
410    * @param store The store where compaction is being requested
411    * @param selected The store files selected to compact
412    * @param request custom compaction
413    */
414   public void postCompactSelection(Store store, ImmutableList<StoreFile> selected,
415       CompactionRequest request) {
416     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
417     for (RegionEnvironment env: coprocessors) {
418       if (env.getInstance() instanceof RegionObserver) {
419         ctx = ObserverContext.createAndPrepare(env, ctx);
420         try {
421           ((RegionObserver) env.getInstance()).postCompactSelection(ctx, store, selected, request);
422         } catch (Throwable e) {
423           handleCoprocessorThrowableNoRethrow(env,e);
424         }
425         if (ctx.shouldComplete()) {
426           break;
427         }
428       }
429     }
430   }
431 
432   /**
433    * Called prior to rewriting the store files selected for compaction
434    * @param store the store being compacted
435    * @param scanner the scanner used to read store data during compaction
436    * @param scanType type of Scan
437    * @param request the compaction that will be executed
438    * @throws IOException
439    */
440   public InternalScanner preCompact(Store store, InternalScanner scanner, ScanType scanType,
441       CompactionRequest request) throws IOException {
442     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
443     boolean bypass = false;
444     for (RegionEnvironment env: coprocessors) {
445       if (env.getInstance() instanceof RegionObserver) {
446         ctx = ObserverContext.createAndPrepare(env, ctx);
447         try {
448           scanner = ((RegionObserver) env.getInstance()).preCompact(ctx, store, scanner, scanType,
449             request);
450         } catch (Throwable e) {
451           handleCoprocessorThrowable(env,e);
452         }
453         bypass |= ctx.shouldBypass();
454         if (ctx.shouldComplete()) {
455           break;
456         }
457       }
458     }
459     return bypass ? null : scanner;
460   }
461 
462   /**
463    * Called after the store compaction has completed.
464    * @param store the store being compacted
465    * @param resultFile the new store file written during compaction
466    * @param request the compaction that is being executed
467    * @throws IOException
468    */
469   public void postCompact(Store store, StoreFile resultFile, CompactionRequest request)
470       throws IOException {
471     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
472     for (RegionEnvironment env: coprocessors) {
473       if (env.getInstance() instanceof RegionObserver) {
474         ctx = ObserverContext.createAndPrepare(env, ctx);
475         try {
476           ((RegionObserver) env.getInstance()).postCompact(ctx, store, resultFile, request);
477         } catch (Throwable e) {
478           handleCoprocessorThrowable(env, e);
479         }
480         if (ctx.shouldComplete()) {
481           break;
482         }
483       }
484     }
485   }
486 
487   /**
488    * Invoked before a memstore flush
489    * @throws IOException
490    */
491   public InternalScanner preFlush(Store store, InternalScanner scanner) throws IOException {
492     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
493     boolean bypass = false;
494     for (RegionEnvironment env: coprocessors) {
495       if (env.getInstance() instanceof RegionObserver) {
496         ctx = ObserverContext.createAndPrepare(env, ctx);
497         try {
498           scanner = ((RegionObserver)env.getInstance()).preFlush(
499               ctx, store, scanner);
500         } catch (Throwable e) {
501           handleCoprocessorThrowable(env,e);
502         }
503         bypass |= ctx.shouldBypass();
504         if (ctx.shouldComplete()) {
505           break;
506         }
507       }
508     }
509     return bypass ? null : scanner;
510   }
511 
512   /**
513    * Invoked before a memstore flush
514    * @throws IOException
515    */
516   public void preFlush() throws IOException {
517     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
518     for (RegionEnvironment env: coprocessors) {
519       if (env.getInstance() instanceof RegionObserver) {
520         ctx = ObserverContext.createAndPrepare(env, ctx);
521         try {
522           ((RegionObserver)env.getInstance()).preFlush(ctx);
523         } catch (Throwable e) {
524           handleCoprocessorThrowable(env, e);
525         }
526         if (ctx.shouldComplete()) {
527           break;
528         }
529       }
530     }
531   }
532 
533   /**
534    * See
535    * {@link RegionObserver#preFlushScannerOpen(ObserverContext,
536    *    Store, KeyValueScanner, InternalScanner)}
537    */
538   public InternalScanner preFlushScannerOpen(Store store, KeyValueScanner memstoreScanner)
539       throws IOException {
540     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
541     InternalScanner s = null;
542     for (RegionEnvironment env : coprocessors) {
543       if (env.getInstance() instanceof RegionObserver) {
544         ctx = ObserverContext.createAndPrepare(env, ctx);
545         try {
546           s = ((RegionObserver) env.getInstance())
547             .preFlushScannerOpen(ctx, store, memstoreScanner, s);
548         } catch (Throwable e) {
549           handleCoprocessorThrowable(env, e);
550         }
551         if (ctx.shouldComplete()) {
552           break;
553         }
554       }
555     }
556     return s;
557   }
558 
559   /**
560    * Invoked after a memstore flush
561    * @throws IOException
562    */
563   public void postFlush() throws IOException {
564     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
565     for (RegionEnvironment env: coprocessors) {
566       if (env.getInstance() instanceof RegionObserver) {
567         ctx = ObserverContext.createAndPrepare(env, ctx);
568         try {
569           ((RegionObserver)env.getInstance()).postFlush(ctx);
570         } catch (Throwable e) {
571           handleCoprocessorThrowable(env, e);
572         }
573         if (ctx.shouldComplete()) {
574           break;
575         }
576       }
577     }
578   }
579 
580   /**
581    * Invoked after a memstore flush
582    * @throws IOException
583    */
584   public void postFlush(final Store store, final StoreFile storeFile) throws IOException {
585     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
586     for (RegionEnvironment env: coprocessors) {
587       if (env.getInstance() instanceof RegionObserver) {
588         ctx = ObserverContext.createAndPrepare(env, ctx);
589         try {
590           ((RegionObserver)env.getInstance()).postFlush(ctx, store, storeFile);
591         } catch (Throwable e) {
592           handleCoprocessorThrowable(env, e);
593         }
594         if (ctx.shouldComplete()) {
595           break;
596         }
597       }
598     }
599   }
600 
601   /**
602    * Invoked just before a split
603    * @throws IOException
604    */
605   public void preSplit() throws IOException {
606     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
607     for (RegionEnvironment env: coprocessors) {
608       if (env.getInstance() instanceof RegionObserver) {
609         ctx = ObserverContext.createAndPrepare(env, ctx);
610         try {
611           ((RegionObserver)env.getInstance()).preSplit(ctx);
612         } catch (Throwable e) {
613           handleCoprocessorThrowable(env, e);
614         }
615         if (ctx.shouldComplete()) {
616           break;
617         }
618       }
619     }
620   }
621 
622   /**
623    * Invoked just before a split
624    * @throws IOException
625    */
626   public void preSplit(byte[] splitRow) throws IOException {
627     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
628     for (RegionEnvironment env: coprocessors) {
629       if (env.getInstance() instanceof RegionObserver) {
630         ctx = ObserverContext.createAndPrepare(env, ctx);
631         try {
632           ((RegionObserver)env.getInstance()).preSplit(ctx, splitRow);
633         } catch (Throwable e) {
634           handleCoprocessorThrowable(env, e);
635         }
636         if (ctx.shouldComplete()) {
637           break;
638         }
639       }
640     }
641   }
642 
643   /**
644    * Invoked just after a split
645    * @param l the new left-hand daughter region
646    * @param r the new right-hand daughter region
647    * @throws IOException
648    */
649   public void postSplit(HRegion l, HRegion r) throws IOException {
650     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
651     for (RegionEnvironment env: coprocessors) {
652       if (env.getInstance() instanceof RegionObserver) {
653         ctx = ObserverContext.createAndPrepare(env, ctx);
654         try {
655           ((RegionObserver)env.getInstance()).postSplit(ctx, l, r);
656         } catch (Throwable e) {
657           handleCoprocessorThrowable(env, e);
658         }
659         if (ctx.shouldComplete()) {
660           break;
661         }
662       }
663     }
664   }
665 
666   /**
667    * Invoked just before the rollback of a failed split is started
668    * @throws IOException
669    */
670   public void preRollBackSplit() throws IOException {
671     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
672     for (RegionEnvironment env : coprocessors) {
673       if (env.getInstance() instanceof RegionObserver) {
674         ctx = ObserverContext.createAndPrepare(env, ctx);
675         try {
676           ((RegionObserver) env.getInstance()).preRollBackSplit(ctx);
677         } catch (Throwable e) {
678           handleCoprocessorThrowable(env, e);
679         }
680         if (ctx.shouldComplete()) {
681           break;
682         }
683       }
684     }
685   }
686 
687   /**
688    * Invoked just after the rollback of a failed split is done
689    * @throws IOException
690    */
691   public void postRollBackSplit() 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         try {
697           ((RegionObserver) env.getInstance()).postRollBackSplit(ctx);
698         } catch (Throwable e) {
699           handleCoprocessorThrowable(env, e);
700         }
701         if (ctx.shouldComplete()) {
702           break;
703         }
704       }
705     }
706   }
707 
708   /**
709    * Invoked after a split is completed irrespective of a failure or success.
710    * @throws IOException
711    */
712   public void postCompleteSplit() throws IOException {
713     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
714     for (RegionEnvironment env : coprocessors) {
715       if (env.getInstance() instanceof RegionObserver) {
716         ctx = ObserverContext.createAndPrepare(env, ctx);
717         try {
718           ((RegionObserver) env.getInstance()).postCompleteSplit(ctx);
719         } catch (Throwable e) {
720           handleCoprocessorThrowable(env, e);
721         }
722         if (ctx.shouldComplete()) {
723           break;
724         }
725       }
726     }
727   }
728   // RegionObserver support
729 
730   /**
731    * @param row the row key
732    * @param family the family
733    * @param result the result set from the region
734    * @return true if default processing should be bypassed
735    * @exception IOException Exception
736    */
737   public boolean preGetClosestRowBefore(final byte[] row, final byte[] family,
738       final Result result) throws IOException {
739     boolean bypass = false;
740     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
741     for (RegionEnvironment env: coprocessors) {
742       if (env.getInstance() instanceof RegionObserver) {
743         ctx = ObserverContext.createAndPrepare(env, ctx);
744         try {
745           ((RegionObserver)env.getInstance()).preGetClosestRowBefore(ctx, row,
746               family, result);
747         } catch (Throwable e) {
748           handleCoprocessorThrowable(env, e);
749         }
750         bypass |= ctx.shouldBypass();
751         if (ctx.shouldComplete()) {
752           break;
753         }
754       }
755     }
756     return bypass;
757   }
758 
759   /**
760    * @param row the row key
761    * @param family the family
762    * @param result the result set from the region
763    * @exception IOException Exception
764    */
765   public void postGetClosestRowBefore(final byte[] row, final byte[] family,
766       final Result result) throws IOException {
767     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
768     for (RegionEnvironment env: coprocessors) {
769       if (env.getInstance() instanceof RegionObserver) {
770         ctx = ObserverContext.createAndPrepare(env, ctx);
771         try {
772           ((RegionObserver)env.getInstance()).postGetClosestRowBefore(ctx, row,
773               family, result);
774         } catch (Throwable e) {
775           handleCoprocessorThrowable(env, e);
776         }
777         if (ctx.shouldComplete()) {
778           break;
779         }
780       }
781     }
782   }
783 
784   /**
785    * @param get the Get request
786    * @return true if default processing should be bypassed
787    * @exception IOException Exception
788    */
789   public boolean preGet(final Get get, final List<Cell> results)
790       throws IOException {
791     boolean bypass = false;
792     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
793     for (RegionEnvironment env: coprocessors) {
794       if (env.getInstance() instanceof RegionObserver) {
795         ctx = ObserverContext.createAndPrepare(env, ctx);
796         try {
797           ((RegionObserver)env.getInstance()).preGetOp(ctx, get, results);
798         } catch (Throwable e) {
799           handleCoprocessorThrowable(env, e);
800         }
801         bypass |= ctx.shouldBypass();
802         if (ctx.shouldComplete()) {
803           break;
804         }
805       }
806     }
807     return bypass;
808   }
809 
810   /**
811    * @param get the Get request
812    * @param results the result sett
813    * @exception IOException Exception
814    */
815   public void postGet(final Get get, final List<Cell> results)
816   throws IOException {
817     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
818     for (RegionEnvironment env: coprocessors) {
819       if (env.getInstance() instanceof RegionObserver) {
820         ctx = ObserverContext.createAndPrepare(env, ctx);
821         try {
822           ((RegionObserver)env.getInstance()).postGetOp(ctx, get, results);
823         } catch (Throwable e) {
824           handleCoprocessorThrowable(env, e);
825         }
826         if (ctx.shouldComplete()) {
827           break;
828         }
829       }
830     }
831   }
832 
833   /**
834    * @param get the Get request
835    * @return true or false to return to client if bypassing normal operation,
836    * or null otherwise
837    * @exception IOException Exception
838    */
839   public Boolean preExists(final Get get) throws IOException {
840     boolean bypass = false;
841     boolean exists = false;
842     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
843     for (RegionEnvironment env: coprocessors) {
844       if (env.getInstance() instanceof RegionObserver) {
845         ctx = ObserverContext.createAndPrepare(env, ctx);
846         try {
847           exists = ((RegionObserver)env.getInstance()).preExists(ctx, get, exists);
848         } catch (Throwable e) {
849           handleCoprocessorThrowable(env, e);
850         }
851         bypass |= ctx.shouldBypass();
852         if (ctx.shouldComplete()) {
853           break;
854         }
855       }
856     }
857     return bypass ? exists : null;
858   }
859 
860   /**
861    * @param get the Get request
862    * @param exists the result returned by the region server
863    * @return the result to return to the client
864    * @exception IOException Exception
865    */
866   public boolean postExists(final Get get, boolean exists)
867       throws IOException {
868     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
869     for (RegionEnvironment env: coprocessors) {
870       if (env.getInstance() instanceof RegionObserver) {
871         ctx = ObserverContext.createAndPrepare(env, ctx);
872         try {
873           exists = ((RegionObserver)env.getInstance()).postExists(ctx, get, exists);
874         } catch (Throwable e) {
875           handleCoprocessorThrowable(env, e);
876         }
877         if (ctx.shouldComplete()) {
878           break;
879         }
880       }
881     }
882     return exists;
883   }
884 
885   /**
886    * @param put The Put object
887    * @param edit The WALEdit object.
888    * @param durability The durability used
889    * @return true if default processing should be bypassed
890    * @exception IOException Exception
891    */
892   public boolean prePut(Put put, WALEdit edit,
893       final Durability durability) throws IOException {
894     boolean bypass = false;
895     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
896     for (RegionEnvironment env: coprocessors) {
897       if (env.getInstance() instanceof RegionObserver) {
898         ctx = ObserverContext.createAndPrepare(env, ctx);
899         try {
900           ((RegionObserver)env.getInstance()).prePut(ctx, put, edit, durability);
901         } catch (Throwable e) {
902           handleCoprocessorThrowable(env, e);
903         }
904         bypass |= ctx.shouldBypass();
905         if (ctx.shouldComplete()) {
906           break;
907         }
908       }
909     }
910     return bypass;
911   }
912 
913   /**
914    * @param put The Put object
915    * @param edit The WALEdit object.
916    * @param durability The durability used
917    * @exception IOException Exception
918    */
919   public void postPut(Put put, WALEdit edit,
920       final Durability durability) throws IOException {
921     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
922     for (RegionEnvironment env: coprocessors) {
923       if (env.getInstance() instanceof RegionObserver) {
924         ctx = ObserverContext.createAndPrepare(env, ctx);
925         try {
926           ((RegionObserver)env.getInstance()).postPut(ctx, put, edit, durability);
927         } catch (Throwable e) {
928           handleCoprocessorThrowable(env, e);
929         }
930         if (ctx.shouldComplete()) {
931           break;
932         }
933       }
934     }
935   }
936 
937   /**
938    * @param delete The Delete object
939    * @param edit The WALEdit object.
940    * @param durability The durability used
941    * @return true if default processing should be bypassed
942    * @exception IOException Exception
943    */
944   public boolean preDelete(Delete delete, WALEdit edit,
945       final Durability durability) throws IOException {
946     boolean bypass = false;
947     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
948     for (RegionEnvironment env: coprocessors) {
949       if (env.getInstance() instanceof RegionObserver) {
950         ctx = ObserverContext.createAndPrepare(env, ctx);
951         try {
952           ((RegionObserver)env.getInstance()).preDelete(ctx, delete, edit, durability);
953         } catch (Throwable e) {
954           handleCoprocessorThrowable(env, e);
955         }
956         bypass |= ctx.shouldBypass();
957         if (ctx.shouldComplete()) {
958           break;
959         }
960       }
961     }
962     return bypass;
963   }
964 
965   /**
966    * @param delete The Delete object
967    * @param edit The WALEdit object.
968    * @param durability The durability used
969    * @exception IOException Exception
970    */
971   public void postDelete(Delete delete, WALEdit edit,
972       final Durability durability) throws IOException {
973     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
974     for (RegionEnvironment env: coprocessors) {
975       if (env.getInstance() instanceof RegionObserver) {
976         ctx = ObserverContext.createAndPrepare(env, ctx);
977         try {
978           ((RegionObserver)env.getInstance()).postDelete(ctx, delete, edit, durability);
979         } catch (Throwable e) {
980           handleCoprocessorThrowable(env, e);
981         }
982         if (ctx.shouldComplete()) {
983           break;
984         }
985       }
986     }
987   }
988   
989   /**
990    * @param miniBatchOp
991    * @return true if default processing should be bypassed
992    * @throws IOException
993    */
994   public boolean preBatchMutate(
995       final MiniBatchOperationInProgress<Mutation> miniBatchOp) throws IOException {
996     boolean bypass = false;
997     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
998     for (RegionEnvironment env : coprocessors) {
999       if (env.getInstance() instanceof RegionObserver) {
1000         ctx = ObserverContext.createAndPrepare(env, ctx);
1001         try {
1002           ((RegionObserver) env.getInstance()).preBatchMutate(ctx, miniBatchOp);
1003         } catch (Throwable e) {
1004           handleCoprocessorThrowable(env, e);
1005         }
1006         bypass |= ctx.shouldBypass();
1007         if (ctx.shouldComplete()) {
1008           break;
1009         }
1010       }
1011     }
1012     return bypass;
1013   }
1014 
1015   /**
1016    * @param miniBatchOp
1017    * @throws IOException
1018    */
1019   public void postBatchMutate(
1020       final MiniBatchOperationInProgress<Mutation> miniBatchOp) 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         try {
1026           ((RegionObserver) env.getInstance()).postBatchMutate(ctx, miniBatchOp);
1027         } catch (Throwable e) {
1028           handleCoprocessorThrowable(env, e);
1029         }
1030         if (ctx.shouldComplete()) {
1031           break;
1032         }
1033       }
1034     }
1035   }
1036 
1037   /**
1038    * @param row row to check
1039    * @param family column family
1040    * @param qualifier column qualifier
1041    * @param compareOp the comparison operation
1042    * @param comparator the comparator
1043    * @param put data to put if check succeeds
1044    * @return true or false to return to client if default processing should
1045    * be bypassed, or null otherwise
1046    * @throws IOException e
1047    */
1048   public Boolean preCheckAndPut(final byte [] row, final byte [] family,
1049       final byte [] qualifier, final CompareOp compareOp,
1050       final ByteArrayComparable comparator, Put put)
1051     throws IOException {
1052     boolean bypass = false;
1053     boolean result = false;
1054     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1055     for (RegionEnvironment env: coprocessors) {
1056       if (env.getInstance() instanceof RegionObserver) {
1057         ctx = ObserverContext.createAndPrepare(env, ctx);
1058         try {
1059           result = ((RegionObserver)env.getInstance()).preCheckAndPut(ctx, row, family,
1060             qualifier, compareOp, comparator, put, result);
1061         } catch (Throwable e) {
1062           handleCoprocessorThrowable(env, e);
1063         }
1064 
1065 
1066         bypass |= ctx.shouldBypass();
1067         if (ctx.shouldComplete()) {
1068           break;
1069         }
1070       }
1071     }
1072     return bypass ? result : null;
1073   }
1074 
1075   /**
1076    * @param row row to check
1077    * @param family column family
1078    * @param qualifier column qualifier
1079    * @param compareOp the comparison operation
1080    * @param comparator the comparator
1081    * @param put data to put if check succeeds
1082    * @throws IOException e
1083    */
1084   public boolean postCheckAndPut(final byte [] row, final byte [] family,
1085       final byte [] qualifier, final CompareOp compareOp,
1086       final ByteArrayComparable comparator, final Put put,
1087       boolean result)
1088     throws IOException {
1089     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1090     for (RegionEnvironment env: coprocessors) {
1091       if (env.getInstance() instanceof RegionObserver) {
1092         ctx = ObserverContext.createAndPrepare(env, ctx);
1093         try {
1094           result = ((RegionObserver)env.getInstance()).postCheckAndPut(ctx, row,
1095             family, qualifier, compareOp, comparator, put, result);
1096         } catch (Throwable e) {
1097           handleCoprocessorThrowable(env, e);
1098         }
1099         if (ctx.shouldComplete()) {
1100           break;
1101         }
1102       }
1103     }
1104     return result;
1105   }
1106 
1107   /**
1108    * @param row row to check
1109    * @param family column family
1110    * @param qualifier column qualifier
1111    * @param compareOp the comparison operation
1112    * @param comparator the comparator
1113    * @param delete delete to commit if check succeeds
1114    * @return true or false to return to client if default processing should
1115    * be bypassed, or null otherwise
1116    * @throws IOException e
1117    */
1118   public Boolean preCheckAndDelete(final byte [] row, final byte [] family,
1119       final byte [] qualifier, final CompareOp compareOp,
1120       final ByteArrayComparable comparator, Delete delete)
1121       throws IOException {
1122     boolean bypass = false;
1123     boolean result = false;
1124     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1125     for (RegionEnvironment env: coprocessors) {
1126       if (env.getInstance() instanceof RegionObserver) {
1127         ctx = ObserverContext.createAndPrepare(env, ctx);
1128         try {
1129           result = ((RegionObserver)env.getInstance()).preCheckAndDelete(ctx, row,
1130             family, qualifier, compareOp, comparator, delete, result);
1131         } catch (Throwable e) {
1132           handleCoprocessorThrowable(env, e);
1133         }
1134         bypass |= ctx.shouldBypass();
1135         if (ctx.shouldComplete()) {
1136           break;
1137         }
1138       }
1139     }
1140     return bypass ? result : null;
1141   }
1142 
1143   /**
1144    * @param row row to check
1145    * @param family column family
1146    * @param qualifier column qualifier
1147    * @param compareOp the comparison operation
1148    * @param comparator the comparator
1149    * @param delete delete to commit if check succeeds
1150    * @throws IOException e
1151    */
1152   public boolean postCheckAndDelete(final byte [] row, final byte [] family,
1153       final byte [] qualifier, final CompareOp compareOp,
1154       final ByteArrayComparable comparator, final Delete delete,
1155       boolean result)
1156     throws IOException {
1157     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1158     for (RegionEnvironment env: coprocessors) {
1159       if (env.getInstance() instanceof RegionObserver) {
1160         ctx = ObserverContext.createAndPrepare(env, ctx);
1161         try {
1162           result = ((RegionObserver)env.getInstance())
1163             .postCheckAndDelete(ctx, row, family, qualifier, compareOp,
1164               comparator, delete, result);
1165         } catch (Throwable e) {
1166           handleCoprocessorThrowable(env, e);
1167         }
1168         if (ctx.shouldComplete()) {
1169           break;
1170         }
1171       }
1172     }
1173     return result;
1174   }
1175 
1176   /**
1177    * @param append append object
1178    * @return result to return to client if default operation should be
1179    * bypassed, null otherwise
1180    * @throws IOException if an error occurred on the coprocessor
1181    */
1182   public Result preAppend(Append append)
1183       throws IOException {
1184     boolean bypass = false;
1185     Result result = null;
1186     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1187     for (RegionEnvironment env: coprocessors) {
1188       if (env.getInstance() instanceof RegionObserver) {
1189         ctx = ObserverContext.createAndPrepare(env, ctx);
1190         try {
1191           result = ((RegionObserver)env.getInstance()).preAppend(ctx, append);
1192         } catch (Throwable e) {
1193           handleCoprocessorThrowable(env, e);
1194         }
1195         bypass |= ctx.shouldBypass();
1196         if (ctx.shouldComplete()) {
1197           break;
1198         }
1199       }
1200     }
1201     return bypass ? result : null;
1202   }
1203 
1204   /**
1205    * @param increment increment object
1206    * @return result to return to client if default operation should be
1207    * bypassed, null otherwise
1208    * @throws IOException if an error occurred on the coprocessor
1209    */
1210   public Result preIncrement(Increment increment)
1211       throws IOException {
1212     boolean bypass = false;
1213     Result result = null;
1214     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1215     for (RegionEnvironment env: coprocessors) {
1216       if (env.getInstance() instanceof RegionObserver) {
1217         ctx = ObserverContext.createAndPrepare(env, ctx);
1218         try {
1219           result = ((RegionObserver)env.getInstance()).preIncrement(ctx, increment);
1220         } catch (Throwable e) {
1221           handleCoprocessorThrowable(env, e);
1222         }
1223         bypass |= ctx.shouldBypass();
1224         if (ctx.shouldComplete()) {
1225           break;
1226         }
1227       }
1228     }
1229     return bypass ? result : null;
1230   }
1231 
1232   /**
1233    * @param append Append object
1234    * @param result the result returned by postAppend
1235    * @throws IOException if an error occurred on the coprocessor
1236    */
1237   public void postAppend(final Append append, Result result)
1238       throws IOException {
1239     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1240     for (RegionEnvironment env: coprocessors) {
1241       if (env.getInstance() instanceof RegionObserver) {
1242         ctx = ObserverContext.createAndPrepare(env, ctx);
1243         try {
1244           ((RegionObserver)env.getInstance()).postAppend(ctx, append, result);
1245         } catch (Throwable e) {
1246           handleCoprocessorThrowable(env, e);
1247         }
1248         if (ctx.shouldComplete()) {
1249           break;
1250         }
1251       }
1252     }
1253   }
1254 
1255   /**
1256    * @param increment increment object
1257    * @param result the result returned by postIncrement
1258    * @throws IOException if an error occurred on the coprocessor
1259    */
1260   public Result postIncrement(final Increment increment, Result result)
1261       throws IOException {
1262     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1263     for (RegionEnvironment env: coprocessors) {
1264       if (env.getInstance() instanceof RegionObserver) {
1265         ctx = ObserverContext.createAndPrepare(env, ctx);
1266         try {
1267           result = ((RegionObserver)env.getInstance()).postIncrement(ctx, increment, result);
1268         } catch (Throwable e) {
1269           handleCoprocessorThrowable(env, e);
1270         }
1271         if (ctx.shouldComplete()) {
1272           break;
1273         }
1274       }
1275     }
1276     return result;
1277   }
1278 
1279   /**
1280    * @param scan the Scan specification
1281    * @return scanner id to return to client if default operation should be
1282    * bypassed, false otherwise
1283    * @exception IOException Exception
1284    */
1285   public RegionScanner preScannerOpen(Scan scan) throws IOException {
1286     boolean bypass = false;
1287     RegionScanner s = null;
1288     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1289     for (RegionEnvironment env: coprocessors) {
1290       if (env.getInstance() instanceof RegionObserver) {
1291         ctx = ObserverContext.createAndPrepare(env, ctx);
1292         try {
1293           s = ((RegionObserver)env.getInstance()).preScannerOpen(ctx, scan, s);
1294         } catch (Throwable e) {
1295           handleCoprocessorThrowable(env, e);
1296         }
1297         bypass |= ctx.shouldBypass();
1298         if (ctx.shouldComplete()) {
1299           break;
1300         }
1301       }
1302     }
1303     return bypass ? s : null;
1304   }
1305 
1306   /**
1307    * See
1308    * {@link RegionObserver#preStoreScannerOpen(ObserverContext,
1309    *    Store, Scan, NavigableSet, KeyValueScanner)}
1310    */
1311   public KeyValueScanner preStoreScannerOpen(Store store, Scan scan,
1312       final NavigableSet<byte[]> targetCols) throws IOException {
1313     KeyValueScanner s = null;
1314     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1315     for (RegionEnvironment env: coprocessors) {
1316       if (env.getInstance() instanceof RegionObserver) {
1317         ctx = ObserverContext.createAndPrepare(env, ctx);
1318         try {
1319           s = ((RegionObserver) env.getInstance()).preStoreScannerOpen(ctx, store, scan,
1320               targetCols, s);
1321         } catch (Throwable e) {
1322           handleCoprocessorThrowable(env, e);
1323         }
1324         if (ctx.shouldComplete()) {
1325           break;
1326         }
1327       }
1328     }
1329     return s;
1330   }
1331 
1332   /**
1333    * @param scan the Scan specification
1334    * @param s the scanner
1335    * @return the scanner instance to use
1336    * @exception IOException Exception
1337    */
1338   public RegionScanner postScannerOpen(final Scan scan, RegionScanner s)
1339       throws IOException {
1340     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1341     for (RegionEnvironment env: coprocessors) {
1342       if (env.getInstance() instanceof RegionObserver) {
1343         ctx = ObserverContext.createAndPrepare(env, ctx);
1344         try {
1345           s = ((RegionObserver)env.getInstance()).postScannerOpen(ctx, scan, s);
1346         } catch (Throwable e) {
1347           handleCoprocessorThrowable(env, e);
1348         }
1349         if (ctx.shouldComplete()) {
1350           break;
1351         }
1352       }
1353     }
1354     return s;
1355   }
1356 
1357   /**
1358    * @param s the scanner
1359    * @param results the result set returned by the region server
1360    * @param limit the maximum number of results to return
1361    * @return 'has next' indication to client if bypassing default behavior, or
1362    * null otherwise
1363    * @exception IOException Exception
1364    */
1365   public Boolean preScannerNext(final InternalScanner s,
1366       final List<Result> results, int limit) throws IOException {
1367     boolean bypass = false;
1368     boolean hasNext = false;
1369     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1370     for (RegionEnvironment env: coprocessors) {
1371       if (env.getInstance() instanceof RegionObserver) {
1372         ctx = ObserverContext.createAndPrepare(env, ctx);
1373         try {
1374           hasNext = ((RegionObserver)env.getInstance()).preScannerNext(ctx, s, results,
1375             limit, hasNext);
1376         } catch (Throwable e) {
1377           handleCoprocessorThrowable(env, e);
1378         }
1379         bypass |= ctx.shouldBypass();
1380         if (ctx.shouldComplete()) {
1381           break;
1382         }
1383       }
1384     }
1385     return bypass ? hasNext : null;
1386   }
1387 
1388   /**
1389    * @param s the scanner
1390    * @param results the result set returned by the region server
1391    * @param limit the maximum number of results to return
1392    * @param hasMore
1393    * @return 'has more' indication to give to client
1394    * @exception IOException Exception
1395    */
1396   public boolean postScannerNext(final InternalScanner s,
1397       final List<Result> results, final int limit, boolean hasMore)
1398       throws IOException {
1399     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1400     for (RegionEnvironment env: coprocessors) {
1401       if (env.getInstance() instanceof RegionObserver) {
1402         ctx = ObserverContext.createAndPrepare(env, ctx);
1403         try {
1404           hasMore = ((RegionObserver)env.getInstance()).postScannerNext(ctx, s,
1405             results, limit, hasMore);
1406         } catch (Throwable e) {
1407           handleCoprocessorThrowable(env, e);
1408         }
1409         if (ctx.shouldComplete()) {
1410           break;
1411         }
1412       }
1413     }
1414     return hasMore;
1415   }
1416 
1417   /**
1418    * This will be called by the scan flow when the current scanned row is being filtered out by the
1419    * filter.
1420    * @param s the scanner
1421    * @param currentRow The current rowkey which got filtered out
1422    * @return whether more rows are available for the scanner or not
1423    * @throws IOException
1424    */
1425   public boolean postScannerFilterRow(final InternalScanner s, final byte[] currentRow)
1426       throws IOException {
1427     boolean hasMore = true; // By default assume more rows there.
1428     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1429     for (RegionEnvironment env : coprocessors) {
1430       if (env.getInstance() instanceof RegionObserver) {
1431         ctx = ObserverContext.createAndPrepare(env, ctx);
1432         try {
1433           hasMore = ((RegionObserver) env.getInstance()).postScannerFilterRow(ctx, s, currentRow,
1434               hasMore);
1435         } catch (Throwable e) {
1436           handleCoprocessorThrowable(env, e);
1437         }
1438         if (ctx.shouldComplete()) {
1439           break;
1440         }
1441       }
1442     }
1443     return hasMore;
1444   }
1445   
1446   /**
1447    * @param s the scanner
1448    * @return true if default behavior should be bypassed, false otherwise
1449    * @exception IOException Exception
1450    */
1451   public boolean preScannerClose(final InternalScanner s)
1452       throws IOException {
1453     boolean bypass = false;
1454     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1455     for (RegionEnvironment env: coprocessors) {
1456       if (env.getInstance() instanceof RegionObserver) {
1457         ctx = ObserverContext.createAndPrepare(env, ctx);
1458         try {
1459           ((RegionObserver)env.getInstance()).preScannerClose(ctx, s);
1460         } catch (Throwable e) {
1461           handleCoprocessorThrowable(env, e);
1462         }
1463         bypass |= ctx.shouldBypass();
1464         if (ctx.shouldComplete()) {
1465           break;
1466         }
1467       }
1468     }
1469     return bypass;
1470   }
1471 
1472   /**
1473    * @param s the scanner
1474    * @exception IOException Exception
1475    */
1476   public void postScannerClose(final InternalScanner s)
1477       throws IOException {
1478     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1479     for (RegionEnvironment env: coprocessors) {
1480       if (env.getInstance() instanceof RegionObserver) {
1481         ctx = ObserverContext.createAndPrepare(env, ctx);
1482         try {
1483           ((RegionObserver)env.getInstance()).postScannerClose(ctx, s);
1484         } catch (Throwable e) {
1485           handleCoprocessorThrowable(env, e);
1486         }
1487         if (ctx.shouldComplete()) {
1488           break;
1489         }
1490       }
1491     }
1492   }
1493 
1494   /**
1495    * @param info
1496    * @param logKey
1497    * @param logEdit
1498    * @return true if default behavior should be bypassed, false otherwise
1499    * @throws IOException
1500    */
1501   public boolean preWALRestore(HRegionInfo info, HLogKey logKey,
1502       WALEdit logEdit) throws IOException {
1503     boolean bypass = false;
1504     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1505     for (RegionEnvironment env: coprocessors) {
1506       if (env.getInstance() instanceof RegionObserver) {
1507         ctx = ObserverContext.createAndPrepare(env, ctx);
1508         try {
1509           ((RegionObserver)env.getInstance()).preWALRestore(ctx, info, logKey,
1510               logEdit);
1511         } catch (Throwable e) {
1512           handleCoprocessorThrowable(env, e);
1513         }
1514         bypass |= ctx.shouldBypass();
1515         if (ctx.shouldComplete()) {
1516           break;
1517         }
1518       }
1519     }
1520     return bypass;
1521   }
1522 
1523   /**
1524    * @param info
1525    * @param logKey
1526    * @param logEdit
1527    * @throws IOException
1528    */
1529   public void postWALRestore(HRegionInfo info, HLogKey logKey,
1530       WALEdit logEdit) throws IOException {
1531     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1532     for (RegionEnvironment env: coprocessors) {
1533       if (env.getInstance() instanceof RegionObserver) {
1534         ctx = ObserverContext.createAndPrepare(env, ctx);
1535         try {
1536           ((RegionObserver)env.getInstance()).postWALRestore(ctx, info,
1537               logKey, logEdit);
1538         } catch (Throwable e) {
1539           handleCoprocessorThrowable(env, e);
1540         }
1541         if (ctx.shouldComplete()) {
1542           break;
1543         }
1544       }
1545     }
1546   }
1547 
1548   /**
1549    * @param familyPaths pairs of { CF, file path } submitted for bulk load
1550    * @return true if the default operation should be bypassed
1551    * @throws IOException
1552    */
1553   public boolean preBulkLoadHFile(List<Pair<byte[], String>> familyPaths) throws IOException {
1554     boolean bypass = false;
1555     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1556     for (RegionEnvironment env: coprocessors) {
1557       if (env.getInstance() instanceof RegionObserver) {
1558         ctx = ObserverContext.createAndPrepare(env, ctx);
1559         try {
1560           ((RegionObserver)env.getInstance()).preBulkLoadHFile(ctx, familyPaths);
1561         } catch (Throwable e) {
1562           handleCoprocessorThrowable(env, e);
1563         }
1564         bypass |= ctx.shouldBypass();
1565         if (ctx.shouldComplete()) {
1566           break;
1567         }
1568       }
1569     }
1570 
1571     return bypass;
1572   }
1573 
1574   /**
1575    * @param familyPaths pairs of { CF, file path } submitted for bulk load
1576    * @param hasLoaded whether load was successful or not
1577    * @return the possibly modified value of hasLoaded
1578    * @throws IOException
1579    */
1580   public boolean postBulkLoadHFile(List<Pair<byte[], String>> familyPaths, boolean hasLoaded)
1581       throws IOException {
1582     ObserverContext<RegionCoprocessorEnvironment> ctx = null;
1583     for (RegionEnvironment env: coprocessors) {
1584       if (env.getInstance() instanceof RegionObserver) {
1585         ctx = ObserverContext.createAndPrepare(env, ctx);
1586         try {
1587           hasLoaded = ((RegionObserver)env.getInstance()).postBulkLoadHFile(ctx,
1588             familyPaths, hasLoaded);
1589         } catch (Throwable e) {
1590           handleCoprocessorThrowable(env, e);
1591         }
1592         if (ctx.shouldComplete()) {
1593           break;
1594         }
1595       }
1596     }
1597 
1598     return hasLoaded;
1599   }
1600 
1601 }