You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hbase.apache.org by ga...@apache.org on 2010/12/21 21:39:37 UTC

svn commit: r1051639 [1/3] - in /hbase/trunk: ./ src/main/java/org/apache/hadoop/hbase/coprocessor/ src/main/java/org/apache/hadoop/hbase/master/ src/main/java/org/apache/hadoop/hbase/regionserver/ src/main/resources/ src/test/java/org/apache/hadoop/hb...

Author: garyh
Date: Tue Dec 21 20:39:26 2010
New Revision: 1051639

URL: http://svn.apache.org/viewvc?rev=1051639&view=rev
Log:
HBASE-3256: Add coprocessor host and observer for HMaster

Added:
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestMasterObserver.java
Removed:
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/CoprocessorHost.java
Modified:
    hbase/trunk/CHANGES.txt
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorEnvironment.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/package-info.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/MasterServices.java
    hbase/trunk/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java
    hbase/trunk/src/main/resources/hbase-default.xml
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/ColumnAggregationEndpoint.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorEndpoint.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorInterface.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverStacking.java
    hbase/trunk/src/test/java/org/apache/hadoop/hbase/master/TestCatalogJanitor.java

Modified: hbase/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/hbase/trunk/CHANGES.txt?rev=1051639&r1=1051638&r2=1051639&view=diff
==============================================================================
--- hbase/trunk/CHANGES.txt (original)
+++ hbase/trunk/CHANGES.txt Tue Dec 21 20:39:26 2010
@@ -46,6 +46,7 @@ Release 0.91.0 - Unreleased
    HBASE-3287  Add option to cache blocks on hfile write and evict blocks on
                hfile close
    HBASE-3335  Add BitComparator for filtering (Nathaniel Cook via Stack)
+   HBASE-3256  Coprocessors: Coprocessor host and observer for HMaster
 
 
 Release 0.90.0 - Unreleased

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java?rev=1051639&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseMasterObserver.java Tue Dec 21 20:39:26 2010
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HServerInfo;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.UnknownRegionException;
+
+import java.io.IOException;
+
+public class BaseMasterObserver implements MasterObserver {
+  @Override
+  public void preCreateTable(MasterCoprocessorEnvironment env,
+      HTableDescriptor desc, byte[][] splitKeys) throws IOException {
+  }
+
+  @Override
+  public void postCreateTable(MasterCoprocessorEnvironment env,
+      HRegionInfo[] regions, boolean sync) throws IOException {
+  }
+
+  @Override
+  public void preDeleteTable(MasterCoprocessorEnvironment env, byte[] tableName)
+      throws IOException {
+  }
+
+  @Override
+  public void postDeleteTable(MasterCoprocessorEnvironment env, byte[] tableName)
+      throws IOException {
+  }
+
+  @Override
+  public void preModifyTable(MasterCoprocessorEnvironment env,
+      byte[] tableName, HTableDescriptor htd) throws IOException {
+  }
+
+  @Override
+  public void postModifyTable(MasterCoprocessorEnvironment env,
+      byte[] tableName, HTableDescriptor htd) throws IOException {
+  }
+
+  @Override
+  public void preAddColumn(MasterCoprocessorEnvironment env,
+      byte[] tableName, HColumnDescriptor column) throws IOException {
+  }
+
+  @Override
+  public void postAddColumn(MasterCoprocessorEnvironment env, byte[] tableName,
+      HColumnDescriptor column) throws IOException {
+  }
+
+  @Override
+  public void preModifyColumn(MasterCoprocessorEnvironment env,
+      byte[] tableName, HColumnDescriptor descriptor) throws IOException {
+  }
+
+  @Override
+  public void postModifyColumn(MasterCoprocessorEnvironment env,
+      byte[] tableName, HColumnDescriptor descriptor) throws IOException {
+  }
+
+  @Override
+  public void preDeleteColumn(MasterCoprocessorEnvironment env,
+      byte[] tableName, byte[] c) throws IOException {
+  }
+
+  @Override
+  public void postDeleteColumn(MasterCoprocessorEnvironment env,
+      byte[] tableName, byte[] c) throws IOException {
+  }
+
+  @Override
+  public void preEnableTable(MasterCoprocessorEnvironment env, byte[] tableName)
+      throws IOException {
+  }
+
+  @Override
+  public void postEnableTable(MasterCoprocessorEnvironment env,
+      byte[] tableName) throws IOException {
+  }
+
+  @Override
+  public void preDisableTable(MasterCoprocessorEnvironment env,
+      byte[] tableName) throws IOException {
+  }
+
+  @Override
+  public void postDisableTable(MasterCoprocessorEnvironment env,
+      byte[] tableName) throws IOException {
+  }
+
+  @Override
+  public void preMove(MasterCoprocessorEnvironment env, HRegionInfo region,
+      HServerInfo srcServer, HServerInfo destServer)
+  throws UnknownRegionException {
+  }
+
+  @Override
+  public void postMove(MasterCoprocessorEnvironment env, HRegionInfo region,
+      HServerInfo srcServer, HServerInfo destServer)
+  throws UnknownRegionException {
+  }
+
+  @Override
+  public void preAssign(MasterCoprocessorEnvironment env, byte[] regionName,
+      boolean force) throws IOException {
+  }
+
+  @Override
+  public void postAssign(MasterCoprocessorEnvironment env,
+      HRegionInfo regionInfo) throws IOException {
+  }
+
+  @Override
+  public void preUnassign(MasterCoprocessorEnvironment env, byte[] regionName,
+      boolean force) throws IOException {
+  }
+
+  @Override
+  public void postUnassign(MasterCoprocessorEnvironment env,
+      HRegionInfo regionInfo, boolean force) throws IOException {
+  }
+
+  @Override
+  public void preBalance(MasterCoprocessorEnvironment env) throws IOException {
+  }
+
+  @Override
+  public void postBalance(MasterCoprocessorEnvironment env) throws IOException {
+  }
+
+  @Override
+  public boolean preBalanceSwitch(MasterCoprocessorEnvironment env, boolean b)
+      throws IOException {
+    return b;
+  }
+
+  @Override
+  public void postBalanceSwitch(MasterCoprocessorEnvironment env,
+      boolean oldValue, boolean newValue) throws IOException {
+  }
+
+  @Override
+  public void preShutdown(MasterCoprocessorEnvironment env) throws IOException {
+  }
+
+  @Override
+  public void preStopMaster(MasterCoprocessorEnvironment env)
+      throws IOException {
+  }
+
+  @Override
+  public void start(CoprocessorEnvironment env) throws IOException {
+  }
+
+  @Override
+  public void stop(CoprocessorEnvironment env) throws IOException {
+  }
+}

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java?rev=1051639&r1=1051638&r2=1051639&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java Tue Dec 21 20:39:26 2010
@@ -36,8 +36,7 @@ import java.io.IOException;
  * By extending it, you can create you own region observer without
  * overriding all abstract methods of Coprocessor and RegionObserver.
  */
-public abstract class BaseRegionObserverCoprocessor implements Coprocessor,
-    RegionObserver {
+public abstract class BaseRegionObserverCoprocessor implements RegionObserver {
   @Override
   public void start(CoprocessorEnvironment e) { }
 
@@ -45,94 +44,94 @@ public abstract class BaseRegionObserver
   public void stop(CoprocessorEnvironment e) { }
 
   @Override
-  public void preOpen(CoprocessorEnvironment e) { }
+  public void preOpen(RegionCoprocessorEnvironment e) { }
 
   @Override
-  public void postOpen(CoprocessorEnvironment e) { }
+  public void postOpen(RegionCoprocessorEnvironment e) { }
 
   @Override
-  public void preClose(CoprocessorEnvironment e, boolean abortRequested)
+  public void preClose(RegionCoprocessorEnvironment e, boolean abortRequested)
     { }
 
   @Override
-  public void postClose(CoprocessorEnvironment e, boolean abortRequested)
+  public void postClose(RegionCoprocessorEnvironment e, boolean abortRequested)
     { }
 
   @Override
-  public void preFlush(CoprocessorEnvironment e) { }
+  public void preFlush(RegionCoprocessorEnvironment e) { }
 
   @Override
-  public void postFlush(CoprocessorEnvironment e) { }
+  public void postFlush(RegionCoprocessorEnvironment e) { }
 
   @Override
-  public void preSplit(CoprocessorEnvironment e) { }
+  public void preSplit(RegionCoprocessorEnvironment e) { }
 
   @Override
-  public void postSplit(CoprocessorEnvironment e, HRegion l, HRegion r) { }
+  public void postSplit(RegionCoprocessorEnvironment e, HRegion l, HRegion r) { }
 
   @Override
-  public void preCompact(CoprocessorEnvironment e, boolean willSplit) { }
+  public void preCompact(RegionCoprocessorEnvironment e, boolean willSplit) { }
 
   @Override
-  public void postCompact(CoprocessorEnvironment e, boolean willSplit) { }
+  public void postCompact(RegionCoprocessorEnvironment e, boolean willSplit) { }
 
   @Override
-  public void preGetClosestRowBefore(final CoprocessorEnvironment e,
+  public void preGetClosestRowBefore(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final Result result)
     throws IOException {
   }
 
   @Override
-  public void postGetClosestRowBefore(final CoprocessorEnvironment e,
+  public void postGetClosestRowBefore(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final Result result)
       throws IOException {
   }
 
   @Override
-  public void preGet(final CoprocessorEnvironment e, final Get get,
+  public void preGet(final RegionCoprocessorEnvironment e, final Get get,
       final List<KeyValue> results) throws IOException {
   }
 
   @Override
-  public void postGet(final CoprocessorEnvironment e, final Get get,
+  public void postGet(final RegionCoprocessorEnvironment e, final Get get,
       final List<KeyValue> results) throws IOException {
   }
 
   @Override
-  public boolean preExists(final CoprocessorEnvironment e, final Get get,
+  public boolean preExists(final RegionCoprocessorEnvironment e, final Get get,
       final boolean exists) throws IOException {
     return exists;
   }
 
   @Override
-  public boolean postExists(final CoprocessorEnvironment e, final Get get,
+  public boolean postExists(final RegionCoprocessorEnvironment e, final Get get,
       boolean exists) throws IOException {
     return exists;
   }
 
   @Override
-  public void prePut(final CoprocessorEnvironment e, final Map<byte[],
+  public void prePut(final RegionCoprocessorEnvironment e, final Map<byte[],
       List<KeyValue>> familyMap, final boolean writeToWAL) throws IOException {
   }
 
   @Override
-  public void postPut(final CoprocessorEnvironment e, final Map<byte[],
+  public void postPut(final RegionCoprocessorEnvironment e, final Map<byte[],
       List<KeyValue>> familyMap, final boolean writeToWAL) throws IOException {
   }
 
   @Override
-  public void preDelete(final CoprocessorEnvironment e, final Map<byte[],
+  public void preDelete(final RegionCoprocessorEnvironment e, final Map<byte[],
       List<KeyValue>> familyMap, final boolean writeToWAL) throws IOException {
   }
 
   @Override
-  public void postDelete(final CoprocessorEnvironment e,
+  public void postDelete(final RegionCoprocessorEnvironment e,
       final Map<byte[], List<KeyValue>> familyMap, final boolean writeToWAL)
       throws IOException {
   }
 
   @Override
-  public boolean preCheckAndPut(final CoprocessorEnvironment e,
+  public boolean preCheckAndPut(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Put put, final boolean result)
       throws IOException {
@@ -140,7 +139,7 @@ public abstract class BaseRegionObserver
   }
 
   @Override
-  public boolean postCheckAndPut(final CoprocessorEnvironment e,
+  public boolean postCheckAndPut(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Put put, final boolean result)
     throws IOException {
@@ -148,7 +147,7 @@ public abstract class BaseRegionObserver
   }
 
   @Override
-  public boolean preCheckAndDelete(final CoprocessorEnvironment e,
+  public boolean preCheckAndDelete(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Delete delete, final boolean result)
       throws IOException {
@@ -156,7 +155,7 @@ public abstract class BaseRegionObserver
   }
 
   @Override
-  public boolean postCheckAndDelete(final CoprocessorEnvironment e,
+  public boolean postCheckAndDelete(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Delete delete, final boolean result)
       throws IOException {
@@ -164,14 +163,14 @@ public abstract class BaseRegionObserver
   }
 
   @Override
-  public long preIncrementColumnValue(final CoprocessorEnvironment e,
+  public long preIncrementColumnValue(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final long amount, final boolean writeToWAL) throws IOException {
     return amount;
   }
 
   @Override
-  public long postIncrementColumnValue(final CoprocessorEnvironment e,
+  public long postIncrementColumnValue(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final long amount, final boolean writeToWAL, long result)
       throws IOException {
@@ -179,48 +178,48 @@ public abstract class BaseRegionObserver
   }
 
   @Override
-  public void preIncrement(final CoprocessorEnvironment e,
+  public void preIncrement(final RegionCoprocessorEnvironment e,
       final Increment increment, final Result result) throws IOException {
   }
 
   @Override
-  public void postIncrement(final CoprocessorEnvironment e,
+  public void postIncrement(final RegionCoprocessorEnvironment e,
       final Increment increment, final Result result) throws IOException {
   }
 
   @Override
-  public InternalScanner preScannerOpen(final CoprocessorEnvironment e,
+  public InternalScanner preScannerOpen(final RegionCoprocessorEnvironment e,
       final Scan scan, final InternalScanner s) throws IOException {
     return s;
   }
 
   @Override
-  public InternalScanner postScannerOpen(final CoprocessorEnvironment e,
+  public InternalScanner postScannerOpen(final RegionCoprocessorEnvironment e,
       final Scan scan, final InternalScanner s) throws IOException {
     return s;
   }
 
   @Override
-  public boolean preScannerNext(final CoprocessorEnvironment e,
+  public boolean preScannerNext(final RegionCoprocessorEnvironment e,
       final InternalScanner s, final List<KeyValue> results,
       final int limit, final boolean hasMore) throws IOException {
     return hasMore;
   }
 
   @Override
-  public boolean postScannerNext(final CoprocessorEnvironment e,
+  public boolean postScannerNext(final RegionCoprocessorEnvironment e,
       final InternalScanner s, final List<KeyValue> results, final int limit,
       final boolean hasMore) throws IOException {
     return hasMore;
   }
 
   @Override
-  public void preScannerClose(final CoprocessorEnvironment e,
+  public void preScannerClose(final RegionCoprocessorEnvironment e,
       final InternalScanner s) throws IOException {
   }
 
   @Override
-  public void postScannerClose(final CoprocessorEnvironment e,
+  public void postScannerClose(final RegionCoprocessorEnvironment e,
       final InternalScanner s) throws IOException {
   }
 }

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorEnvironment.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorEnvironment.java?rev=1051639&r1=1051638&r2=1051639&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorEnvironment.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorEnvironment.java Tue Dec 21 20:39:26 2010
@@ -33,11 +33,11 @@ public interface CoprocessorEnvironment 
   /** @return the HBase version as a string (e.g. "0.21.0") */
   public String getHBaseVersion();
 
-  /** @return the region associated with this coprocessor */
-  public HRegion getRegion();
+  /** @return the loaded coprocessor instance */
+  public Coprocessor getInstance();
 
-  /** @return reference to the region server services */
-  public RegionServerServices getRegionServerServices();
+  /** @return the priority assigned to the loaded coprocessor */
+  public Coprocessor.Priority getPriority();
 
   /**
    * @return an interface for accessing the given table

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java?rev=1051639&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/CoprocessorHost.java Tue Dec 21 20:39:26 2010
@@ -0,0 +1,556 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.fs.FileSystem;
+import org.apache.hadoop.fs.Path;
+import org.apache.hadoop.hbase.HBaseConfiguration;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.client.*;
+import org.apache.hadoop.hbase.client.coprocessor.Batch;
+import org.apache.hadoop.hbase.ipc.CoprocessorProtocol;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.apache.hadoop.hbase.util.VersionInfo;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.*;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+/**
+ * Provides the common setup framework and runtime services for coprocessor
+ * invocation from HBase services.
+ * @param <E> the specific environment extension that a concrete implementation
+ * provides
+ */
+public abstract class CoprocessorHost<E extends CoprocessorEnvironment> {
+  public static final String REGION_COPROCESSOR_CONF_KEY =
+      "hbase.coprocessor.region.classes";
+  public static final String MASTER_COPROCESSOR_CONF_KEY =
+      "hbase.coprocessor.master.classes";
+  private static final Log LOG = LogFactory.getLog(CoprocessorHost.class);
+  /** Ordered set of loaded coprocessors with lock */
+  protected final ReentrantReadWriteLock coprocessorLock = new ReentrantReadWriteLock();
+  protected Set<E> coprocessors =
+    new TreeSet<E>(new EnvironmentPriorityComparator());
+  // unique file prefix to use for local copies of jars when classloading
+  protected String pathPrefix;
+
+  public CoprocessorHost() {
+    pathPrefix = UUID.randomUUID().toString();
+  }
+
+  /**
+   * Load system coprocessors. Read the class names from configuration.
+   * Called by constructor.
+   */
+  protected void loadSystemCoprocessors(Configuration conf, String confKey) {
+    Class<?> implClass = null;
+
+    // load default coprocessors from configure file
+    String defaultCPClasses = conf.get(confKey);
+    if (defaultCPClasses == null || defaultCPClasses.length() == 0)
+      return;
+    StringTokenizer st = new StringTokenizer(defaultCPClasses, ",");
+    int priority = Coprocessor.Priority.SYSTEM.intValue();
+    while (st.hasMoreTokens()) {
+      String className = st.nextToken();
+      if (findCoprocessor(className) != null) {
+        continue;
+      }
+      ClassLoader cl = ClassLoader.getSystemClassLoader();
+      Thread.currentThread().setContextClassLoader(cl);
+      try {
+        implClass = cl.loadClass(className);
+        load(implClass, Coprocessor.Priority.SYSTEM);
+        LOG.info("System coprocessor " + className + " was loaded " +
+            "successfully with priority (" + priority++ + ").");
+      } catch (ClassNotFoundException e) {
+        LOG.warn("Class " + className + " cannot be found. " +
+            e.getMessage());
+      } catch (IOException e) {
+        LOG.warn("Load coprocessor " + className + " failed. " +
+            e.getMessage());
+      }
+    }
+  }
+
+  /**
+   * Load a coprocessor implementation into the host
+   * @param path path to implementation jar
+   * @param className the main class name
+   * @param priority chaining priority
+   * @throws java.io.IOException Exception
+   */
+  @SuppressWarnings("deprecation")
+  public void load(Path path, String className, Coprocessor.Priority priority)
+      throws IOException {
+    Class<?> implClass = null;
+
+    // Have we already loaded the class, perhaps from an earlier region open
+    // for the same table?
+    try {
+      implClass = getClass().getClassLoader().loadClass(className);
+    } catch (ClassNotFoundException e) {
+      LOG.info("Class " + className + " needs to be loaded from a file - " +
+          path.toString() + ".");
+      // go ahead to load from file system.
+    }
+
+    // If not, load
+    if (implClass == null) {
+      // copy the jar to the local filesystem
+      if (!path.toString().endsWith(".jar")) {
+        throw new IOException(path.toString() + ": not a jar file?");
+      }
+      FileSystem fs = path.getFileSystem(HBaseConfiguration.create());
+      Path dst = new Path("/tmp/." + pathPrefix +
+        "." + className + "." + System.currentTimeMillis() + ".jar");
+      fs.copyToLocalFile(path, dst);
+      fs.deleteOnExit(dst);
+
+      // TODO: code weaving goes here
+
+      // TODO: wrap heap allocations and enforce maximum usage limits
+
+      /* TODO: inject code into loop headers that monitors CPU use and
+         aborts runaway user code */
+
+      // load the jar and get the implementation main class
+      String cp = System.getProperty("java.class.path");
+      // NOTE: Path.toURL is deprecated (toURI instead) but the URLClassLoader
+      // unsuprisingly wants URLs, not URIs; so we will use the deprecated
+      // method which returns URLs for as long as it is available
+      List<URL> paths = new ArrayList<URL>();
+      paths.add(new File(dst.toString()).getCanonicalFile().toURL());
+      StringTokenizer st = new StringTokenizer(cp, File.pathSeparator);
+      while (st.hasMoreTokens()) {
+        paths.add((new File(st.nextToken())).getCanonicalFile().toURL());
+      }
+      ClassLoader cl = new URLClassLoader(paths.toArray(new URL[]{}),
+        ClassLoader.getSystemClassLoader());
+      Thread.currentThread().setContextClassLoader(cl);
+      try {
+        implClass = cl.loadClass(className);
+      } catch (ClassNotFoundException e) {
+        throw new IOException(e);
+      }
+    }
+
+    load(implClass, priority);
+  }
+
+  /**
+   * @param implClass Implementation class
+   * @param priority priority
+   * @throws java.io.IOException Exception
+   */
+  public void load(Class<?> implClass, Coprocessor.Priority priority)
+      throws IOException {
+    // create the instance
+    Coprocessor impl;
+    Object o = null;
+    try {
+      o = implClass.newInstance();
+      impl = (Coprocessor)o;
+    } catch (InstantiationException e) {
+      throw new IOException(e);
+    } catch (IllegalAccessException e) {
+      throw new IOException(e);
+    }
+    // create the environment
+    E env = createEnvironment(implClass, impl, priority);
+    if (env instanceof Environment) {
+      ((Environment)env).startup();
+    }
+
+    try {
+      coprocessorLock.writeLock().lock();
+      coprocessors.add(env);
+    } finally {
+      coprocessorLock.writeLock().unlock();
+    }
+  }
+
+  /**
+   * Called when a new Coprocessor class is loaded
+   */
+  public abstract E createEnvironment(Class<?> implClass, Coprocessor instance,
+      Coprocessor.Priority priority);
+
+  public void shutdown(CoprocessorEnvironment e) {
+    if (e instanceof Environment) {
+      ((Environment)e).shutdown();
+    } else {
+      LOG.warn("Shutdown called on unknown environment: "+
+          e.getClass().getName());
+    }
+  }
+
+  /**
+   * Find a coprocessor implementation by class name
+   * @param className the class name
+   * @return the coprocessor, or null if not found
+   */
+  public Coprocessor findCoprocessor(String className) {
+    // initialize the coprocessors
+    try {
+      coprocessorLock.readLock().lock();
+      for (E env: coprocessors) {
+        if (env.getInstance().getClass().getName().equals(className) ||
+            env.getInstance().getClass().getSimpleName().equals(className)) {
+          return env.getInstance();
+        }
+      }
+      return null;
+    } finally {
+      coprocessorLock.readLock().unlock();
+    }
+  }
+
+  /**
+   * Environment priority comparator.
+   * Coprocessors are chained in sorted order.
+   */
+  static class EnvironmentPriorityComparator implements Comparator<CoprocessorEnvironment> {
+    public int compare(CoprocessorEnvironment env1, CoprocessorEnvironment env2) {
+      if (env1.getPriority().intValue() < env2.getPriority().intValue()) {
+        return -1;
+      } else if (env1.getPriority().intValue() > env2.getPriority().intValue()) {
+        return 1;
+      }
+      return 0;
+    }
+  }
+
+  /**
+   * Encapsulation of the environment of each coprocessor
+   */
+  public static class Environment implements CoprocessorEnvironment {
+
+    /**
+     * A wrapper for HTable. Can be used to restrict privilege.
+     *
+     * Currently it just helps to track tables opened by a Coprocessor and
+     * facilitate close of them if it is aborted.
+     *
+     * We also disallow row locking.
+     *
+     * There is nothing now that will stop a coprocessor from using HTable
+     * objects directly instead of this API, but in the future we intend to
+     * analyze coprocessor implementations as they are loaded and reject those
+     * which attempt to use objects and methods outside the Environment
+     * sandbox.
+     */
+    class HTableWrapper implements HTableInterface {
+
+      private byte[] tableName;
+      private HTable table;
+
+      public HTableWrapper(byte[] tableName) throws IOException {
+        this.tableName = tableName;
+        this.table = new HTable(tableName);
+        openTables.add(this);
+      }
+
+      void internalClose() throws IOException {
+        table.close();
+      }
+
+      public Configuration getConfiguration() {
+        return table.getConfiguration();
+      }
+
+      public void close() throws IOException {
+        try {
+          internalClose();
+        } finally {
+          openTables.remove(this);
+        }
+      }
+
+      public Result getRowOrBefore(byte[] row, byte[] family)
+          throws IOException {
+        return table.getRowOrBefore(row, family);
+      }
+
+      public Result get(Get get) throws IOException {
+        return table.get(get);
+      }
+
+      public boolean exists(Get get) throws IOException {
+        return table.exists(get);
+      }
+
+      public void put(Put put) throws IOException {
+        table.put(put);
+      }
+
+      public void put(List<Put> puts) throws IOException {
+        table.put(puts);
+      }
+
+      public void delete(Delete delete) throws IOException {
+        table.delete(delete);
+      }
+
+      public void delete(List<Delete> deletes) throws IOException {
+        table.delete(deletes);
+      }
+
+      public boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier,
+          byte[] value, Put put) throws IOException {
+        return table.checkAndPut(row, family, qualifier, value, put);
+      }
+
+      public boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier,
+          byte[] value, Delete delete) throws IOException {
+        return table.checkAndDelete(row, family, qualifier, value, delete);
+      }
+
+      public long incrementColumnValue(byte[] row, byte[] family,
+          byte[] qualifier, long amount) throws IOException {
+        return table.incrementColumnValue(row, family, qualifier, amount);
+      }
+
+      public long incrementColumnValue(byte[] row, byte[] family,
+          byte[] qualifier, long amount, boolean writeToWAL)
+          throws IOException {
+        return table.incrementColumnValue(row, family, qualifier, amount,
+          writeToWAL);
+      }
+
+      @Override
+      public Result increment(Increment increment) throws IOException {
+        return table.increment(increment);
+      }
+
+      public void flushCommits() throws IOException {
+        table.flushCommits();
+      }
+
+      public boolean isAutoFlush() {
+        return table.isAutoFlush();
+      }
+
+      public ResultScanner getScanner(Scan scan) throws IOException {
+        return table.getScanner(scan);
+      }
+
+      public ResultScanner getScanner(byte[] family) throws IOException {
+        return table.getScanner(family);
+      }
+
+      public ResultScanner getScanner(byte[] family, byte[] qualifier)
+          throws IOException {
+        return table.getScanner(family, qualifier);
+      }
+
+      public HTableDescriptor getTableDescriptor() throws IOException {
+        return table.getTableDescriptor();
+      }
+
+      public byte[] getTableName() {
+        return tableName;
+      }
+
+      public RowLock lockRow(byte[] row) throws IOException {
+        throw new RuntimeException(
+          "row locking is not allowed within the coprocessor environment");
+      }
+
+      public void unlockRow(RowLock rl) throws IOException {
+        throw new RuntimeException(
+          "row locking is not allowed within the coprocessor environment");
+      }
+
+      @Override
+      public void batch(List<Row> actions, Object[] results)
+          throws IOException, InterruptedException {
+        table.batch(actions, results);
+      }
+
+      @Override
+      public Object[] batch(List<Row> actions)
+          throws IOException, InterruptedException {
+        return table.batch(actions);
+      }
+
+      @Override
+      public Result[] get(List<Get> gets) throws IOException {
+        return table.get(gets);
+      }
+
+      @Override
+      public <T extends CoprocessorProtocol, R> void coprocessorExec(Class<T> protocol,
+          byte[] startKey, byte[] endKey, Batch.Call<T, R> callable,
+          Batch.Callback<R> callback) throws IOException, Throwable {
+        table.coprocessorExec(protocol, startKey, endKey, callable, callback);
+      }
+
+      @Override
+      public <T extends CoprocessorProtocol, R> Map<byte[], R> coprocessorExec(
+          Class<T> protocol, byte[] startKey, byte[] endKey, Batch.Call<T, R> callable)
+          throws IOException, Throwable {
+        return table.coprocessorExec(protocol, startKey, endKey, callable);
+      }
+
+      @Override
+      public <T extends CoprocessorProtocol> T coprocessorProxy(Class<T> protocol,
+          byte[] row) {
+        return table.coprocessorProxy(protocol, row);
+      }
+    }
+
+    /** The coprocessor */
+    public Coprocessor impl;
+    /** Chaining priority */
+    protected Coprocessor.Priority priority = Coprocessor.Priority.USER;
+    /** Current coprocessor state */
+    Coprocessor.State state = Coprocessor.State.UNINSTALLED;
+    /** Accounting for tables opened by the coprocessor */
+    protected List<HTableInterface> openTables =
+      Collections.synchronizedList(new ArrayList<HTableInterface>());
+    static final ThreadLocal<Boolean> bypass = new ThreadLocal<Boolean>() {
+      @Override protected Boolean initialValue() {
+        return Boolean.FALSE;
+      }
+    };
+    static final ThreadLocal<Boolean> complete = new ThreadLocal<Boolean>() {
+      @Override protected Boolean initialValue() {
+        return Boolean.FALSE;
+      }
+    };
+
+    /**
+     * Constructor
+     * @param impl the coprocessor instance
+     * @param priority chaining priority
+     */
+    public Environment(final Coprocessor impl, Coprocessor.Priority priority) {
+      this.impl = impl;
+      this.priority = priority;
+      this.state = Coprocessor.State.INSTALLED;
+    }
+
+    /** Initialize the environment */
+    public void startup() {
+      if (state == Coprocessor.State.INSTALLED ||
+          state == Coprocessor.State.STOPPED) {
+        state = Coprocessor.State.STARTING;
+        try {
+          impl.start(this);
+          state = Coprocessor.State.ACTIVE;
+        } catch (IOException ioe) {
+          LOG.error("Error starting coprocessor "+impl.getClass().getName(), ioe);
+        }
+      } else {
+        LOG.warn("Not starting coprocessor "+impl.getClass().getName()+
+            " because not inactive (state="+state.toString()+")");
+      }
+    }
+
+    /** Clean up the environment */
+    protected void shutdown() {
+      if (state == Coprocessor.State.ACTIVE) {
+        state = Coprocessor.State.STOPPING;
+        try {
+          impl.stop(this);
+          state = Coprocessor.State.STOPPED;
+        } catch (IOException ioe) {
+          LOG.error("Error stopping coprocessor "+impl.getClass().getName(), ioe);
+        }
+      } else {
+        LOG.warn("Not stopping coprocessor "+impl.getClass().getName()+
+            " because not active (state="+state.toString()+")");
+      }
+      // clean up any table references
+      for (HTableInterface table: openTables) {
+        try {
+          ((HTableWrapper)table).internalClose();
+        } catch (IOException e) {
+          // nothing can be done here
+          LOG.warn("Failed to close " +
+              Bytes.toStringBinary(table.getTableName()), e);
+        }
+      }
+    }
+
+    public boolean shouldBypass() {
+      boolean current = bypass.get();
+      bypass.set(false);
+      return current;
+    }
+
+    public boolean shouldComplete() {
+      boolean current = complete.get();
+      complete.set(false);
+      return current;
+    }
+
+    @Override
+    public Coprocessor getInstance() {
+      return impl;
+    }
+
+    @Override
+    public Coprocessor.Priority getPriority() {
+      return priority;
+    }
+
+    /** @return the coprocessor environment version */
+    @Override
+    public int getVersion() {
+      return Coprocessor.VERSION;
+    }
+
+    /** @return the HBase release */
+    @Override
+    public String getHBaseVersion() {
+      return VersionInfo.getVersion();
+    }
+
+    /**
+     * Open a table from within the Coprocessor environment
+     * @param tableName the table name
+     * @return an interface for manipulating the table
+     * @exception java.io.IOException Exception
+     */
+    @Override
+    public HTableInterface getTable(byte[] tableName) throws IOException {
+      return new HTableWrapper(tableName);
+    }
+
+    @Override
+    public void complete() {
+      complete.set(true);
+    }
+
+    @Override
+    public void bypass() {
+      bypass.set(true);
+    }
+  }}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java?rev=1051639&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java Tue Dec 21 20:39:26 2010
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import org.apache.hadoop.hbase.master.MasterServices;
+
+public interface MasterCoprocessorEnvironment extends CoprocessorEnvironment {
+  /** @return reference to the HMaster services */
+  MasterServices getMasterServices();
+}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java?rev=1051639&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterObserver.java Tue Dec 21 20:39:26 2010
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import org.apache.hadoop.hbase.*;
+
+import java.io.IOException;
+
+/**
+ * Defines coprocessor hooks for interacting with operations on the
+ * {@link org.apache.hadoop.hbase.master.HMaster} process.
+ */
+public interface MasterObserver extends Coprocessor {
+
+  /**
+   * Called before a new table is created by
+   * {@link org.apache.hadoop.hbase.master.HMaster}.
+   */
+  void preCreateTable(MasterCoprocessorEnvironment env,
+      HTableDescriptor desc, byte[][] splitKeys) throws IOException;
+
+  /**
+   * Called after the initial table regions have been created.
+   * @param env the environment to interact with the framework and master
+   * @param regions the initial regions created for the table
+   * @param sync whether the client call is waiting for region assignment to
+   * complete before returning
+   * @throws IOException
+   */
+  void postCreateTable(MasterCoprocessorEnvironment env,
+      HRegionInfo[] regions, boolean sync) throws IOException;
+
+  /**
+   * Called before {@link org.apache.hadoop.hbase.master.HMaster} deletes a
+   * table
+   */
+  void preDeleteTable(MasterCoprocessorEnvironment env, byte[] tableName)
+      throws IOException;
+
+  /**
+   * Called after the table has been deleted, before returning to the client.
+   */
+  void postDeleteTable(MasterCoprocessorEnvironment env, byte[] tableName)
+      throws IOException;
+
+  /**
+   * Called prior to modifying a table's properties.
+   */
+  void preModifyTable(MasterCoprocessorEnvironment env, final byte[] tableName,
+      HTableDescriptor htd) throws IOException;
+
+  /**
+   * Called after {@link org.apache.hadoop.hbase.master.HMaster} has modified
+   * the table's properties in all the table regions.
+   */
+  void postModifyTable(MasterCoprocessorEnvironment env, final byte[] tableName,
+      HTableDescriptor htd) throws IOException;
+
+  /**
+   * Called prior to adding a new column family to the table.
+   */
+  void preAddColumn(MasterCoprocessorEnvironment env, byte[] tableName,
+      HColumnDescriptor column) throws IOException;
+
+  /**
+   * Called after the new column family has been created.
+   */
+  void postAddColumn(MasterCoprocessorEnvironment env, byte[] tableName,
+      HColumnDescriptor column) throws IOException;
+
+  /**
+   * Called prior to modifying a column family's attributes.
+   */
+  void preModifyColumn(MasterCoprocessorEnvironment env,
+      byte [] tableName, HColumnDescriptor descriptor) throws IOException;
+
+  /**
+   * Called after the column family has been updated.
+   */
+  void postModifyColumn(MasterCoprocessorEnvironment env, byte[] tableName,
+      HColumnDescriptor descriptor) throws IOException;
+
+  /**
+   * Called prior to deleting the entire column family.
+   */
+  void preDeleteColumn(MasterCoprocessorEnvironment env,
+      final byte [] tableName, final byte[] c) throws IOException;
+
+  /**
+   * Called after the column family has been deleted.
+   */
+  void postDeleteColumn(MasterCoprocessorEnvironment env,
+      final byte [] tableName, final byte[] c) throws IOException;
+
+  /**
+   * Called prior to enabling a table.
+   */
+  void preEnableTable(MasterCoprocessorEnvironment env, final byte[] tableName)
+      throws IOException;
+
+  /**
+   * Called after the table has been enabled.
+   */
+  void postEnableTable(MasterCoprocessorEnvironment env, final byte[] tableName)
+      throws IOException;
+
+  /**
+   * Called prior to disabling a table.
+   */
+  void preDisableTable(MasterCoprocessorEnvironment env, final byte[] tableName)
+      throws IOException;
+
+  /**
+   * Called after the table has been disabled.
+   */
+  void postDisableTable(MasterCoprocessorEnvironment env, final byte[] tableName)
+      throws IOException;
+
+  /**
+   * Called prior to moving a given region from one region server to another.
+   */
+  void preMove(MasterCoprocessorEnvironment env, final HRegionInfo region,
+      final HServerInfo srcServer, final HServerInfo destServer)
+    throws UnknownRegionException;
+
+  /**
+   * Called after the region move has been requested.
+   */
+  void postMove(MasterCoprocessorEnvironment env, final HRegionInfo region,
+      final HServerInfo srcServer, final HServerInfo destServer)
+    throws UnknownRegionException;
+
+  /**
+   * Called prior to assigning a specific region.
+   */
+  void preAssign(MasterCoprocessorEnvironment env, final byte [] regionName,
+      final boolean force) throws IOException;
+
+  /**
+   * Called after the region assignment has been requested.
+   */
+  void postAssign(MasterCoprocessorEnvironment env, final HRegionInfo regionInfo)
+      throws IOException;
+
+  /**
+   * Called prior to unassigning a given region.
+   */
+  void preUnassign(MasterCoprocessorEnvironment env, final byte [] regionName,
+      final boolean force) throws IOException;
+
+  /**
+   * Called after the region unassignment has been requested.
+   */
+  void postUnassign(MasterCoprocessorEnvironment env,
+      final HRegionInfo regionInfo, final boolean force) throws IOException;
+
+  /**
+   * Called prior to requesting rebalancing of the cluster regions, though after
+   * the initial checks for regions in transition and the balance switch flag.
+   */
+  void preBalance(MasterCoprocessorEnvironment env) throws IOException;
+
+  /**
+   * Called after the balancing plan has been submitted.
+   */
+  void postBalance(MasterCoprocessorEnvironment env) throws IOException;
+
+  /**
+   * Called prior to modifying the flag used to enable/disable region balancing.
+   * @param env the coprocessor instance's environment
+   * @param newValue the new flag value submitted in the call
+   */
+  boolean preBalanceSwitch(MasterCoprocessorEnvironment env,
+      final boolean newValue) throws IOException;
+
+  /**
+   * Called after the flag to enable/disable balancing has changed.
+   * @param env the coprocessor instance's environment
+   * @param oldValue the previously set balanceSwitch value
+   * @param newValue the newly set balanceSwitch value
+   */
+  void postBalanceSwitch(MasterCoprocessorEnvironment env,
+      final boolean oldValue, final boolean newValue) throws IOException;
+
+  /**
+   * Called prior to shutting down the full HBase cluster, including this
+   * {@link org.apache.hadoop.hbase.master.HMaster} process.
+   */
+  void preShutdown(MasterCoprocessorEnvironment env) throws IOException;
+
+
+  /**
+   * Called immediatly prior to stopping this
+   * {@link org.apache.hadoop.hbase.master.HMaster} process.
+   */
+  void preStopMaster(MasterCoprocessorEnvironment env) throws IOException;
+}

Added: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java?rev=1051639&view=auto
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java (added)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java Tue Dec 21 20:39:26 2010
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010 The Apache Software Foundation
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import org.apache.hadoop.hbase.regionserver.HRegion;
+import org.apache.hadoop.hbase.regionserver.RegionServerServices;
+
+public interface RegionCoprocessorEnvironment extends CoprocessorEnvironment {
+  /** @return the region associated with this coprocessor */
+  public HRegion getRegion();
+
+  /** @return reference to the region server services */
+  public RegionServerServices getRegionServerServices();
+
+}

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java?rev=1051639&r1=1051638&r2=1051639&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java Tue Dec 21 20:39:26 2010
@@ -42,25 +42,25 @@ public interface RegionObserver extends 
    * Called before the region is reported as open to the master.
    * @param e the environment provided by the region server
    */
-  public void preOpen(final CoprocessorEnvironment e);
+  public void preOpen(final RegionCoprocessorEnvironment e);
 
   /**
    * Called after the region is reported as open to the master.
    * @param e the environment provided by the region server
    */
-  public void postOpen(final CoprocessorEnvironment e);
+  public void postOpen(final RegionCoprocessorEnvironment e);
 
   /**
    * Called before the memstore is flushed to disk.
    * @param e the environment provided by the region server
    */
-  public void preFlush(final CoprocessorEnvironment e);
+  public void preFlush(final RegionCoprocessorEnvironment e);
 
   /**
    * Called after the memstore is flushed to disk.
    * @param e the environment provided by the region server
    */
-  public void postFlush(final CoprocessorEnvironment e);
+  public void postFlush(final RegionCoprocessorEnvironment e);
 
   /**
    * Called before compaction.
@@ -68,7 +68,7 @@ public interface RegionObserver extends 
    * @param willSplit true if compaction will result in a split, false
    * otherwise
    */
-  public void preCompact(final CoprocessorEnvironment e,
+  public void preCompact(final RegionCoprocessorEnvironment e,
     final boolean willSplit);
 
   /**
@@ -77,7 +77,7 @@ public interface RegionObserver extends 
    * @param willSplit true if compaction will result in a split, false
    * otherwise
    */
-  public void postCompact(final CoprocessorEnvironment e,
+  public void postCompact(final RegionCoprocessorEnvironment e,
     final boolean willSplit);
 
   /**
@@ -85,7 +85,7 @@ public interface RegionObserver extends 
    * @param e the environment provided by the region server
    * (e.getRegion() returns the parent region)
    */
-  public void preSplit(final CoprocessorEnvironment e);
+  public void preSplit(final RegionCoprocessorEnvironment e);
 
   /**
    * Called after the region is split.
@@ -94,7 +94,7 @@ public interface RegionObserver extends 
    * @param l the left daughter region
    * @param r the right daughter region
    */
-  public void postSplit(final CoprocessorEnvironment e, final HRegion l,
+  public void postSplit(final RegionCoprocessorEnvironment e, final HRegion l,
     final HRegion r);
 
   /**
@@ -102,14 +102,16 @@ public interface RegionObserver extends 
    * @param e the environment provided by the region server
    * @param abortRequested true if the region server is aborting
    */
-  public void preClose(final CoprocessorEnvironment e, boolean abortRequested);
+  public void preClose(final RegionCoprocessorEnvironment e,
+      boolean abortRequested);
 
   /**
    * Called after the region is reported as closed to the master.
    * @param e the environment provided by the region server
    * @param abortRequested true if the region server is aborting
    */
-  public void postClose(final CoprocessorEnvironment e, boolean abortRequested);
+  public void postClose(final RegionCoprocessorEnvironment e,
+      boolean abortRequested);
 
   /**
    * Called before a client makes a GetClosestRowBefore request.
@@ -126,7 +128,7 @@ public interface RegionObserver extends 
    * is not bypassed.
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void preGetClosestRowBefore(final CoprocessorEnvironment e,
+  public void preGetClosestRowBefore(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final Result result)
     throws IOException;
 
@@ -141,7 +143,7 @@ public interface RegionObserver extends 
    * @param result the result to return to the client, modify as necessary
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void postGetClosestRowBefore(final CoprocessorEnvironment e,
+  public void postGetClosestRowBefore(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final Result result)
     throws IOException;
 
@@ -159,7 +161,7 @@ public interface RegionObserver extends 
    * is not bypassed.
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void preGet(final CoprocessorEnvironment e, final Get get,
+  public void preGet(final RegionCoprocessorEnvironment e, final Get get,
       final List<KeyValue> result)
     throws IOException;
 
@@ -173,7 +175,7 @@ public interface RegionObserver extends 
    * @param result the result to return to the client, modify as necessary
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void postGet(final CoprocessorEnvironment e, final Get get,
+  public void postGet(final RegionCoprocessorEnvironment e, final Get get,
       final List<KeyValue> result)
     throws IOException;
 
@@ -190,7 +192,7 @@ public interface RegionObserver extends 
    * @return the value to return to the client if bypassing default processing
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean preExists(final CoprocessorEnvironment e, final Get get,
+  public boolean preExists(final RegionCoprocessorEnvironment e, final Get get,
       final boolean exists)
     throws IOException;
 
@@ -205,7 +207,7 @@ public interface RegionObserver extends 
    * @return the result to return to the client
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean postExists(final CoprocessorEnvironment e, final Get get,
+  public boolean postExists(final RegionCoprocessorEnvironment e, final Get get,
       final boolean exists)
     throws IOException;
 
@@ -221,7 +223,7 @@ public interface RegionObserver extends 
    * @param writeToWAL true if the change should be written to the WAL
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void prePut(final CoprocessorEnvironment e, final Map<byte[],
+  public void prePut(final RegionCoprocessorEnvironment e, final Map<byte[],
       List<KeyValue>> familyMap, final boolean writeToWAL)
     throws IOException;
 
@@ -235,7 +237,7 @@ public interface RegionObserver extends 
    * @param writeToWAL true if the change should be written to the WAL
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void postPut(final CoprocessorEnvironment e, final Map<byte[],
+  public void postPut(final RegionCoprocessorEnvironment e, final Map<byte[],
       List<KeyValue>> familyMap, final boolean writeToWAL)
     throws IOException;
 
@@ -251,7 +253,7 @@ public interface RegionObserver extends 
    * @param writeToWAL true if the change should be written to the WAL
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void preDelete(final CoprocessorEnvironment e, final Map<byte[],
+  public void preDelete(final RegionCoprocessorEnvironment e, final Map<byte[],
       List<KeyValue>> familyMap, final boolean writeToWAL)
     throws IOException;
 
@@ -265,7 +267,7 @@ public interface RegionObserver extends 
    * @param writeToWAL true if the change should be written to the WAL
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void postDelete(final CoprocessorEnvironment e,
+  public void postDelete(final RegionCoprocessorEnvironment e,
       final Map<byte[], List<KeyValue>> familyMap, final boolean writeToWAL)
     throws IOException;
 
@@ -287,7 +289,7 @@ public interface RegionObserver extends 
    * processing
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean preCheckAndPut(final CoprocessorEnvironment e,
+  public boolean preCheckAndPut(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Put put, final boolean result)
     throws IOException;
@@ -307,7 +309,7 @@ public interface RegionObserver extends 
    * @return the possibly transformed return value to return to client
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean postCheckAndPut(final CoprocessorEnvironment e,
+  public boolean postCheckAndPut(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Put put, final boolean result)
     throws IOException;
@@ -329,7 +331,7 @@ public interface RegionObserver extends 
    * @return the value to return to client if bypassing default processing
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean preCheckAndDelete(final CoprocessorEnvironment e,
+  public boolean preCheckAndDelete(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Delete delete, final boolean result)
     throws IOException;
@@ -349,7 +351,7 @@ public interface RegionObserver extends 
    * @return the possibly transformed returned value to return to client
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean postCheckAndDelete(final CoprocessorEnvironment e,
+  public boolean postCheckAndDelete(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final byte [] value, final Delete delete, final boolean result)
     throws IOException;
@@ -370,7 +372,7 @@ public interface RegionObserver extends 
    * @return value to return to the client if bypassing default processing
    * @throws IOException if an error occurred on the coprocessor
    */
-  public long preIncrementColumnValue(final CoprocessorEnvironment e,
+  public long preIncrementColumnValue(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final long amount, final boolean writeToWAL)
     throws IOException;
@@ -390,7 +392,7 @@ public interface RegionObserver extends 
    * @return the result to return to the client
    * @throws IOException if an error occurred on the coprocessor
    */
-  public long postIncrementColumnValue(final CoprocessorEnvironment e,
+  public long postIncrementColumnValue(final RegionCoprocessorEnvironment e,
       final byte [] row, final byte [] family, final byte [] qualifier,
       final long amount, final boolean writeToWAL, final long result)
     throws IOException;
@@ -407,10 +409,9 @@ public interface RegionObserver extends 
    * @param result The result to return to the client if default processing
    * is bypassed. Can be modified. Will not be used if default processing
    * is not bypassed.
-   * @param writeToWAL true if the change should be written to the WAL
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void preIncrement(final CoprocessorEnvironment e,
+  public void preIncrement(final RegionCoprocessorEnvironment e,
       final Increment increment, final Result result)
     throws IOException;
 
@@ -421,11 +422,10 @@ public interface RegionObserver extends 
    * coprocessors
    * @param e the environment provided by the region server
    * @param increment increment object
-   * @param writeToWAL true if the change should be written to the WAL
    * @param result the result returned by increment, can be modified
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void postIncrement(final CoprocessorEnvironment e,
+  public void postIncrement(final RegionCoprocessorEnvironment e,
       final Increment increment, final Result result)
     throws IOException;
 
@@ -443,7 +443,7 @@ public interface RegionObserver extends 
    * overriding default behavior, null otherwise
    * @throws IOException if an error occurred on the coprocessor
    */
-  public InternalScanner preScannerOpen(final CoprocessorEnvironment e,
+  public InternalScanner preScannerOpen(final RegionCoprocessorEnvironment e,
       final Scan scan, final InternalScanner s)
     throws IOException;
 
@@ -458,7 +458,7 @@ public interface RegionObserver extends 
    * @return the scanner instance to use
    * @throws IOException if an error occurred on the coprocessor
    */
-  public InternalScanner postScannerOpen(final CoprocessorEnvironment e,
+  public InternalScanner postScannerOpen(final RegionCoprocessorEnvironment e,
       final Scan scan, final InternalScanner s)
     throws IOException;
 
@@ -479,7 +479,7 @@ public interface RegionObserver extends 
    * @return 'has more' indication that should be sent to client
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean preScannerNext(final CoprocessorEnvironment e,
+  public boolean preScannerNext(final RegionCoprocessorEnvironment e,
       final InternalScanner s, final List<KeyValue> result,
       final int limit, final boolean hasNext)
     throws IOException;
@@ -497,7 +497,7 @@ public interface RegionObserver extends 
    * @return 'has more' indication that should be sent to client
    * @throws IOException if an error occurred on the coprocessor
    */
-  public boolean postScannerNext(final CoprocessorEnvironment e,
+  public boolean postScannerNext(final RegionCoprocessorEnvironment e,
       final InternalScanner s, final List<KeyValue> result, final int limit,
       final boolean hasNext)
     throws IOException;
@@ -513,7 +513,7 @@ public interface RegionObserver extends 
    * @param s the scanner
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void preScannerClose(final CoprocessorEnvironment e,
+  public void preScannerClose(final RegionCoprocessorEnvironment e,
       final InternalScanner s)
     throws IOException;
 
@@ -526,7 +526,7 @@ public interface RegionObserver extends 
    * @param s the scanner
    * @throws IOException if an error occurred on the coprocessor
    */
-  public void postScannerClose(final CoprocessorEnvironment e,
+  public void postScannerClose(final RegionCoprocessorEnvironment e,
       final InternalScanner s)
     throws IOException;
 }

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/package-info.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/package-info.java?rev=1051639&r1=1051638&r2=1051639&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/package-info.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/coprocessor/package-info.java Tue Dec 21 20:39:26 2010
@@ -286,7 +286,7 @@ or by <code>HTableDescriptor</code> for 
 opened regions.)
 <h3>Load from configuration</h3>
 Whenever a region is opened, it will read coprocessor class names from
-<code>hbase.coprocessor.default.classes</code> from <code>Configuration</code>.
+<code>hbase.coprocessor.region.classes</code> from <code>Configuration</code>.
 Coprocessor framework will automatically load the configured classes as
 default coprocessors. The classes must be included in the classpath already.
 
@@ -294,7 +294,7 @@ default coprocessors. The classes must b
 <div style="background-color: #cccccc; padding: 2px">
 <blockquote><pre>
   &lt;property&gt;
-    &lt;name&gt;hbase.coprocessor.default.classes&lt;/name&gt;
+    &lt;name&gt;hbase.coprocessor.region.classes&lt;/name&gt;
     &lt;value&gt;org.apache.hadoop.hbase.coprocessor.AccessControllCoprocessor, org.apache.hadoop.hbase.coprocessor.ColumnAggregationProtocol&lt;/value&gt;
     &lt;description&gt;A comma-separated list of Coprocessors that are loaded by
     default. For any override coprocessor method from RegionObservor or

Modified: hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/HMaster.java
URL: http://svn.apache.org/viewvc/hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/HMaster.java?rev=1051639&r1=1051638&r2=1051639&view=diff
==============================================================================
--- hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/HMaster.java (original)
+++ hbase/trunk/src/main/java/org/apache/hadoop/hbase/master/HMaster.java Tue Dec 21 20:39:26 2010
@@ -170,6 +170,8 @@ implements HMasterInterface, HMasterRegi
   private Thread catalogJanitorChore;
   private LogCleaner logCleaner;
 
+  private MasterCoprocessorHost cpHost;
+
   /**
    * Initializes the HMaster. The steps are as follows:
    * <p>
@@ -369,6 +371,9 @@ implements HMasterInterface, HMasterRegi
       Long.toHexString(this.zooKeeper.getZooKeeper().getSessionId()) +
       ", cluster-up flag was=" + wasUp);
 
+    // initialize master side coprocessors before we start handling requests
+    this.cpHost = new MasterCoprocessorHost(this, this.conf);
+
     // start up all service threads.
     startServiceThreads();
 
@@ -675,6 +680,19 @@ implements HMasterInterface, HMasterRegi
           this.serverManager.getDeadServers());
         return false;
       }
+
+      if (this.cpHost != null) {
+        try {
+          if (this.cpHost.preBalance()) {
+            LOG.debug("Coprocessor bypassing balancer request");
+            return false;
+          }
+        } catch (IOException ioe) {
+          LOG.error("Error invoking master coprocessor preBalance()", ioe);
+          return false;
+        }
+      }
+
       Map<HServerInfo, List<HRegionInfo>> assignments =
         this.assignmentManager.getAssignments();
       // Returned Map from AM does not include mention of servers w/o assignments.
@@ -692,6 +710,14 @@ implements HMasterInterface, HMasterRegi
           this.assignmentManager.balance(plan);
         }
       }
+      if (this.cpHost != null) {
+        try {
+          this.cpHost.postBalance();
+        } catch (IOException ioe) {
+          // balancing already succeeded so don't change the result
+          LOG.error("Error invoking master coprocessor postBalance()", ioe);
+        }
+      }
     }
     return true;
   }
@@ -699,8 +725,19 @@ implements HMasterInterface, HMasterRegi
   @Override
   public boolean balanceSwitch(final boolean b) {
     boolean oldValue = this.balanceSwitch;
-    this.balanceSwitch = b;
-    LOG.info("Balance=" + b);
+    boolean newValue = b;
+    try {
+      if (this.cpHost != null) {
+        newValue = this.cpHost.preBalanceSwitch(newValue);
+      }
+      this.balanceSwitch = newValue;
+      LOG.info("Balance=" + newValue);
+      if (this.cpHost != null) {
+        this.cpHost.postBalanceSwitch(oldValue, newValue);
+      }
+    } catch (IOException ioe) {
+      LOG.warn("Error flipping balance switch", ioe);
+    }
     return oldValue;
   }
 
@@ -721,8 +758,15 @@ implements HMasterInterface, HMasterRegi
       this.assignmentManager.unassign(hri);
     } else {
       dest = this.serverManager.getServerInfo(new String(destServerName));
+
+      if (this.cpHost != null) {
+        this.cpHost.preMove(p.getFirst(), p.getSecond(), dest);
+      }
       RegionPlan rp = new RegionPlan(p.getFirst(), p.getSecond(), dest);
       this.assignmentManager.balance(rp);
+      if (this.cpHost != null) {
+        this.cpHost.postMove(p.getFirst(), p.getSecond(), dest);
+      }
     }
   }
 
@@ -737,6 +781,9 @@ implements HMasterInterface, HMasterRegi
     if (!isMasterRunning()) {
       throw new MasterNotRunningException();
     }
+    if (cpHost != null) {
+      cpHost.preCreateTable(desc, splitKeys);
+    }
     HRegionInfo [] newRegions = null;
     if(splitKeys == null || splitKeys.length == 0) {
       newRegions = new HRegionInfo [] { new HRegionInfo(desc, null, null) };
@@ -810,6 +857,10 @@ implements HMasterInterface, HMasterRegi
         }
       }
     }
+
+    if (cpHost != null) {
+      cpHost.postCreateTable(newRegions, sync);
+    }
   }
 
   private static boolean isCatalogTable(final byte [] tableName) {
@@ -818,32 +869,68 @@ implements HMasterInterface, HMasterRegi
   }
 
   public void deleteTable(final byte [] tableName) throws IOException {
+    if (cpHost != null) {
+      cpHost.preDeleteTable(tableName);
+    }
     this.executorService.submit(new DeleteTableHandler(tableName, this, this));
+    if (cpHost != null) {
+      cpHost.postDeleteTable(tableName);
+    }
   }
 
   public void addColumn(byte [] tableName, HColumnDescriptor column)
   throws IOException {
+    if (cpHost != null) {
+      cpHost.preAddColumn(tableName, column);
+    }
     new TableAddFamilyHandler(tableName, column, this, this).process();
+    if (cpHost != null) {
+      cpHost.postAddColumn(tableName, column);
+    }
   }
 
   public void modifyColumn(byte [] tableName, HColumnDescriptor descriptor)
   throws IOException {
+    if (cpHost != null) {
+      cpHost.preModifyColumn(tableName, descriptor);
+    }
     new TableModifyFamilyHandler(tableName, descriptor, this, this).process();
+    if (cpHost != null) {
+      cpHost.postModifyColumn(tableName, descriptor);
+    }
   }
 
   public void deleteColumn(final byte [] tableName, final byte [] c)
   throws IOException {
+    if (cpHost != null) {
+      cpHost.preDeleteColumn(tableName, c);
+    }
     new TableDeleteFamilyHandler(tableName, c, this, this).process();
+    if (cpHost != null) {
+      cpHost.postDeleteColumn(tableName, c);
+    }
   }
 
   public void enableTable(final byte [] tableName) throws IOException {
+    if (cpHost != null) {
+      cpHost.preEnableTable(tableName);
+    }
     this.executorService.submit(new EnableTableHandler(this, tableName,
       catalogTracker, assignmentManager));
+    if (cpHost != null) {
+      cpHost.postEnableTable(tableName);
+    }
   }
 
   public void disableTable(final byte [] tableName) throws IOException {
+    if (cpHost != null) {
+      cpHost.preDisableTable(tableName);
+    }
     this.executorService.submit(new DisableTableHandler(this, tableName,
       catalogTracker, assignmentManager));
+    if (cpHost != null) {
+      cpHost.postDisableTable(tableName);
+    }
   }
 
   /**
@@ -886,7 +973,13 @@ implements HMasterInterface, HMasterRegi
   @Override
   public void modifyTable(final byte[] tableName, HTableDescriptor htd)
   throws IOException {
+    if (cpHost != null) {
+      cpHost.preModifyTable(tableName, htd);
+    }
     this.executorService.submit(new ModifyTableHandler(tableName, htd, this, this));
+    if (cpHost != null) {
+      cpHost.postModifyTable(tableName, htd);
+    }
   }
 
   @Override
@@ -935,6 +1028,10 @@ implements HMasterInterface, HMasterRegi
     return zooKeeper;
   }
 
+  public MasterCoprocessorHost getCoprocessorHost() {
+    return cpHost;
+  }
+
   @Override
   public String getServerName() {
     return address.toString();
@@ -952,6 +1049,13 @@ implements HMasterInterface, HMasterRegi
 
   @Override
   public void shutdown() {
+    if (cpHost != null) {
+      try {
+        cpHost.preShutdown();
+      } catch (IOException ioe) {
+        LOG.error("Error call master coprocessor preShutdown()", ioe);
+      }
+    }
     this.serverManager.shutdownCluster();
     try {
       this.clusterStatusTracker.setClusterDown();
@@ -962,6 +1066,13 @@ implements HMasterInterface, HMasterRegi
 
   @Override
   public void stopMaster() {
+    if (cpHost != null) {
+      try {
+        cpHost.preStopMaster();
+      } catch (IOException ioe) {
+        LOG.error("Error call master coprocessor preStopMaster()", ioe);
+      }
+    }
     stop("Stopped by " + Thread.currentThread().getName());
   }
 
@@ -1008,10 +1119,18 @@ implements HMasterInterface, HMasterRegi
   @Override
   public void assign(final byte [] regionName, final boolean force)
   throws IOException {
+    if (cpHost != null) {
+      if (cpHost.preAssign(regionName, force)) {
+        return;
+      }
+    }
     Pair<HRegionInfo, HServerAddress> pair =
       MetaReader.getRegion(this.catalogTracker, regionName);
     if (pair == null) throw new UnknownRegionException(Bytes.toString(regionName));
     assignRegion(pair.getFirst());
+    if (cpHost != null) {
+      cpHost.postAssign(pair.getFirst());
+    }
   }
 
   public void assignRegion(HRegionInfo hri) {
@@ -1021,12 +1140,20 @@ implements HMasterInterface, HMasterRegi
   @Override
   public void unassign(final byte [] regionName, final boolean force)
   throws IOException {
+    if (cpHost != null) {
+      if (cpHost.preUnassign(regionName, force)) {
+        return;
+      }
+    }
     Pair<HRegionInfo, HServerAddress> pair =
       MetaReader.getRegion(this.catalogTracker, regionName);
     if (pair == null) throw new UnknownRegionException(Bytes.toString(regionName));
     HRegionInfo hri = pair.getFirst();
     if (force) this.assignmentManager.clearRegionFromTransition(hri);
     this.assignmentManager.unassign(hri, force);
+    if (cpHost != null) {
+      cpHost.postUnassign(hri, force);
+    }
   }
 
   /**