Index: src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorEndpoint.java =================================================================== --- src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorEndpoint.java (revision 1211645) +++ src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorEndpoint.java (working copy) @@ -31,8 +31,13 @@ import org.apache.hadoop.hbase.client.Put; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.coprocessor.Batch; +import org.apache.hadoop.hbase.client.coprocessor.Exec; +import org.apache.hadoop.hbase.io.HbaseObjectWritable; import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.io.DataInputBuffer; +import org.apache.hadoop.io.DataOutputBuffer; import org.apache.hadoop.io.Text; +import org.apache.hadoop.io.Writable; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; @@ -48,6 +53,9 @@ private static final byte[] TEST_FAMILY = Bytes.toBytes("TestFamily"); private static final byte[] TEST_QUALIFIER = Bytes.toBytes("TestQualifier"); private static byte[] ROW = Bytes.toBytes("testRow"); + + private static final String protocolName = "org.apache.hadoop.hbase.CustomProtocol"; + private static final String methodName = "myFunc"; private static final int ROWSIZE = 20; private static final int rowSeperator1 = 5; @@ -171,6 +179,28 @@ assertEquals("Invalid result", sumResult, expectedResult); } + @Test + public void testExecDeserialization() throws IOException { + DataOutputBuffer dob = new DataOutputBuffer(); + dob.writeUTF(methodName); + dob.writeInt(1); + Scan scan = new Scan(); + HbaseObjectWritable.writeObject(dob, scan, Scan.class, new Configuration()); + dob.writeUTF("org.apache.hadoop.hbase.client.Scan"); + Bytes.writeByteArray(dob, new byte[]{'a'}); + // this is the dynamic protocol name + dob.writeUTF(protocolName); + + DataInputBuffer dib = new DataInputBuffer(); + dib.reset(dob.getData(), dob.getLength()); + + Exec after = new Exec(); + after.readFields(dib); + // no error thrown + assertEquals(after.getProtocolName(), protocolName); + assertEquals(after.getMethodName(), methodName); + } + private static byte[][] makeN(byte[] base, int n) { byte[][] ret = new byte[n][]; for (int i = 0; i < n; i++) { Index: src/main/java/org/apache/hadoop/hbase/client/coprocessor/Exec.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/client/coprocessor/Exec.java (revision 1211645) +++ src/main/java/org/apache/hadoop/hbase/client/coprocessor/Exec.java (working copy) @@ -56,6 +56,7 @@ /** Row key used as a reference for any region lookups */ private byte[] referenceRow; private Class protocol; + private String protocolName; public Exec() { } @@ -68,8 +69,13 @@ this.conf = configuration; this.referenceRow = row; this.protocol = protocol; + this.protocolName = protocol.getName(); } + public String getProtocolName() { + return protocolName; + } + public Class getProtocol() { return protocol; } @@ -117,12 +123,13 @@ } // fields for Exec referenceRow = Bytes.readByteArray(in); - String protocolName = in.readUTF(); + protocolName = in.readUTF(); + /* can't do eager instantiation. pass it as a string and deserialize later. try { protocol = (Class)conf.getClassByName(protocolName); } catch (ClassNotFoundException cnfe) { throw new IOException("Protocol class "+protocolName+" not found", cnfe); - } + } */ } } Index: src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java =================================================================== --- src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (revision 1211645) +++ src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java (working copy) @@ -50,6 +50,7 @@ import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.ReentrantReadWriteLock; +import com.google.common.collect.Maps; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.conf.Configuration; @@ -181,6 +182,9 @@ // Registered region protocol handlers private ClassToInstanceMap protocolHandlers = MutableClassToInstanceMap.create(); + + private Map> + protocolHandlerNames = Maps.newHashMap(); /** * Temporary subdirectory of the region directory used for compaction output. @@ -4397,6 +4401,7 @@ } protocolHandlers.putInstance(protocol, handler); + protocolHandlerNames.put(protocol.getName(), protocol); if (LOG.isDebugEnabled()) { LOG.debug("Registered protocol handler: region="+ Bytes.toStringBinary(getRegionName())+" protocol="+protocol.getName()); @@ -4422,6 +4427,19 @@ public ExecResult exec(Exec call) throws IOException { Class protocol = call.getProtocol(); + if (protocol == null) { + String protocolName = call.getProtocolName(); + if (LOG.isDebugEnabled()) { + LOG.debug("Received dynamic protocol exec call with protocolName " + protocolName); + } + // detect the actual protocol class + protocol = protocolHandlerNames.get(protocolName); + if (protocol == null) { + throw new HBaseRPC.UnknownProtocolException(protocol, + "No matching handler for protocol "+protocolName+ + " in region "+Bytes.toStringBinary(getRegionName())); + } + } if (!protocolHandlers.containsKey(protocol)) { throw new HBaseRPC.UnknownProtocolException(protocol, "No matching handler for protocol "+protocol.getName()+