diff --git a/CHANGES.txt b/CHANGES.txt index 302c75f2eb6..003d77e4773 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -46,6 +46,9 @@ Release 0.91.0 - Unreleased HBASE-3532 HRegion#equals is broken (Ted Yu via Stack) HBASE-3697 Admin actions that use MetaReader to iterate regions need to skip offline ones + HBASE-3583 Coprocessors: scannerNext and scannerClose hooks are called + when HRegionInterface#get is invoked (Mingjie Lai via + Andrew Purtell) IMPROVEMENTS HBASE-3290 Max Compaction Size (Nicolas Spiegelberg via Stack) diff --git a/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java b/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java index 92d941accfe..5a0f0958b84 100644 --- a/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java +++ b/src/main/java/org/apache/hadoop/hbase/coprocessor/BaseRegionObserverCoprocessor.java @@ -206,14 +206,14 @@ public abstract class BaseRegionObserverCoprocessor implements RegionObserver { @Override public boolean preScannerNext(final RegionCoprocessorEnvironment e, - final InternalScanner s, final List results, + final InternalScanner s, final List results, final int limit, final boolean hasMore) throws IOException { return hasMore; } @Override public boolean postScannerNext(final RegionCoprocessorEnvironment e, - final InternalScanner s, final List results, final int limit, + final InternalScanner s, final List results, final int limit, final boolean hasMore) throws IOException { return hasMore; } diff --git a/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java b/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java index ae93a0a12be..35019580c32 100644 --- a/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java +++ b/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionObserver.java @@ -492,7 +492,7 @@ public interface RegionObserver extends Coprocessor { * @throws IOException if an error occurred on the coprocessor */ public boolean preScannerNext(final RegionCoprocessorEnvironment e, - final InternalScanner s, final List result, + final InternalScanner s, final List result, final int limit, final boolean hasNext) throws IOException; @@ -510,7 +510,7 @@ public interface RegionObserver extends Coprocessor { * @throws IOException if an error occurred on the coprocessor */ public boolean postScannerNext(final RegionCoprocessorEnvironment e, - final InternalScanner s, final List result, final int limit, + final InternalScanner s, final List result, final int limit, final boolean hasNext) throws IOException; diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java index 36eadb27205..b91e5fae885 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java @@ -2461,14 +2461,6 @@ public class HRegion implements HeapSize { // , Writable{ public synchronized boolean next(List outResults, int limit) throws IOException { - if (coprocessorHost != null) { - Boolean result = coprocessorHost.preScannerNext((InternalScanner)this, - outResults, limit); - if (result != null) { - return result.booleanValue(); - } - } - if (this.filterClosed) { throw new UnknownScannerException("Scanner was closed (timed out?) " + "after we renewed it. Could be caused by a very slow scanner " + @@ -2484,11 +2476,6 @@ public class HRegion implements HeapSize { // , Writable{ boolean returnResult = nextInternal(limit); - if (coprocessorHost != null) { - returnResult = coprocessorHost.postScannerNext((InternalScanner)this, - results, limit, returnResult); - } - outResults.addAll(results); resetFilters(); if (isFilterDone()) { @@ -2596,20 +2583,12 @@ public class HRegion implements HeapSize { // , Writable{ currentRow, 0, currentRow.length) <= isScan); } - public synchronized void close() throws IOException { - if (coprocessorHost != null) { - if (coprocessorHost.preScannerClose((InternalScanner)this)) { - return; - } - } + public synchronized void close() { if (storeHeap != null) { storeHeap.close(); storeHeap = null; } this.filterClosed = true; - if (coprocessorHost != null) { - coprocessorHost.postScannerClose((InternalScanner)this); - } } } diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java index 991f5dc73f5..154758015ba 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/HRegionServer.java @@ -1929,6 +1929,32 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, List results = new ArrayList(nbRows); long currentScanResultSize = 0; List values = new ArrayList(); + + // Call coprocessor. Get region info from scanner. + HRegion region = null; + if (s instanceof HRegion.RegionScanner) { + HRegion.RegionScanner rs = (HRegion.RegionScanner) s; + region = getRegion(rs.getRegionName().getRegionName()); + } else { + throw new IOException("InternalScanner implementation is expected " + + "to be HRegion.RegionScanner."); + } + if (region != null && region.getCoprocessorHost() != null) { + Boolean bypass = region.getCoprocessorHost().preScannerNext(s, + results, nbRows); + if (!results.isEmpty()) { + for (Result r : results) { + for (KeyValue kv : r.raw()) { + currentScanResultSize += kv.heapSize(); + } + } + } + if (bypass != null) { + return ((HRegion.RegionScanner) s).isFilterDone() && results.isEmpty() ? null + : results.toArray(new Result[0]); + } + } + for (int i = 0; i < nbRows && currentScanResultSize < maxScannerResultSize; i++) { requestCount.incrementAndGet(); @@ -1945,6 +1971,12 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, } values.clear(); } + + // coprocessor postNext hook + if (region != null && region.getCoprocessorHost() != null) { + region.getCoprocessorHost().postScannerNext(s, results, nbRows, true); + } + // Below is an ugly hack where we cast the InternalScanner to be a // HRegion.RegionScanner. The alternative is to change InternalScanner // interface but its used everywhere whereas we just need a bit of info @@ -1967,10 +1999,33 @@ public class HRegionServer implements HRegionInterface, HBaseRPCErrorHandler, checkOpen(); requestCount.incrementAndGet(); String scannerName = String.valueOf(scannerId); - InternalScanner s = scanners.remove(scannerName); + InternalScanner s = scanners.get(scannerName); + + HRegion region = null; + if (s != null) { + // call coprocessor. + if (s instanceof HRegion.RegionScanner) { + HRegion.RegionScanner rs = (HRegion.RegionScanner) s; + region = getRegion(rs.getRegionName().getRegionName()); + } else { + throw new IOException("InternalScanner implementation is expected " + + "to be HRegion.RegionScanner."); + } + if (region != null && region.getCoprocessorHost() != null) { + if (region.getCoprocessorHost().preScannerClose(s)) { + return; // bypass + } + } + } + + s = scanners.remove(scannerName); if (s != null) { s.close(); this.leases.cancelLease(scannerName); + + if (region != null && region.getCoprocessorHost() != null) { + region.getCoprocessorHost().postScannerClose(s); + } } } catch (Throwable t) { throw convertThrowableToIOE(cleanup(t)); diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java b/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java index 7fd56076287..66fb32104a0 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java @@ -911,7 +911,7 @@ public class RegionCoprocessorHost * @exception IOException Exception */ public Boolean preScannerNext(final InternalScanner s, - final List results, int limit) throws IOException { + final List results, int limit) throws IOException { try { boolean bypass = false; boolean hasNext = false; @@ -941,7 +941,7 @@ public class RegionCoprocessorHost * @exception IOException Exception */ public boolean postScannerNext(final InternalScanner s, - final List results, final int limit, boolean hasMore) + final List results, final int limit, boolean hasMore) throws IOException { try { coprocessorLock.readLock().lock(); diff --git a/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java b/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java index 2612b101530..0ce21475f60 100644 --- a/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java +++ b/src/test/java/org/apache/hadoop/hbase/coprocessor/SimpleRegionObserver.java @@ -35,6 +35,7 @@ import org.apache.hadoop.hbase.client.Get; import org.apache.hadoop.hbase.client.Increment; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.regionserver.HRegion; +import org.apache.hadoop.hbase.regionserver.InternalScanner; import org.apache.hadoop.hbase.util.Bytes; /** @@ -68,6 +69,10 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { boolean hadPostIncrement = false; boolean hadPreWALRestored = false; boolean hadPostWALRestored = false; + boolean hadPreScannerNext = false; + boolean hadPostScannerNext = false; + boolean hadPreScannerClose = false; + boolean hadPostScannerClose = false; @Override public void preOpen(RegionCoprocessorEnvironment e) { @@ -135,6 +140,35 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { hadPostCompact = true; } + + @Override + public boolean preScannerNext(final RegionCoprocessorEnvironment e, + final InternalScanner s, final List results, + final int limit, final boolean hasMore) throws IOException { + hadPreScannerNext = true; + return hasMore; + } + + @Override + public boolean postScannerNext(final RegionCoprocessorEnvironment e, + final InternalScanner s, final List results, final int limit, + final boolean hasMore) throws IOException { + hadPostScannerNext = true; + return hasMore; + } + + @Override + public void preScannerClose(final RegionCoprocessorEnvironment e, + final InternalScanner s) throws IOException { + hadPreScannerClose = true; + } + + @Override + public void postScannerClose(final RegionCoprocessorEnvironment e, + final InternalScanner s) throws IOException { + hadPostScannerClose = true; + } + public boolean wasCompacted() { return hadPreCompact && hadPostCompact; } @@ -146,10 +180,7 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(e.getRegion()); assertNotNull(get); assertNotNull(results); - if (Arrays.equals(e.getRegion().getTableDesc().getName(), - TestRegionObserverInterface.TEST_TABLE)) { - hadPreGet = true; - } + hadPreGet = true; } @Override @@ -178,8 +209,8 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertTrue(foundA); assertTrue(foundB); assertTrue(foundC); - hadPostGet = true; } + hadPostGet = true; } @Override @@ -205,8 +236,8 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(kvs.get(0)); assertTrue(Bytes.equals(kvs.get(0).getQualifier(), TestRegionObserverInterface.C)); - hadPrePut = true; } + hadPrePut = true; } @Override @@ -232,8 +263,8 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(kvs.get(0)); assertTrue(Bytes.equals(kvs.get(0).getQualifier(), TestRegionObserverInterface.C)); - hadPostPut = true; } + hadPostPut = true; } @Override @@ -242,8 +273,7 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(e); assertNotNull(e.getRegion()); assertNotNull(familyMap); - if (beforeDelete && e.getRegion().getTableDesc().getName().equals( - TestRegionObserverInterface.TEST_TABLE)) { + if (beforeDelete) { hadPreDeleted = true; } } @@ -254,11 +284,8 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(e); assertNotNull(e.getRegion()); assertNotNull(familyMap); - if (Arrays.equals(e.getRegion().getTableDesc().getName(), - TestRegionObserverInterface.TEST_TABLE)) { - beforeDelete = false; - hadPostDeleted = true; - } + beforeDelete = false; + hadPostDeleted = true; } @Override @@ -269,8 +296,7 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(e.getRegion()); assertNotNull(row); assertNotNull(result); - if (beforeDelete && e.getRegion().getTableDesc().getName().equals( - TestRegionObserverInterface.TEST_TABLE)) { + if (beforeDelete) { hadPreGetClosestRowBefore = true; } } @@ -283,63 +309,59 @@ public class SimpleRegionObserver extends BaseRegionObserverCoprocessor { assertNotNull(e.getRegion()); assertNotNull(row); assertNotNull(result); - if (Arrays.equals(e.getRegion().getTableDesc().getName(), - TestRegionObserverInterface.TEST_TABLE)) { - hadPostGetClosestRowBefore = true; - } + hadPostGetClosestRowBefore = true; } @Override public void preIncrement(final RegionCoprocessorEnvironment e, final Increment increment, final Result result) throws IOException { - if (Arrays.equals(e.getRegion().getTableDesc().getName(), - TestRegionObserverInterface.TEST_TABLE_2)) { - hadPreIncrement = true; - } + hadPreIncrement = true; } @Override public void postIncrement(final RegionCoprocessorEnvironment e, final Increment increment, final Result result) throws IOException { - if (Arrays.equals(e.getRegion().getTableDesc().getName(), - TestRegionObserverInterface.TEST_TABLE_2)) { - hadPostIncrement = true; - } + hadPostIncrement = true; } - boolean hadPreGet() { + public boolean hadPreGet() { return hadPreGet; } - boolean hadPostGet() { + public boolean hadPostGet() { return hadPostGet; } - boolean hadPrePut() { + public boolean hadPrePut() { return hadPrePut; } - boolean hadPostPut() { + public boolean hadPostPut() { return hadPostPut; } - - boolean hadDelete() { + public boolean hadDelete() { return !beforeDelete; } - boolean hadPreIncrement() { + public boolean hadPreIncrement() { return hadPreIncrement; } - boolean hadPostIncrement() { + public boolean hadPostIncrement() { return hadPostIncrement; } - boolean hadPreWALRestored() { + public boolean hadPreWALRestored() { return hadPreWALRestored; } - boolean hadPostWALRestored() { + public boolean hadPostWALRestored() { return hadPostWALRestored; } + public boolean wasScannerNextCalled() { + return hadPreScannerNext && hadPostScannerNext; + } + public boolean wasScannerCloseCalled() { + return hadPreScannerClose && hadPostScannerClose; + } } diff --git a/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java b/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java index b63e9dd1cd6..8c9e5b8e2b4 100644 --- a/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java +++ b/src/test/java/org/apache/hadoop/hbase/coprocessor/TestRegionObserverInterface.java @@ -21,6 +21,7 @@ package org.apache.hadoop.hbase.coprocessor; import java.io.IOException; +import java.lang.reflect.Method; import java.util.Arrays; import org.apache.commons.logging.Log; @@ -32,11 +33,7 @@ import org.apache.hadoop.hbase.HColumnDescriptor; import org.apache.hadoop.hbase.HRegionInfo; import org.apache.hadoop.hbase.HTableDescriptor; import org.apache.hadoop.hbase.MiniHBaseCluster; -import org.apache.hadoop.hbase.client.Delete; -import org.apache.hadoop.hbase.client.Get; -import org.apache.hadoop.hbase.client.HTable; -import org.apache.hadoop.hbase.client.Increment; -import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.client.*; import org.apache.hadoop.hbase.coprocessor.Coprocessor.Priority; import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost; import org.apache.hadoop.hbase.regionserver.HRegion; @@ -47,6 +44,8 @@ import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; +import javax.el.MethodNotFoundException; + import static org.junit.Assert.*; public class TestRegionObserverInterface { @@ -54,16 +53,10 @@ public class TestRegionObserverInterface { static final String DIR = "test/build/data/TestRegionObserver/"; public static final byte[] TEST_TABLE = Bytes.toBytes("TestTable"); - public static final byte[] TEST_TABLE_2 = Bytes.toBytes("TestTable2"); - public static final byte[] TEST_FAMILY = Bytes.toBytes("TestFamily"); - public static final byte[] TEST_QUALIFIER = Bytes.toBytes("TestQualifier"); - public final static byte[] A = Bytes.toBytes("a"); public final static byte[] B = Bytes.toBytes("b"); public final static byte[] C = Bytes.toBytes("c"); public final static byte[] ROW = Bytes.toBytes("testrow"); - public final static byte[] ROW1 = Bytes.toBytes("testrow1"); - public final static byte[] ROW2 = Bytes.toBytes("testrow2"); private static final int ROWSIZE = 20; private static byte [][] ROWS = makeN(ROW, ROWSIZE); @@ -80,17 +73,6 @@ public class TestRegionObserverInterface { util.startMiniCluster(2); cluster = util.getMiniHBaseCluster(); - - HTable table = util.createTable(TEST_TABLE_2, TEST_FAMILY); - - for(int i = 0; i < ROWSIZE; i++) { - Put put = new Put(ROWS[i]); - put.add(TEST_FAMILY, TEST_QUALIFIER, Bytes.toBytes(i)); - table.put(put); - } - - // sleep here is an ugly hack to allow region transitions to finish - Thread.sleep(5000); } @AfterClass @@ -98,87 +80,157 @@ public class TestRegionObserverInterface { util.shutdownMiniCluster(); } - HRegion initHRegion (byte [] tableName, String callingMethod, - Configuration conf, Class implClass, byte [] ... families) - throws IOException{ - HTableDescriptor htd = new HTableDescriptor(tableName); - for(byte [] family : families) { - htd.addFamily(new HColumnDescriptor(family)); - } - HRegionInfo info = new HRegionInfo(htd, null, null, false); - Path path = new Path(DIR + callingMethod); - // this following piece is a hack. currently a coprocessorHost - // is secretly loaded at OpenRegionHandler. we don't really - // start a region server here, so just manually create cphost - // and set it to region. - HRegion r = HRegion.createHRegion(info, path, conf); - RegionCoprocessorHost host = new RegionCoprocessorHost(r, null, conf); - r.setCoprocessorHost(host); - host.load(implClass, Priority.USER); - return r; - } - @Test public void testRegionObserver() throws IOException { - byte[] TABLE = Bytes.toBytes(getClass().getName()); - byte[][] FAMILIES = new byte[][] { A, B, C } ; + byte[] tableName = TEST_TABLE; + // recreate table every time in order to reset the status of the + // coproccessor. + HTable table = util.createTable(tableName, new byte[][] {A, B, C}); + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut", + "hadDelete"}, + TEST_TABLE, + new Boolean[] {false, false, false, false, false}); Put put = new Put(ROW); put.add(A, A, A); put.add(B, B, B); put.add(C, C, C); + table.put(put); + + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut", + "hadDelete"}, + TEST_TABLE, + new Boolean[] {false, false, true, true, false} + ); Get get = new Get(ROW); get.addColumn(A, A); get.addColumn(B, B); get.addColumn(C, C); + table.get(get); + + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut", + "hadDelete"}, + TEST_TABLE, + new Boolean[] {true, true, true, true, false} + ); Delete delete = new Delete(ROW); delete.deleteColumn(A, A); delete.deleteColumn(B, B); delete.deleteColumn(C, C); + table.delete(delete); - for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) { - for (HRegionInfo r : t.getRegionServer().getOnlineRegions()) { - if (!Arrays.equals(r.getTableDesc().getName(), TEST_TABLE)) { - continue; - } - RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()). - getCoprocessorHost(); - Coprocessor c = cph.findCoprocessor(SimpleRegionObserver.class.getName()); - assertNotNull(c); - assertTrue(((SimpleRegionObserver)c).hadPreGet()); - assertTrue(((SimpleRegionObserver)c).hadPostGet()); - assertTrue(((SimpleRegionObserver)c).hadPrePut()); - assertTrue(((SimpleRegionObserver)c).hadPostPut()); - assertTrue(((SimpleRegionObserver)c).hadDelete()); - } - } + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut", + "hadDelete"}, + TEST_TABLE, + new Boolean[] {true, true, true, true, true} + ); + util.deleteTable(tableName); } - // TODO: add tests for other methods which need to be tested - // at region servers. - @Test public void testIncrementHook() throws IOException { - HTable table = new HTable(util.getConfiguration(), TEST_TABLE_2); + byte[] tableName = TEST_TABLE; + HTable table = util.createTable(tableName, new byte[][] {A, B, C}); Increment inc = new Increment(Bytes.toBytes(0)); - inc.addColumn(TEST_FAMILY, TEST_QUALIFIER, 1); + inc.addColumn(A, A, 1); + + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreIncrement", "hadPostIncrement"}, + tableName, + new Boolean[] {false, false} + ); table.increment(inc); - for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) { - for (HRegionInfo r : t.getRegionServer().getOnlineRegions()) { - if (!Arrays.equals(r.getTableDesc().getName(), TEST_TABLE_2)) { - continue; - } - RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()). - getCoprocessorHost(); - Coprocessor c = cph.findCoprocessor(SimpleRegionObserver.class.getName()); - assertTrue(((SimpleRegionObserver)c).hadPreIncrement()); - assertTrue(((SimpleRegionObserver)c).hadPostIncrement()); + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreIncrement", "hadPostIncrement"}, + tableName, + new Boolean[] {true, true} + ); + util.deleteTable(tableName); + } + + @Test + // HBase-3583 + public void testHBase3583() throws IOException { + byte[] tableName = Bytes.toBytes("testHBase3583"); + util.createTable(tableName, new byte[][] {A, B, C}); + + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreGet", "hadPostGet", "wasScannerNextCalled", + "wasScannerCloseCalled"}, + tableName, + new Boolean[] {false, false, false, false} + ); + + HTable table = new HTable(util.getConfiguration(), tableName); + Put put = new Put(ROW); + put.add(A, A, A); + table.put(put); + + Get get = new Get(ROW); + get.addColumn(A, A); + table.get(get); + + // verify that scannerNext and scannerClose upcalls won't be invoked + // when we perform get(). + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"hadPreGet", "hadPostGet", "wasScannerNextCalled", + "wasScannerCloseCalled"}, + tableName, + new Boolean[] {true, true, false, false} + ); + + Scan s = new Scan(); + ResultScanner scanner = table.getScanner(s); + try { + for (Result rr = scanner.next(); rr != null; rr = scanner.next()) { } + } finally { + scanner.close(); + } + + // now scanner hooks should be invoked. + verifyMethodResult(SimpleRegionObserver.class, + new String[] {"wasScannerNextCalled", "wasScannerCloseCalled"}, + tableName, + new Boolean[] {true, true} + ); + util.deleteTable(tableName); + } + + // check each region whether the coprocessor upcalls are called or not. + private void verifyMethodResult(Class c, String methodName[], byte[] tableName, + Object value[]) throws IOException { + try { + for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) { + for (HRegionInfo r : t.getRegionServer().getOnlineRegions()) { + if (!Arrays.equals(r.getTableDesc().getName(), tableName)) { + continue; + } + RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()). + getCoprocessorHost(); + + Coprocessor cp = cph.findCoprocessor(c.getName()); + assertNotNull(cp); + for (int i = 0; i < methodName.length; ++i) { + Method m = c.getMethod(methodName[i]); + Object o = m.invoke(cp); + assertTrue("Result of " + c.getName() + "." + methodName[i] + + " is expected to be " + value[i].toString() + + ", while we get " + o.toString(), o.equals(value[i])); + } + } + } + } catch (Exception e) { + throw new IOException(e.toString()); } }