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