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