001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.regionserver;
019
020import com.google.protobuf.Message;
021import com.google.protobuf.Service;
022import java.io.IOException;
023import java.lang.reflect.InvocationTargetException;
024import java.util.ArrayList;
025import java.util.List;
026import java.util.Map;
027import java.util.UUID;
028import java.util.concurrent.ConcurrentHashMap;
029import java.util.concurrent.ConcurrentMap;
030import java.util.stream.Collectors;
031import org.apache.hadoop.conf.Configuration;
032import org.apache.hadoop.fs.FileSystem;
033import org.apache.hadoop.fs.Path;
034import org.apache.hadoop.hbase.Cell;
035import org.apache.hadoop.hbase.HBaseConfiguration;
036import org.apache.hadoop.hbase.HConstants;
037import org.apache.hadoop.hbase.RawCellBuilder;
038import org.apache.hadoop.hbase.RawCellBuilderFactory;
039import org.apache.hadoop.hbase.ServerName;
040import org.apache.hadoop.hbase.SharedConnection;
041import org.apache.hadoop.hbase.client.Append;
042import org.apache.hadoop.hbase.client.CheckAndMutate;
043import org.apache.hadoop.hbase.client.CheckAndMutateResult;
044import org.apache.hadoop.hbase.client.Connection;
045import org.apache.hadoop.hbase.client.Delete;
046import org.apache.hadoop.hbase.client.Get;
047import org.apache.hadoop.hbase.client.Increment;
048import org.apache.hadoop.hbase.client.Mutation;
049import org.apache.hadoop.hbase.client.Put;
050import org.apache.hadoop.hbase.client.RegionInfo;
051import org.apache.hadoop.hbase.client.Result;
052import org.apache.hadoop.hbase.client.Scan;
053import org.apache.hadoop.hbase.client.TableDescriptor;
054import org.apache.hadoop.hbase.coprocessor.BaseEnvironment;
055import org.apache.hadoop.hbase.coprocessor.BulkLoadObserver;
056import org.apache.hadoop.hbase.coprocessor.CoprocessorException;
057import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
058import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
059import org.apache.hadoop.hbase.coprocessor.CoprocessorServiceBackwardCompatiblity;
060import org.apache.hadoop.hbase.coprocessor.CoreCoprocessor;
061import org.apache.hadoop.hbase.coprocessor.EndpointObserver;
062import org.apache.hadoop.hbase.coprocessor.HasRegionServerServices;
063import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
064import org.apache.hadoop.hbase.coprocessor.ObserverContext;
065import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor;
066import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
067import org.apache.hadoop.hbase.coprocessor.RegionObserver;
068import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
069import org.apache.hadoop.hbase.io.Reference;
070import org.apache.hadoop.hbase.io.hfile.CacheConfig;
071import org.apache.hadoop.hbase.metrics.MetricRegistry;
072import org.apache.hadoop.hbase.regionserver.Region.Operation;
073import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker;
074import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
075import org.apache.hadoop.hbase.regionserver.querymatcher.DeleteTracker;
076import org.apache.hadoop.hbase.security.User;
077import org.apache.hadoop.hbase.util.CoprocessorClassLoader;
078import org.apache.hadoop.hbase.util.Pair;
079import org.apache.hadoop.hbase.wal.WALEdit;
080import org.apache.hadoop.hbase.wal.WALKey;
081import org.apache.yetus.audience.InterfaceAudience;
082import org.slf4j.Logger;
083import org.slf4j.LoggerFactory;
084
085import org.apache.hbase.thirdparty.org.apache.commons.collections4.map.AbstractReferenceMap;
086import org.apache.hbase.thirdparty.org.apache.commons.collections4.map.ReferenceMap;
087
088/**
089 * Implements the coprocessor environment and runtime support for coprocessors loaded within a
090 * {@link Region}.
091 */
092@InterfaceAudience.Private
093public class RegionCoprocessorHost
094  extends CoprocessorHost<RegionCoprocessor, RegionCoprocessorEnvironment> {
095
096  private static final Logger LOG = LoggerFactory.getLogger(RegionCoprocessorHost.class);
097  // The shared data map
098  private static final ReferenceMap<String, ConcurrentMap<String, Object>> SHARED_DATA_MAP =
099    new ReferenceMap<>(AbstractReferenceMap.ReferenceStrength.HARD,
100      AbstractReferenceMap.ReferenceStrength.WEAK);
101
102  // optimization: no need to call postScannerFilterRow, if no coprocessor implements it
103  private final boolean hasCustomPostScannerFilterRow;
104
105  /*
106   * Whether any configured CPs override postScannerFilterRow hook
107   */
108  public boolean hasCustomPostScannerFilterRow() {
109    return hasCustomPostScannerFilterRow;
110  }
111
112  /**
113   * Encapsulation of the environment of each coprocessor
114   */
115  private static class RegionEnvironment extends BaseEnvironment<RegionCoprocessor>
116    implements RegionCoprocessorEnvironment {
117    private Region region;
118    ConcurrentMap<String, Object> sharedData;
119    private final MetricRegistry metricRegistry;
120    private final RegionServerServices services;
121
122    /**
123     * Constructor
124     * @param impl     the coprocessor instance
125     * @param priority chaining priority
126     */
127    public RegionEnvironment(final RegionCoprocessor impl, final int priority, final int seq,
128      final Configuration conf, final Region region, final RegionServerServices services,
129      final ConcurrentMap<String, Object> sharedData) {
130      super(impl, priority, seq, conf);
131      this.region = region;
132      this.sharedData = sharedData;
133      this.services = services;
134      this.metricRegistry =
135        MetricsCoprocessor.createRegistryForRegionCoprocessor(impl.getClass().getName());
136    }
137
138    /** Returns the region */
139    @Override
140    public Region getRegion() {
141      return region;
142    }
143
144    @Override
145    public OnlineRegions getOnlineRegions() {
146      return this.services;
147    }
148
149    @Override
150    public Connection getConnection() {
151      // Mocks may have services as null at test time.
152      return services != null ? new SharedConnection(services.getConnection()) : null;
153    }
154
155    @Override
156    public Connection createConnection(Configuration conf) throws IOException {
157      return services != null ? this.services.createConnection(conf) : null;
158    }
159
160    @Override
161    public ServerName getServerName() {
162      return services != null ? services.getServerName() : null;
163    }
164
165    @Override
166    public void shutdown() {
167      super.shutdown();
168      MetricsCoprocessor.removeRegistry(this.metricRegistry);
169    }
170
171    @Override
172    public ConcurrentMap<String, Object> getSharedData() {
173      return sharedData;
174    }
175
176    @Override
177    public RegionInfo getRegionInfo() {
178      return region.getRegionInfo();
179    }
180
181    @Override
182    public MetricRegistry getMetricRegistryForRegionServer() {
183      return metricRegistry;
184    }
185
186    @Override
187    public RawCellBuilder getCellBuilder() {
188      // We always do a DEEP_COPY only
189      return RawCellBuilderFactory.create();
190    }
191  }
192
193  /**
194   * Special version of RegionEnvironment that exposes RegionServerServices for Core Coprocessors
195   * only. Temporary hack until Core Coprocessors are integrated into Core.
196   */
197  private static class RegionEnvironmentForCoreCoprocessors extends RegionEnvironment
198    implements HasRegionServerServices {
199    private final RegionServerServices rsServices;
200
201    public RegionEnvironmentForCoreCoprocessors(final RegionCoprocessor impl, final int priority,
202      final int seq, final Configuration conf, final Region region,
203      final RegionServerServices services, final ConcurrentMap<String, Object> sharedData) {
204      super(impl, priority, seq, conf, region, services, sharedData);
205      this.rsServices = services;
206    }
207
208    /**
209     * @return An instance of RegionServerServices, an object NOT for general user-space Coprocessor
210     *         consumption.
211     */
212    @Override
213    public RegionServerServices getRegionServerServices() {
214      return this.rsServices;
215    }
216  }
217
218  static class TableCoprocessorAttribute {
219    private Path path;
220    private String className;
221    private int priority;
222    private Configuration conf;
223
224    public TableCoprocessorAttribute(Path path, String className, int priority,
225      Configuration conf) {
226      this.path = path;
227      this.className = className;
228      this.priority = priority;
229      this.conf = conf;
230    }
231
232    public Path getPath() {
233      return path;
234    }
235
236    public String getClassName() {
237      return className;
238    }
239
240    public int getPriority() {
241      return priority;
242    }
243
244    public Configuration getConf() {
245      return conf;
246    }
247  }
248
249  /** The region server services */
250  RegionServerServices rsServices;
251  /** The region */
252  HRegion region;
253
254  /**
255   * Constructor
256   * @param region     the region
257   * @param rsServices interface to available region server functionality
258   * @param conf       the configuration
259   */
260  @SuppressWarnings("ReturnValueIgnored") // Checking method exists as CPU optimization
261  public RegionCoprocessorHost(final HRegion region, final RegionServerServices rsServices,
262    final Configuration conf) {
263    super(rsServices);
264    this.conf = conf;
265    this.rsServices = rsServices;
266    this.region = region;
267    this.pathPrefix = Integer.toString(this.region.getRegionInfo().hashCode());
268
269    // load system default cp's from configuration.
270    loadSystemCoprocessors(conf, REGION_COPROCESSOR_CONF_KEY);
271
272    // load system default cp's for user tables from configuration.
273    if (!region.getRegionInfo().getTable().isSystemTable()) {
274      loadSystemCoprocessors(conf, USER_REGION_COPROCESSOR_CONF_KEY);
275    }
276
277    // load Coprocessor From HDFS
278    loadTableCoprocessors(conf);
279
280    // now check whether any coprocessor implements postScannerFilterRow
281    boolean hasCustomPostScannerFilterRow = false;
282    out: for (RegionCoprocessorEnvironment env : coprocEnvironments) {
283      if (env.getInstance() instanceof RegionObserver) {
284        Class<?> clazz = env.getInstance().getClass();
285        for (;;) {
286          if (clazz == Object.class) {
287            // we dont need to look postScannerFilterRow into Object class
288            break; // break the inner loop
289          }
290          try {
291            clazz.getDeclaredMethod("postScannerFilterRow", ObserverContext.class,
292              InternalScanner.class, Cell.class, boolean.class);
293            // this coprocessor has a custom version of postScannerFilterRow
294            hasCustomPostScannerFilterRow = true;
295            break out;
296          } catch (NoSuchMethodException ignore) {
297          }
298          // the deprecated signature still exists
299          try {
300            clazz.getDeclaredMethod("postScannerFilterRow", ObserverContext.class,
301              InternalScanner.class, byte[].class, int.class, short.class, boolean.class);
302            // this coprocessor has a custom version of postScannerFilterRow
303            hasCustomPostScannerFilterRow = true;
304            break out;
305          } catch (NoSuchMethodException ignore) {
306          }
307          clazz = clazz.getSuperclass();
308        }
309      }
310    }
311    this.hasCustomPostScannerFilterRow = hasCustomPostScannerFilterRow;
312  }
313
314  static List<TableCoprocessorAttribute> getTableCoprocessorAttrsFromSchema(Configuration conf,
315    TableDescriptor htd) {
316    return htd.getCoprocessorDescriptors().stream().map(cp -> {
317      Path path = cp.getJarPath().map(p -> new Path(p)).orElse(null);
318      Configuration ourConf;
319      if (!cp.getProperties().isEmpty()) {
320        // do an explicit deep copy of the passed configuration
321        ourConf = new Configuration(false);
322        HBaseConfiguration.merge(ourConf, conf);
323        cp.getProperties().forEach((k, v) -> ourConf.set(k, v));
324      } else {
325        ourConf = conf;
326      }
327      return new TableCoprocessorAttribute(path, cp.getClassName(), cp.getPriority(), ourConf);
328    }).collect(Collectors.toList());
329  }
330
331  /**
332   * Sanity check the table coprocessor attributes of the supplied schema. Will throw an exception
333   * if there is a problem.
334   */
335  public static void testTableCoprocessorAttrs(final Configuration conf, final TableDescriptor htd)
336    throws IOException {
337    String pathPrefix = UUID.randomUUID().toString();
338    for (TableCoprocessorAttribute attr : getTableCoprocessorAttrsFromSchema(conf, htd)) {
339      if (attr.getPriority() < 0) {
340        throw new IOException(
341          "Priority for coprocessor " + attr.getClassName() + " cannot be less than 0");
342      }
343      ClassLoader old = Thread.currentThread().getContextClassLoader();
344      try {
345        ClassLoader cl;
346        if (attr.getPath() != null) {
347          cl = CoprocessorClassLoader.getClassLoader(attr.getPath(),
348            CoprocessorHost.class.getClassLoader(), pathPrefix, conf);
349        } else {
350          cl = CoprocessorHost.class.getClassLoader();
351        }
352        Thread.currentThread().setContextClassLoader(cl);
353        if (cl instanceof CoprocessorClassLoader) {
354          String[] includedClassPrefixes = null;
355          if (conf.get(HConstants.CP_HTD_ATTR_INCLUSION_KEY) != null) {
356            String prefixes = attr.conf.get(HConstants.CP_HTD_ATTR_INCLUSION_KEY);
357            includedClassPrefixes = prefixes.split(";");
358          }
359          ((CoprocessorClassLoader) cl).loadClass(attr.getClassName(), includedClassPrefixes);
360        } else {
361          cl.loadClass(attr.getClassName());
362        }
363      } catch (ClassNotFoundException e) {
364        throw new IOException("Class " + attr.getClassName() + " cannot be loaded", e);
365      } finally {
366        Thread.currentThread().setContextClassLoader(old);
367      }
368    }
369  }
370
371  void loadTableCoprocessors(final Configuration conf) {
372    boolean coprocessorsEnabled =
373      conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY, DEFAULT_COPROCESSORS_ENABLED);
374    boolean tableCoprocessorsEnabled =
375      conf.getBoolean(USER_COPROCESSORS_ENABLED_CONF_KEY, DEFAULT_USER_COPROCESSORS_ENABLED);
376    if (!(coprocessorsEnabled && tableCoprocessorsEnabled)) {
377      return;
378    }
379
380    // scan the table attributes for coprocessor load specifications
381    // initialize the coprocessors
382    List<RegionCoprocessorEnvironment> configured = new ArrayList<>();
383    for (TableCoprocessorAttribute attr : getTableCoprocessorAttrsFromSchema(conf,
384      region.getTableDescriptor())) {
385      // Load encompasses classloading and coprocessor initialization
386      try {
387        RegionCoprocessorEnvironment env =
388          load(attr.getPath(), attr.getClassName(), attr.getPriority(), attr.getConf());
389        if (env == null) {
390          continue;
391        }
392        configured.add(env);
393        LOG.info("Loaded coprocessor " + attr.getClassName() + " from HTD of "
394          + region.getTableDescriptor().getTableName().getNameAsString() + " successfully.");
395      } catch (Throwable t) {
396        // Coprocessor failed to load, do we abort on error?
397        if (conf.getBoolean(ABORT_ON_ERROR_KEY, DEFAULT_ABORT_ON_ERROR)) {
398          abortServer(attr.getClassName(), t);
399        } else {
400          LOG.error("Failed to load coprocessor " + attr.getClassName(), t);
401        }
402      }
403    }
404    // add together to coprocessor set for COW efficiency
405    coprocEnvironments.addAll(configured);
406  }
407
408  @Override
409  public RegionEnvironment createEnvironment(RegionCoprocessor instance, int priority, int seq,
410    Configuration conf) {
411    // If coprocessor exposes any services, register them.
412    for (Service service : instance.getServices()) {
413      region.registerService(service);
414    }
415    ConcurrentMap<String, Object> classData;
416    // make sure only one thread can add maps
417    synchronized (SHARED_DATA_MAP) {
418      // as long as at least one RegionEnvironment holds on to its classData it will
419      // remain in this map
420      classData = SHARED_DATA_MAP.computeIfAbsent(instance.getClass().getName(),
421        k -> new ConcurrentHashMap<>());
422    }
423    // If a CoreCoprocessor, return a 'richer' environment, one laden with RegionServerServices.
424    return instance.getClass().isAnnotationPresent(CoreCoprocessor.class)
425      ? new RegionEnvironmentForCoreCoprocessors(instance, priority, seq, conf, region, rsServices,
426        classData)
427      : new RegionEnvironment(instance, priority, seq, conf, region, rsServices, classData);
428  }
429
430  @Override
431  public RegionCoprocessor checkAndGetInstance(Class<?> implClass)
432    throws InstantiationException, IllegalAccessException {
433    try {
434      if (RegionCoprocessor.class.isAssignableFrom(implClass)) {
435        return implClass.asSubclass(RegionCoprocessor.class).getDeclaredConstructor().newInstance();
436      } else if (CoprocessorService.class.isAssignableFrom(implClass)) {
437        // For backward compatibility with old CoprocessorService impl which don't extend
438        // RegionCoprocessor.
439        CoprocessorService cs;
440        cs = implClass.asSubclass(CoprocessorService.class).getDeclaredConstructor().newInstance();
441        return new CoprocessorServiceBackwardCompatiblity.RegionCoprocessorService(cs);
442      } else {
443        LOG.error("{} is not of type RegionCoprocessor. Check the configuration of {}",
444          implClass.getName(), CoprocessorHost.REGION_COPROCESSOR_CONF_KEY);
445        return null;
446      }
447    } catch (NoSuchMethodException | InvocationTargetException e) {
448      throw (InstantiationException) new InstantiationException(implClass.getName()).initCause(e);
449    }
450  }
451
452  private ObserverGetter<RegionCoprocessor, RegionObserver> regionObserverGetter =
453    RegionCoprocessor::getRegionObserver;
454
455  private ObserverGetter<RegionCoprocessor, EndpointObserver> endpointObserverGetter =
456    RegionCoprocessor::getEndpointObserver;
457
458  abstract class RegionObserverOperationWithoutResult
459    extends ObserverOperationWithoutResult<RegionObserver> {
460    public RegionObserverOperationWithoutResult() {
461      super(regionObserverGetter);
462    }
463
464    public RegionObserverOperationWithoutResult(User user) {
465      super(regionObserverGetter, user);
466    }
467
468    public RegionObserverOperationWithoutResult(boolean bypassable) {
469      super(regionObserverGetter, null, bypassable);
470    }
471
472    public RegionObserverOperationWithoutResult(User user, boolean bypassable) {
473      super(regionObserverGetter, user, bypassable);
474    }
475  }
476
477  abstract class BulkLoadObserverOperation
478    extends ObserverOperationWithoutResult<BulkLoadObserver> {
479    public BulkLoadObserverOperation(User user) {
480      super(RegionCoprocessor::getBulkLoadObserver, user);
481    }
482  }
483
484  //////////////////////////////////////////////////////////////////////////////////////////////////
485  // Observer operations
486  //////////////////////////////////////////////////////////////////////////////////////////////////
487
488  //////////////////////////////////////////////////////////////////////////////////////////////////
489  // Observer operations
490  //////////////////////////////////////////////////////////////////////////////////////////////////
491
492  /**
493   * Invoked before a region open.
494   * @throws IOException Signals that an I/O exception has occurred.
495   */
496  public void preOpen() throws IOException {
497    if (coprocEnvironments.isEmpty()) {
498      return;
499    }
500    execOperation(new RegionObserverOperationWithoutResult() {
501      @Override
502      public void call(RegionObserver observer) throws IOException {
503        observer.preOpen(this);
504      }
505    });
506  }
507
508  /**
509   * Invoked after a region open
510   */
511  public void postOpen() {
512    if (coprocEnvironments.isEmpty()) {
513      return;
514    }
515    try {
516      execOperation(new RegionObserverOperationWithoutResult() {
517        @Override
518        public void call(RegionObserver observer) throws IOException {
519          observer.postOpen(this);
520        }
521      });
522    } catch (IOException e) {
523      LOG.warn(e.toString(), e);
524    }
525  }
526
527  /**
528   * Invoked before a region is closed
529   * @param abortRequested true if the server is aborting
530   */
531  public void preClose(final boolean abortRequested) throws IOException {
532    execOperation(new RegionObserverOperationWithoutResult() {
533      @Override
534      public void call(RegionObserver observer) throws IOException {
535        observer.preClose(this, abortRequested);
536      }
537    });
538  }
539
540  /**
541   * Invoked after a region is closed
542   * @param abortRequested true if the server is aborting
543   */
544  public void postClose(final boolean abortRequested) {
545    try {
546      execOperation(new RegionObserverOperationWithoutResult() {
547        @Override
548        public void call(RegionObserver observer) throws IOException {
549          observer.postClose(this, abortRequested);
550        }
551
552        @Override
553        public void postEnvCall() {
554          shutdown(this.getEnvironment());
555        }
556      });
557    } catch (IOException e) {
558      LOG.warn(e.toString(), e);
559    }
560  }
561
562  /**
563   * Called prior to selecting the {@link HStoreFile}s for compaction from the list of currently
564   * available candidates.
565   * <p>
566   * Supports Coprocessor 'bypass' -- 'bypass' is how this method indicates that it changed the
567   * passed in <code>candidates</code>.
568   * @param store      The store where compaction is being requested
569   * @param candidates The currently available store files
570   * @param tracker    used to track the life cycle of a compaction
571   * @param user       the user
572   */
573  public boolean preCompactSelection(final HStore store, final List<HStoreFile> candidates,
574    final CompactionLifeCycleTracker tracker, final User user) throws IOException {
575    if (coprocEnvironments.isEmpty()) {
576      return false;
577    }
578    boolean bypassable = true;
579    return execOperation(new RegionObserverOperationWithoutResult(user, bypassable) {
580      @Override
581      public void call(RegionObserver observer) throws IOException {
582        observer.preCompactSelection(this, store, candidates, tracker);
583      }
584    });
585  }
586
587  /**
588   * Called after the {@link HStoreFile}s to be compacted have been selected from the available
589   * candidates.
590   * @param store    The store where compaction is being requested
591   * @param selected The store files selected to compact
592   * @param tracker  used to track the life cycle of a compaction
593   * @param request  the compaction request
594   * @param user     the user
595   */
596  public void postCompactSelection(final HStore store, final List<HStoreFile> selected,
597    final CompactionLifeCycleTracker tracker, final CompactionRequest request, final User user)
598    throws IOException {
599    if (coprocEnvironments.isEmpty()) {
600      return;
601    }
602    execOperation(new RegionObserverOperationWithoutResult(user) {
603      @Override
604      public void call(RegionObserver observer) throws IOException {
605        observer.postCompactSelection(this, store, selected, tracker, request);
606      }
607    });
608  }
609
610  /**
611   * Called prior to opening store scanner for compaction.
612   */
613  public ScanInfo preCompactScannerOpen(HStore store, ScanType scanType,
614    CompactionLifeCycleTracker tracker, CompactionRequest request, User user) throws IOException {
615    if (coprocEnvironments.isEmpty()) {
616      return store.getScanInfo();
617    }
618    CustomizedScanInfoBuilder builder = new CustomizedScanInfoBuilder(store.getScanInfo());
619    execOperation(new RegionObserverOperationWithoutResult(user) {
620      @Override
621      public void call(RegionObserver observer) throws IOException {
622        observer.preCompactScannerOpen(this, store, scanType, builder, tracker, request);
623      }
624    });
625    return builder.build();
626  }
627
628  /**
629   * Called prior to rewriting the store files selected for compaction
630   * @param store    the store being compacted
631   * @param scanner  the scanner used to read store data during compaction
632   * @param scanType type of Scan
633   * @param tracker  used to track the life cycle of a compaction
634   * @param request  the compaction request
635   * @param user     the user
636   * @return Scanner to use (cannot be null!)
637   */
638  public InternalScanner preCompact(final HStore store, final InternalScanner scanner,
639    final ScanType scanType, final CompactionLifeCycleTracker tracker,
640    final CompactionRequest request, final User user) throws IOException {
641    InternalScanner defaultResult = scanner;
642    if (coprocEnvironments.isEmpty()) {
643      return defaultResult;
644    }
645    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, InternalScanner>(
646      regionObserverGetter, defaultResult, user) {
647      @Override
648      public InternalScanner call(RegionObserver observer) throws IOException {
649        InternalScanner scanner =
650          observer.preCompact(this, store, getResult(), scanType, tracker, request);
651        if (scanner == null) {
652          throw new CoprocessorException("Null Scanner return disallowed!");
653        }
654        return scanner;
655      }
656    });
657  }
658
659  /**
660   * Called after the store compaction has completed.
661   * @param store      the store being compacted
662   * @param resultFile the new store file written during compaction
663   * @param tracker    used to track the life cycle of a compaction
664   * @param request    the compaction request
665   * @param user       the user
666   */
667  public void postCompact(final HStore store, final HStoreFile resultFile,
668    final CompactionLifeCycleTracker tracker, final CompactionRequest request, final User user)
669    throws IOException {
670    execOperation(
671      coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult(user) {
672        @Override
673        public void call(RegionObserver observer) throws IOException {
674          observer.postCompact(this, store, resultFile, tracker, request);
675        }
676      });
677  }
678
679  /**
680   * Invoked before create StoreScanner for flush.
681   */
682  public ScanInfo preFlushScannerOpen(HStore store, FlushLifeCycleTracker tracker)
683    throws IOException {
684    if (coprocEnvironments.isEmpty()) {
685      return store.getScanInfo();
686    }
687    CustomizedScanInfoBuilder builder = new CustomizedScanInfoBuilder(store.getScanInfo());
688    execOperation(new RegionObserverOperationWithoutResult() {
689      @Override
690      public void call(RegionObserver observer) throws IOException {
691        observer.preFlushScannerOpen(this, store, builder, tracker);
692      }
693    });
694    return builder.build();
695  }
696
697  /**
698   * Invoked before a memstore flush
699   * @return Scanner to use (cannot be null!)
700   */
701  public InternalScanner preFlush(HStore store, InternalScanner scanner,
702    FlushLifeCycleTracker tracker) throws IOException {
703    if (coprocEnvironments.isEmpty()) {
704      return scanner;
705    }
706    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, InternalScanner>(
707      regionObserverGetter, scanner) {
708      @Override
709      public InternalScanner call(RegionObserver observer) throws IOException {
710        InternalScanner scanner = observer.preFlush(this, store, getResult(), tracker);
711        if (scanner == null) {
712          throw new CoprocessorException("Null Scanner return disallowed!");
713        }
714        return scanner;
715      }
716    });
717  }
718
719  /**
720   * Invoked before a memstore flush
721   */
722  public void preFlush(FlushLifeCycleTracker tracker) throws IOException {
723    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
724      @Override
725      public void call(RegionObserver observer) throws IOException {
726        observer.preFlush(this, tracker);
727      }
728    });
729  }
730
731  /**
732   * Invoked after a memstore flush
733   */
734  public void postFlush(FlushLifeCycleTracker tracker) throws IOException {
735    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
736      @Override
737      public void call(RegionObserver observer) throws IOException {
738        observer.postFlush(this, tracker);
739      }
740    });
741  }
742
743  /**
744   * Invoked before in memory compaction.
745   */
746  public void preMemStoreCompaction(HStore store) throws IOException {
747    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
748      @Override
749      public void call(RegionObserver observer) throws IOException {
750        observer.preMemStoreCompaction(this, store);
751      }
752    });
753  }
754
755  /**
756   * Invoked before create StoreScanner for in memory compaction.
757   */
758  public ScanInfo preMemStoreCompactionCompactScannerOpen(HStore store) throws IOException {
759    CustomizedScanInfoBuilder builder = new CustomizedScanInfoBuilder(store.getScanInfo());
760    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
761      @Override
762      public void call(RegionObserver observer) throws IOException {
763        observer.preMemStoreCompactionCompactScannerOpen(this, store, builder);
764      }
765    });
766    return builder.build();
767  }
768
769  /**
770   * Invoked before compacting memstore.
771   */
772  public InternalScanner preMemStoreCompactionCompact(HStore store, InternalScanner scanner)
773    throws IOException {
774    if (coprocEnvironments.isEmpty()) {
775      return scanner;
776    }
777    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, InternalScanner>(
778      regionObserverGetter, scanner) {
779      @Override
780      public InternalScanner call(RegionObserver observer) throws IOException {
781        return observer.preMemStoreCompactionCompact(this, store, getResult());
782      }
783    });
784  }
785
786  /**
787   * Invoked after in memory compaction.
788   */
789  public void postMemStoreCompaction(HStore store) throws IOException {
790    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
791      @Override
792      public void call(RegionObserver observer) throws IOException {
793        observer.postMemStoreCompaction(this, store);
794      }
795    });
796  }
797
798  /**
799   * Invoked after a memstore flush
800   */
801  public void postFlush(HStore store, HStoreFile storeFile, FlushLifeCycleTracker tracker)
802    throws IOException {
803    if (coprocEnvironments.isEmpty()) {
804      return;
805    }
806    execOperation(new RegionObserverOperationWithoutResult() {
807      @Override
808      public void call(RegionObserver observer) throws IOException {
809        observer.postFlush(this, store, storeFile, tracker);
810      }
811    });
812  }
813
814  // RegionObserver support
815  /**
816   * Supports Coprocessor 'bypass'.
817   * @param get     the Get request
818   * @param results What to return if return is true/'bypass'.
819   * @return true if default processing should be bypassed.
820   * @exception IOException Exception
821   */
822  public boolean preGet(final Get get, final List<Cell> results) throws IOException {
823    if (coprocEnvironments.isEmpty()) {
824      return false;
825    }
826    boolean bypassable = true;
827    return execOperation(new RegionObserverOperationWithoutResult(bypassable) {
828      @Override
829      public void call(RegionObserver observer) throws IOException {
830        observer.preGetOp(this, get, results);
831      }
832    });
833  }
834
835  /**
836   * @param get     the Get request
837   * @param results the result set
838   * @exception IOException Exception
839   */
840  public void postGet(final Get get, final List<Cell> results) throws IOException {
841    if (coprocEnvironments.isEmpty()) {
842      return;
843    }
844    execOperation(new RegionObserverOperationWithoutResult() {
845      @Override
846      public void call(RegionObserver observer) throws IOException {
847        observer.postGetOp(this, get, results);
848      }
849    });
850  }
851
852  /**
853   * Supports Coprocessor 'bypass'.
854   * @param get the Get request
855   * @return true or false to return to client if bypassing normal operation, or null otherwise
856   * @exception IOException Exception
857   */
858  public Boolean preExists(final Get get) throws IOException {
859    boolean bypassable = true;
860    boolean defaultResult = false;
861    if (coprocEnvironments.isEmpty()) {
862      return null;
863    }
864    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Boolean>(
865      regionObserverGetter, defaultResult, bypassable) {
866      @Override
867      public Boolean call(RegionObserver observer) throws IOException {
868        return observer.preExists(this, get, getResult());
869      }
870    });
871  }
872
873  /**
874   * @param get    the Get request
875   * @param result the result returned by the region server
876   * @return the result to return to the client
877   * @exception IOException Exception
878   */
879  public boolean postExists(final Get get, boolean result) throws IOException {
880    if (this.coprocEnvironments.isEmpty()) {
881      return result;
882    }
883    return execOperationWithResult(
884      new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, result) {
885        @Override
886        public Boolean call(RegionObserver observer) throws IOException {
887          return observer.postExists(this, get, getResult());
888        }
889      });
890  }
891
892  /**
893   * Supports Coprocessor 'bypass'.
894   * @param put  The Put object
895   * @param edit The WALEdit object.
896   * @return true if default processing should be bypassed
897   * @exception IOException Exception
898   */
899  public boolean prePut(final Put put, final WALEdit edit) throws IOException {
900    if (coprocEnvironments.isEmpty()) {
901      return false;
902    }
903    boolean bypassable = true;
904    return execOperation(new RegionObserverOperationWithoutResult(bypassable) {
905      @Override
906      public void call(RegionObserver observer) throws IOException {
907        observer.prePut(this, put, edit);
908      }
909    });
910  }
911
912  /**
913   * Supports Coprocessor 'bypass'.
914   * @param mutation - the current mutation
915   * @param kv       - the current cell
916   * @param byteNow  - current timestamp in bytes
917   * @param get      - the get that could be used Note that the get only does not specify the family
918   *                 and qualifier that should be used
919   * @return true if default processing should be bypassed
920   * @deprecated In hbase-2.0.0. Will be removed in hbase-3.0.0. Added explicitly for a single
921   *             Coprocessor for its needs only. Will be removed.
922   */
923  @Deprecated
924  public boolean prePrepareTimeStampForDeleteVersion(final Mutation mutation, final Cell kv,
925    final byte[] byteNow, final Get get) throws IOException {
926    if (coprocEnvironments.isEmpty()) {
927      return false;
928    }
929    boolean bypassable = true;
930    return execOperation(new RegionObserverOperationWithoutResult(bypassable) {
931      @Override
932      public void call(RegionObserver observer) throws IOException {
933        observer.prePrepareTimeStampForDeleteVersion(this, mutation, kv, byteNow, get);
934      }
935    });
936  }
937
938  /**
939   * @param put  The Put object
940   * @param edit The WALEdit object.
941   * @exception IOException Exception
942   */
943  public void postPut(final Put put, final WALEdit edit) throws IOException {
944    if (coprocEnvironments.isEmpty()) {
945      return;
946    }
947    execOperation(new RegionObserverOperationWithoutResult() {
948      @Override
949      public void call(RegionObserver observer) throws IOException {
950        observer.postPut(this, put, edit);
951      }
952    });
953  }
954
955  /**
956   * Supports Coprocessor 'bypass'.
957   * @param delete The Delete object
958   * @param edit   The WALEdit object.
959   * @return true if default processing should be bypassed
960   * @exception IOException Exception
961   */
962  public boolean preDelete(final Delete delete, final WALEdit edit) throws IOException {
963    if (this.coprocEnvironments.isEmpty()) {
964      return false;
965    }
966    boolean bypassable = true;
967    return execOperation(new RegionObserverOperationWithoutResult(bypassable) {
968      @Override
969      public void call(RegionObserver observer) throws IOException {
970        observer.preDelete(this, delete, edit);
971      }
972    });
973  }
974
975  /**
976   * @param delete The Delete object
977   * @param edit   The WALEdit object.
978   * @exception IOException Exception
979   */
980  public void postDelete(final Delete delete, final WALEdit edit) throws IOException {
981    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
982      @Override
983      public void call(RegionObserver observer) throws IOException {
984        observer.postDelete(this, delete, edit);
985      }
986    });
987  }
988
989  public void preBatchMutate(final MiniBatchOperationInProgress<Mutation> miniBatchOp)
990    throws IOException {
991    if (this.coprocEnvironments.isEmpty()) {
992      return;
993    }
994    execOperation(new RegionObserverOperationWithoutResult() {
995      @Override
996      public void call(RegionObserver observer) throws IOException {
997        observer.preBatchMutate(this, miniBatchOp);
998      }
999    });
1000  }
1001
1002  public void postBatchMutate(final MiniBatchOperationInProgress<Mutation> miniBatchOp)
1003    throws IOException {
1004    if (this.coprocEnvironments.isEmpty()) {
1005      return;
1006    }
1007    execOperation(new RegionObserverOperationWithoutResult() {
1008      @Override
1009      public void call(RegionObserver observer) throws IOException {
1010        observer.postBatchMutate(this, miniBatchOp);
1011      }
1012    });
1013  }
1014
1015  public void postBatchMutateIndispensably(final MiniBatchOperationInProgress<Mutation> miniBatchOp,
1016    final boolean success) throws IOException {
1017    if (this.coprocEnvironments.isEmpty()) {
1018      return;
1019    }
1020    execOperation(new RegionObserverOperationWithoutResult() {
1021      @Override
1022      public void call(RegionObserver observer) throws IOException {
1023        observer.postBatchMutateIndispensably(this, miniBatchOp, success);
1024      }
1025    });
1026  }
1027
1028  /**
1029   * Supports Coprocessor 'bypass'.
1030   * @param checkAndMutate the CheckAndMutate object
1031   * @return true or false to return to client if default processing should be bypassed, or null
1032   *         otherwise
1033   * @throws IOException if an error occurred on the coprocessor
1034   */
1035  public CheckAndMutateResult preCheckAndMutate(CheckAndMutate checkAndMutate) throws IOException {
1036    boolean bypassable = true;
1037    CheckAndMutateResult defaultResult = new CheckAndMutateResult(false, null);
1038    if (coprocEnvironments.isEmpty()) {
1039      return null;
1040    }
1041    return execOperationWithResult(
1042      new ObserverOperationWithResult<RegionObserver, CheckAndMutateResult>(regionObserverGetter,
1043        defaultResult, bypassable) {
1044        @Override
1045        public CheckAndMutateResult call(RegionObserver observer) throws IOException {
1046          return observer.preCheckAndMutate(this, checkAndMutate, getResult());
1047        }
1048      });
1049  }
1050
1051  /**
1052   * Supports Coprocessor 'bypass'.
1053   * @param checkAndMutate the CheckAndMutate object
1054   * @return true or false to return to client if default processing should be bypassed, or null
1055   *         otherwise
1056   * @throws IOException if an error occurred on the coprocessor
1057   */
1058  public CheckAndMutateResult preCheckAndMutateAfterRowLock(CheckAndMutate checkAndMutate)
1059    throws IOException {
1060    boolean bypassable = true;
1061    CheckAndMutateResult defaultResult = new CheckAndMutateResult(false, null);
1062    if (coprocEnvironments.isEmpty()) {
1063      return null;
1064    }
1065    return execOperationWithResult(
1066      new ObserverOperationWithResult<RegionObserver, CheckAndMutateResult>(regionObserverGetter,
1067        defaultResult, bypassable) {
1068        @Override
1069        public CheckAndMutateResult call(RegionObserver observer) throws IOException {
1070          return observer.preCheckAndMutateAfterRowLock(this, checkAndMutate, getResult());
1071        }
1072      });
1073  }
1074
1075  /**
1076   * @param checkAndMutate the CheckAndMutate object
1077   * @param result         the result returned by the checkAndMutate
1078   * @return true or false to return to client if default processing should be bypassed, or null
1079   *         otherwise
1080   * @throws IOException if an error occurred on the coprocessor
1081   */
1082  public CheckAndMutateResult postCheckAndMutate(CheckAndMutate checkAndMutate,
1083    CheckAndMutateResult result) throws IOException {
1084    if (this.coprocEnvironments.isEmpty()) {
1085      return result;
1086    }
1087    return execOperationWithResult(
1088      new ObserverOperationWithResult<RegionObserver, CheckAndMutateResult>(regionObserverGetter,
1089        result) {
1090        @Override
1091        public CheckAndMutateResult call(RegionObserver observer) throws IOException {
1092          return observer.postCheckAndMutate(this, checkAndMutate, getResult());
1093        }
1094      });
1095  }
1096
1097  /**
1098   * Supports Coprocessor 'bypass'.
1099   * @param append append object
1100   * @param edit   The WALEdit object.
1101   * @return result to return to client if default operation should be bypassed, null otherwise
1102   * @throws IOException if an error occurred on the coprocessor
1103   */
1104  public Result preAppend(final Append append, final WALEdit edit) throws IOException {
1105    boolean bypassable = true;
1106    Result defaultResult = null;
1107    if (this.coprocEnvironments.isEmpty()) {
1108      return defaultResult;
1109    }
1110    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Result>(
1111      regionObserverGetter, defaultResult, bypassable) {
1112      @Override
1113      public Result call(RegionObserver observer) throws IOException {
1114        return observer.preAppend(this, append, edit);
1115      }
1116    });
1117  }
1118
1119  /**
1120   * Supports Coprocessor 'bypass'.
1121   * @param append append object
1122   * @return result to return to client if default operation should be bypassed, null otherwise
1123   * @throws IOException if an error occurred on the coprocessor
1124   */
1125  public Result preAppendAfterRowLock(final Append append) throws IOException {
1126    boolean bypassable = true;
1127    Result defaultResult = null;
1128    if (this.coprocEnvironments.isEmpty()) {
1129      return defaultResult;
1130    }
1131    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Result>(
1132      regionObserverGetter, defaultResult, bypassable) {
1133      @Override
1134      public Result call(RegionObserver observer) throws IOException {
1135        return observer.preAppendAfterRowLock(this, append);
1136      }
1137    });
1138  }
1139
1140  /**
1141   * Supports Coprocessor 'bypass'.
1142   * @param increment increment object
1143   * @param edit      The WALEdit object.
1144   * @return result to return to client if default operation should be bypassed, null otherwise
1145   * @throws IOException if an error occurred on the coprocessor
1146   */
1147  public Result preIncrement(final Increment increment, final WALEdit edit) throws IOException {
1148    boolean bypassable = true;
1149    Result defaultResult = null;
1150    if (coprocEnvironments.isEmpty()) {
1151      return defaultResult;
1152    }
1153    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Result>(
1154      regionObserverGetter, defaultResult, bypassable) {
1155      @Override
1156      public Result call(RegionObserver observer) throws IOException {
1157        return observer.preIncrement(this, increment, edit);
1158      }
1159    });
1160  }
1161
1162  /**
1163   * Supports Coprocessor 'bypass'.
1164   * @param increment increment object
1165   * @return result to return to client if default operation should be bypassed, null otherwise
1166   * @throws IOException if an error occurred on the coprocessor
1167   */
1168  public Result preIncrementAfterRowLock(final Increment increment) throws IOException {
1169    boolean bypassable = true;
1170    Result defaultResult = null;
1171    if (coprocEnvironments.isEmpty()) {
1172      return defaultResult;
1173    }
1174    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Result>(
1175      regionObserverGetter, defaultResult, bypassable) {
1176      @Override
1177      public Result call(RegionObserver observer) throws IOException {
1178        return observer.preIncrementAfterRowLock(this, increment);
1179      }
1180    });
1181  }
1182
1183  /**
1184   * @param append Append object
1185   * @param result the result returned by the append
1186   * @param edit   The WALEdit object.
1187   * @throws IOException if an error occurred on the coprocessor
1188   */
1189  public Result postAppend(final Append append, final Result result, final WALEdit edit)
1190    throws IOException {
1191    if (this.coprocEnvironments.isEmpty()) {
1192      return result;
1193    }
1194    return execOperationWithResult(
1195      new ObserverOperationWithResult<RegionObserver, Result>(regionObserverGetter, result) {
1196        @Override
1197        public Result call(RegionObserver observer) throws IOException {
1198          return observer.postAppend(this, append, result, edit);
1199        }
1200      });
1201  }
1202
1203  /**
1204   * @param increment increment object
1205   * @param result    the result returned by postIncrement
1206   * @param edit      The WALEdit object.
1207   * @throws IOException if an error occurred on the coprocessor
1208   */
1209  public Result postIncrement(final Increment increment, Result result, final WALEdit edit)
1210    throws IOException {
1211    if (this.coprocEnvironments.isEmpty()) {
1212      return result;
1213    }
1214    return execOperationWithResult(
1215      new ObserverOperationWithResult<RegionObserver, Result>(regionObserverGetter, result) {
1216        @Override
1217        public Result call(RegionObserver observer) throws IOException {
1218          return observer.postIncrement(this, increment, getResult(), edit);
1219        }
1220      });
1221  }
1222
1223  /**
1224   * @param scan the Scan specification
1225   * @exception IOException Exception
1226   */
1227  public void preScannerOpen(final Scan scan) throws IOException {
1228    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1229      @Override
1230      public void call(RegionObserver observer) throws IOException {
1231        observer.preScannerOpen(this, scan);
1232      }
1233    });
1234  }
1235
1236  /**
1237   * @param scan the Scan specification
1238   * @param s    the scanner
1239   * @return the scanner instance to use
1240   * @exception IOException Exception
1241   */
1242  public RegionScanner postScannerOpen(final Scan scan, RegionScanner s) throws IOException {
1243    if (this.coprocEnvironments.isEmpty()) {
1244      return s;
1245    }
1246    return execOperationWithResult(
1247      new ObserverOperationWithResult<RegionObserver, RegionScanner>(regionObserverGetter, s) {
1248        @Override
1249        public RegionScanner call(RegionObserver observer) throws IOException {
1250          return observer.postScannerOpen(this, scan, getResult());
1251        }
1252      });
1253  }
1254
1255  /**
1256   * @param s       the scanner
1257   * @param results the result set returned by the region server
1258   * @param limit   the maximum number of results to return
1259   * @return 'has next' indication to client if bypassing default behavior, or null otherwise
1260   * @exception IOException Exception
1261   */
1262  public Boolean preScannerNext(final InternalScanner s, final List<Result> results,
1263    final int limit) throws IOException {
1264    boolean bypassable = true;
1265    boolean defaultResult = false;
1266    if (coprocEnvironments.isEmpty()) {
1267      return null;
1268    }
1269    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Boolean>(
1270      regionObserverGetter, defaultResult, bypassable) {
1271      @Override
1272      public Boolean call(RegionObserver observer) throws IOException {
1273        return observer.preScannerNext(this, s, results, limit, getResult());
1274      }
1275    });
1276  }
1277
1278  /**
1279   * @param s       the scanner
1280   * @param results the result set returned by the region server
1281   * @param limit   the maximum number of results to return
1282   * @return 'has more' indication to give to client
1283   * @exception IOException Exception
1284   */
1285  public boolean postScannerNext(final InternalScanner s, final List<Result> results,
1286    final int limit, boolean hasMore) throws IOException {
1287    if (this.coprocEnvironments.isEmpty()) {
1288      return hasMore;
1289    }
1290    return execOperationWithResult(
1291      new ObserverOperationWithResult<RegionObserver, Boolean>(regionObserverGetter, hasMore) {
1292        @Override
1293        public Boolean call(RegionObserver observer) throws IOException {
1294          return observer.postScannerNext(this, s, results, limit, getResult());
1295        }
1296      });
1297  }
1298
1299  /**
1300   * This will be called by the scan flow when the current scanned row is being filtered out by the
1301   * filter.
1302   * @param s          the scanner
1303   * @param curRowCell The cell in the current row which got filtered out
1304   * @return whether more rows are available for the scanner or not
1305   */
1306  public boolean postScannerFilterRow(final InternalScanner s, final Cell curRowCell)
1307    throws IOException {
1308    // short circuit for performance
1309    boolean defaultResult = true;
1310    if (!hasCustomPostScannerFilterRow) {
1311      return defaultResult;
1312    }
1313    if (this.coprocEnvironments.isEmpty()) {
1314      return defaultResult;
1315    }
1316    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, Boolean>(
1317      regionObserverGetter, defaultResult) {
1318      @Override
1319      public Boolean call(RegionObserver observer) throws IOException {
1320        return observer.postScannerFilterRow(this, s, curRowCell, getResult());
1321      }
1322    });
1323  }
1324
1325  /**
1326   * Supports Coprocessor 'bypass'.
1327   * @param s the scanner
1328   * @return true if default behavior should be bypassed, false otherwise
1329   * @exception IOException Exception
1330   */
1331  // Should this be bypassable?
1332  public boolean preScannerClose(final InternalScanner s) throws IOException {
1333    return execOperation(
1334      coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult(true) {
1335        @Override
1336        public void call(RegionObserver observer) throws IOException {
1337          observer.preScannerClose(this, s);
1338        }
1339      });
1340  }
1341
1342  /**
1343   * @exception IOException Exception
1344   */
1345  public void postScannerClose(final InternalScanner s) throws IOException {
1346    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1347      @Override
1348      public void call(RegionObserver observer) throws IOException {
1349        observer.postScannerClose(this, s);
1350      }
1351    });
1352  }
1353
1354  /**
1355   * Called before open store scanner for user scan.
1356   */
1357  public ScanInfo preStoreScannerOpen(HStore store, Scan scan) throws IOException {
1358    if (coprocEnvironments.isEmpty()) return store.getScanInfo();
1359    CustomizedScanInfoBuilder builder = new CustomizedScanInfoBuilder(store.getScanInfo(), scan);
1360    execOperation(new RegionObserverOperationWithoutResult() {
1361      @Override
1362      public void call(RegionObserver observer) throws IOException {
1363        observer.preStoreScannerOpen(this, store, builder);
1364      }
1365    });
1366    return builder.build();
1367  }
1368
1369  /**
1370   * @param info  the RegionInfo for this region
1371   * @param edits the file of recovered edits
1372   */
1373  public void preReplayWALs(final RegionInfo info, final Path edits) throws IOException {
1374    execOperation(
1375      coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult(true) {
1376        @Override
1377        public void call(RegionObserver observer) throws IOException {
1378          observer.preReplayWALs(this, info, edits);
1379        }
1380      });
1381  }
1382
1383  /**
1384   * @param info  the RegionInfo for this region
1385   * @param edits the file of recovered edits
1386   * @throws IOException Exception
1387   */
1388  public void postReplayWALs(final RegionInfo info, final Path edits) throws IOException {
1389    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1390      @Override
1391      public void call(RegionObserver observer) throws IOException {
1392        observer.postReplayWALs(this, info, edits);
1393      }
1394    });
1395  }
1396
1397  /**
1398   * Supports Coprocessor 'bypass'.
1399   * @return true if default behavior should be bypassed, false otherwise
1400   * @deprecated Since hbase-2.0.0. No replacement. To be removed in hbase-3.0.0 and replaced with
1401   *             something that doesn't expose IntefaceAudience.Private classes.
1402   */
1403  @Deprecated
1404  public boolean preWALRestore(final RegionInfo info, final WALKey logKey, final WALEdit logEdit)
1405    throws IOException {
1406    return execOperation(
1407      coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult(true) {
1408        @Override
1409        public void call(RegionObserver observer) throws IOException {
1410          observer.preWALRestore(this, info, logKey, logEdit);
1411        }
1412      });
1413  }
1414
1415  /**
1416   * @deprecated Since hbase-2.0.0. No replacement. To be removed in hbase-3.0.0 and replaced with
1417   *             something that doesn't expose IntefaceAudience.Private classes.
1418   */
1419  @Deprecated
1420  public void postWALRestore(final RegionInfo info, final WALKey logKey, final WALEdit logEdit)
1421    throws IOException {
1422    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1423      @Override
1424      public void call(RegionObserver observer) throws IOException {
1425        observer.postWALRestore(this, info, logKey, logEdit);
1426      }
1427    });
1428  }
1429
1430  /**
1431   * @param familyPaths pairs of { CF, file path } submitted for bulk load
1432   */
1433  public void preBulkLoadHFile(final List<Pair<byte[], String>> familyPaths) throws IOException {
1434    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1435      @Override
1436      public void call(RegionObserver observer) throws IOException {
1437        observer.preBulkLoadHFile(this, familyPaths);
1438      }
1439    });
1440  }
1441
1442  public boolean preCommitStoreFile(final byte[] family, final List<Pair<Path, Path>> pairs)
1443    throws IOException {
1444    return execOperation(
1445      coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1446        @Override
1447        public void call(RegionObserver observer) throws IOException {
1448          observer.preCommitStoreFile(this, family, pairs);
1449        }
1450      });
1451  }
1452
1453  public void postCommitStoreFile(final byte[] family, Path srcPath, Path dstPath)
1454    throws IOException {
1455    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1456      @Override
1457      public void call(RegionObserver observer) throws IOException {
1458        observer.postCommitStoreFile(this, family, srcPath, dstPath);
1459      }
1460    });
1461  }
1462
1463  /**
1464   * @param familyPaths pairs of { CF, file path } submitted for bulk load
1465   * @param map         Map of CF to List of file paths for the final loaded files
1466   */
1467  public void postBulkLoadHFile(final List<Pair<byte[], String>> familyPaths,
1468    Map<byte[], List<Path>> map) throws IOException {
1469    if (this.coprocEnvironments.isEmpty()) {
1470      return;
1471    }
1472    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1473      @Override
1474      public void call(RegionObserver observer) throws IOException {
1475        observer.postBulkLoadHFile(this, familyPaths, map);
1476      }
1477    });
1478  }
1479
1480  public void postStartRegionOperation(final Operation op) throws IOException {
1481    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1482      @Override
1483      public void call(RegionObserver observer) throws IOException {
1484        observer.postStartRegionOperation(this, op);
1485      }
1486    });
1487  }
1488
1489  public void postCloseRegionOperation(final Operation op) throws IOException {
1490    execOperation(coprocEnvironments.isEmpty() ? null : new RegionObserverOperationWithoutResult() {
1491      @Override
1492      public void call(RegionObserver observer) throws IOException {
1493        observer.postCloseRegionOperation(this, op);
1494      }
1495    });
1496  }
1497
1498  /**
1499   * @param fs   fileystem to read from
1500   * @param p    path to the file
1501   * @param in   {@link FSDataInputStreamWrapper}
1502   * @param size Full size of the file
1503   * @param r    original reference file. This will be not null only when reading a split file.
1504   * @return a Reader instance to use instead of the base reader if overriding default behavior,
1505   *         null otherwise
1506   */
1507  public StoreFileReader preStoreFileReaderOpen(final FileSystem fs, final Path p,
1508    final FSDataInputStreamWrapper in, final long size, final CacheConfig cacheConf,
1509    final Reference r) throws IOException {
1510    if (coprocEnvironments.isEmpty()) {
1511      return null;
1512    }
1513    return execOperationWithResult(
1514      new ObserverOperationWithResult<RegionObserver, StoreFileReader>(regionObserverGetter, null) {
1515        @Override
1516        public StoreFileReader call(RegionObserver observer) throws IOException {
1517          return observer.preStoreFileReaderOpen(this, fs, p, in, size, cacheConf, r, getResult());
1518        }
1519      });
1520  }
1521
1522  /**
1523   * @param fs     fileystem to read from
1524   * @param p      path to the file
1525   * @param in     {@link FSDataInputStreamWrapper}
1526   * @param size   Full size of the file
1527   * @param r      original reference file. This will be not null only when reading a split file.
1528   * @param reader the base reader instance
1529   * @return The reader to use
1530   */
1531  public StoreFileReader postStoreFileReaderOpen(final FileSystem fs, final Path p,
1532    final FSDataInputStreamWrapper in, final long size, final CacheConfig cacheConf,
1533    final Reference r, final StoreFileReader reader) throws IOException {
1534    if (this.coprocEnvironments.isEmpty()) {
1535      return reader;
1536    }
1537    return execOperationWithResult(new ObserverOperationWithResult<RegionObserver, StoreFileReader>(
1538      regionObserverGetter, reader) {
1539      @Override
1540      public StoreFileReader call(RegionObserver observer) throws IOException {
1541        return observer.postStoreFileReaderOpen(this, fs, p, in, size, cacheConf, r, getResult());
1542      }
1543    });
1544  }
1545
1546  public List<Pair<Cell, Cell>> postIncrementBeforeWAL(final Mutation mutation,
1547    final List<Pair<Cell, Cell>> cellPairs) throws IOException {
1548    if (this.coprocEnvironments.isEmpty()) {
1549      return cellPairs;
1550    }
1551    return execOperationWithResult(
1552      new ObserverOperationWithResult<RegionObserver, List<Pair<Cell, Cell>>>(regionObserverGetter,
1553        cellPairs) {
1554        @Override
1555        public List<Pair<Cell, Cell>> call(RegionObserver observer) throws IOException {
1556          return observer.postIncrementBeforeWAL(this, mutation, getResult());
1557        }
1558      });
1559  }
1560
1561  public List<Pair<Cell, Cell>> postAppendBeforeWAL(final Mutation mutation,
1562    final List<Pair<Cell, Cell>> cellPairs) throws IOException {
1563    if (this.coprocEnvironments.isEmpty()) {
1564      return cellPairs;
1565    }
1566    return execOperationWithResult(
1567      new ObserverOperationWithResult<RegionObserver, List<Pair<Cell, Cell>>>(regionObserverGetter,
1568        cellPairs) {
1569        @Override
1570        public List<Pair<Cell, Cell>> call(RegionObserver observer) throws IOException {
1571          return observer.postAppendBeforeWAL(this, mutation, getResult());
1572        }
1573      });
1574  }
1575
1576  public void preWALAppend(WALKey key, WALEdit edit) throws IOException {
1577    if (this.coprocEnvironments.isEmpty()) {
1578      return;
1579    }
1580    execOperation(new RegionObserverOperationWithoutResult() {
1581      @Override
1582      public void call(RegionObserver observer) throws IOException {
1583        observer.preWALAppend(this, key, edit);
1584      }
1585    });
1586  }
1587
1588  public Message preEndpointInvocation(final Service service, final String methodName,
1589    Message request) throws IOException {
1590    if (coprocEnvironments.isEmpty()) {
1591      return request;
1592    }
1593    return execOperationWithResult(
1594      new ObserverOperationWithResult<EndpointObserver, Message>(endpointObserverGetter, request) {
1595        @Override
1596        public Message call(EndpointObserver observer) throws IOException {
1597          return observer.preEndpointInvocation(this, service, methodName, getResult());
1598        }
1599      });
1600  }
1601
1602  public void postEndpointInvocation(final Service service, final String methodName,
1603    final Message request, final Message.Builder responseBuilder) throws IOException {
1604    execOperation(coprocEnvironments.isEmpty()
1605      ? null
1606      : new ObserverOperationWithoutResult<EndpointObserver>(endpointObserverGetter) {
1607        @Override
1608        public void call(EndpointObserver observer) throws IOException {
1609          observer.postEndpointInvocation(this, service, methodName, request, responseBuilder);
1610        }
1611      });
1612  }
1613
1614  /**
1615   * @deprecated Since 2.0 with out any replacement and will be removed in 3.0
1616   */
1617  @Deprecated
1618  public DeleteTracker postInstantiateDeleteTracker(DeleteTracker result) throws IOException {
1619    if (this.coprocEnvironments.isEmpty()) {
1620      return result;
1621    }
1622    return execOperationWithResult(
1623      new ObserverOperationWithResult<RegionObserver, DeleteTracker>(regionObserverGetter, result) {
1624        @Override
1625        public DeleteTracker call(RegionObserver observer) throws IOException {
1626          return observer.postInstantiateDeleteTracker(this, getResult());
1627        }
1628      });
1629  }
1630
1631  /////////////////////////////////////////////////////////////////////////////////////////////////
1632  // BulkLoadObserver hooks
1633  /////////////////////////////////////////////////////////////////////////////////////////////////
1634  public void prePrepareBulkLoad(User user) throws IOException {
1635    execOperation(coprocEnvironments.isEmpty() ? null : new BulkLoadObserverOperation(user) {
1636      @Override
1637      protected void call(BulkLoadObserver observer) throws IOException {
1638        observer.prePrepareBulkLoad(this);
1639      }
1640    });
1641  }
1642
1643  public void preCleanupBulkLoad(User user) throws IOException {
1644    execOperation(coprocEnvironments.isEmpty() ? null : new BulkLoadObserverOperation(user) {
1645      @Override
1646      protected void call(BulkLoadObserver observer) throws IOException {
1647        observer.preCleanupBulkLoad(this);
1648      }
1649    });
1650  }
1651}