View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.coprocessor;
21  
22  import static org.junit.Assert.assertArrayEquals;
23  import static org.junit.Assert.assertFalse;
24  import static org.junit.Assert.assertNotNull;
25  import static org.junit.Assert.assertTrue;
26  
27  import java.io.IOException;
28  import java.lang.reflect.Method;
29  import java.util.ArrayList;
30  import java.util.List;
31  
32  import org.apache.commons.logging.Log;
33  import org.apache.commons.logging.LogFactory;
34  import org.apache.hadoop.conf.Configuration;
35  import org.apache.hadoop.fs.FileSystem;
36  import org.apache.hadoop.fs.Path;
37  import org.apache.hadoop.hbase.Cell;
38  import org.apache.hadoop.hbase.CellUtil;
39  import org.apache.hadoop.hbase.Coprocessor;
40  import org.apache.hadoop.hbase.HBaseTestingUtility;
41  import org.apache.hadoop.hbase.HColumnDescriptor;
42  import org.apache.hadoop.hbase.HRegionInfo;
43  import org.apache.hadoop.hbase.HTableDescriptor;
44  import org.apache.hadoop.hbase.KeyValue;
45  import org.apache.hadoop.hbase.testclassification.MediumTests;
46  import org.apache.hadoop.hbase.MiniHBaseCluster;
47  import org.apache.hadoop.hbase.ServerName;
48  import org.apache.hadoop.hbase.TableName;
49  import org.apache.hadoop.hbase.client.Append;
50  import org.apache.hadoop.hbase.client.Delete;
51  import org.apache.hadoop.hbase.client.Durability;
52  import org.apache.hadoop.hbase.client.Get;
53  import org.apache.hadoop.hbase.client.HBaseAdmin;
54  import org.apache.hadoop.hbase.client.HTable;
55  import org.apache.hadoop.hbase.client.Increment;
56  import org.apache.hadoop.hbase.client.Put;
57  import org.apache.hadoop.hbase.client.Result;
58  import org.apache.hadoop.hbase.client.ResultScanner;
59  import org.apache.hadoop.hbase.client.RowMutations;
60  import org.apache.hadoop.hbase.client.Scan;
61  import org.apache.hadoop.hbase.filter.FilterAllFilter;
62  import org.apache.hadoop.hbase.io.hfile.CacheConfig;
63  import org.apache.hadoop.hbase.io.hfile.HFile;
64  import org.apache.hadoop.hbase.io.hfile.HFileContext;
65  import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder;
66  import org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles;
67  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
68  import org.apache.hadoop.hbase.regionserver.HRegion;
69  import org.apache.hadoop.hbase.regionserver.InternalScanner;
70  import org.apache.hadoop.hbase.regionserver.RegionCoprocessorHost;
71  import org.apache.hadoop.hbase.regionserver.ScanType;
72  import org.apache.hadoop.hbase.regionserver.Store;
73  import org.apache.hadoop.hbase.regionserver.StoreFile;
74  import org.apache.hadoop.hbase.util.Bytes;
75  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
76  import org.apache.hadoop.hbase.util.JVMClusterUtil;
77  import org.apache.hadoop.hbase.util.Threads;
78  import org.junit.AfterClass;
79  import org.junit.BeforeClass;
80  import org.junit.Test;
81  import org.junit.experimental.categories.Category;
82  
83  @Category(MediumTests.class)
84  public class TestRegionObserverInterface {
85    static final Log LOG = LogFactory.getLog(TestRegionObserverInterface.class);
86  
87    public static final TableName TEST_TABLE = TableName.valueOf("TestTable");
88    public final static byte[] A = Bytes.toBytes("a");
89    public final static byte[] B = Bytes.toBytes("b");
90    public final static byte[] C = Bytes.toBytes("c");
91    public final static byte[] ROW = Bytes.toBytes("testrow");
92  
93    private static HBaseTestingUtility util = new HBaseTestingUtility();
94    private static MiniHBaseCluster cluster = null;
95  
96    @BeforeClass
97    public static void setupBeforeClass() throws Exception {
98      // set configure to indicate which cp should be loaded
99      Configuration conf = util.getConfiguration();
100     conf.setBoolean("hbase.master.distributed.log.replay", true);
101     conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
102         "org.apache.hadoop.hbase.coprocessor.SimpleRegionObserver");
103 
104     util.startMiniCluster();
105     cluster = util.getMiniHBaseCluster();
106   }
107 
108   @AfterClass
109   public static void tearDownAfterClass() throws Exception {
110     util.shutdownMiniCluster();
111   }
112 
113   @Test
114   public void testRegionObserver() throws IOException {
115     TableName tableName = TableName.valueOf(TEST_TABLE.getNameAsString() + ".testRegionObserver");
116     // recreate table every time in order to reset the status of the
117     // coprocessor.
118     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
119     try {
120       verifyMethodResult(SimpleRegionObserver.class, new String[] { "hadPreGet", "hadPostGet",
121           "hadPrePut", "hadPostPut", "hadDelete", "hadPostStartRegionOperation",
122           "hadPostCloseRegionOperation", "hadPostBatchMutateIndispensably" }, tableName,
123         new Boolean[] { false, false, false, false, false, false, false, false });
124 
125       Put put = new Put(ROW);
126       put.add(A, A, A);
127       put.add(B, B, B);
128       put.add(C, C, C);
129       table.put(put);
130 
131       verifyMethodResult(SimpleRegionObserver.class, new String[] { "hadPreGet", "hadPostGet",
132           "hadPrePut", "hadPostPut", "hadPreBatchMutate", "hadPostBatchMutate", "hadDelete",
133           "hadPostStartRegionOperation", "hadPostCloseRegionOperation",
134           "hadPostBatchMutateIndispensably" }, TEST_TABLE, new Boolean[] { false, false, true,
135           true, true, true, false, true, true, true });
136 
137       verifyMethodResult(SimpleRegionObserver.class,
138           new String[] {"getCtPreOpen", "getCtPostOpen", "getCtPreClose", "getCtPostClose"},
139           tableName,
140           new Integer[] {1, 1, 0, 0});
141 
142       Get get = new Get(ROW);
143       get.addColumn(A, A);
144       get.addColumn(B, B);
145       get.addColumn(C, C);
146       table.get(get);
147 
148       verifyMethodResult(SimpleRegionObserver.class,
149           new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
150       "hadDelete", "hadPrePreparedDeleteTS"},
151       tableName,
152       new Boolean[] {true, true, true, true, false, false}
153           );
154 
155       Delete delete = new Delete(ROW);
156       delete.deleteColumn(A, A);
157       delete.deleteColumn(B, B);
158       delete.deleteColumn(C, C);
159       table.delete(delete);
160 
161       verifyMethodResult(SimpleRegionObserver.class,
162           new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
163         "hadPreBatchMutate", "hadPostBatchMutate", "hadDelete", "hadPrePreparedDeleteTS"},
164         tableName,
165         new Boolean[] {true, true, true, true, true, true, true, true}
166           );
167     } finally {
168       util.deleteTable(tableName);
169       table.close();
170     }
171     verifyMethodResult(SimpleRegionObserver.class,
172         new String[] {"getCtPreOpen", "getCtPostOpen", "getCtPreClose", "getCtPostClose"},
173         tableName,
174         new Integer[] {1, 1, 1, 1});
175   }
176 
177   @Test
178   public void testRowMutation() throws IOException {
179     TableName tableName = TableName.valueOf(TEST_TABLE.getNameAsString() + ".testRowMutation");
180     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
181     try {
182       verifyMethodResult(SimpleRegionObserver.class,
183         new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
184             "hadDeleted"},
185         tableName,
186         new Boolean[] {false, false, false, false, false});
187       Put put = new Put(ROW);
188       put.add(A, A, A);
189       put.add(B, B, B);
190       put.add(C, C, C);
191 
192       Delete delete = new Delete(ROW);
193       delete.deleteColumn(A, A);
194       delete.deleteColumn(B, B);
195       delete.deleteColumn(C, C);
196 
197       RowMutations arm = new RowMutations(ROW);
198       arm.add(put);
199       arm.add(delete);
200       table.mutateRow(arm);
201 
202       verifyMethodResult(SimpleRegionObserver.class,
203           new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
204       "hadDeleted"},
205       tableName,
206       new Boolean[] {false, false, true, true, true}
207           );
208     } finally {
209       util.deleteTable(tableName);
210       table.close();
211     }
212   }
213 
214   @Test
215   public void testIncrementHook() throws IOException {
216     TableName tableName = TableName.valueOf(TEST_TABLE.getNameAsString() + ".testIncrementHook");
217     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
218     try {
219       Increment inc = new Increment(Bytes.toBytes(0));
220       inc.addColumn(A, A, 1);
221 
222       verifyMethodResult(SimpleRegionObserver.class,
223           new String[] {"hadPreIncrement", "hadPostIncrement", "hadPreIncrementAfterRowLock"},
224           tableName,
225           new Boolean[] {false, false, false}
226           );
227 
228       table.increment(inc);
229 
230       verifyMethodResult(SimpleRegionObserver.class,
231           new String[] {"hadPreIncrement", "hadPostIncrement", "hadPreIncrementAfterRowLock"},
232           tableName,
233           new Boolean[] {true, true, true}
234           );
235     } finally {
236       util.deleteTable(tableName);
237       table.close();
238     }
239   }
240 
241   @Test
242   public void testCheckAndPutHooks() throws IOException {
243     TableName tableName = 
244         TableName.valueOf(TEST_TABLE.getNameAsString() + ".testCheckAndPutHooks");
245     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
246     try {
247       Put p = new Put(Bytes.toBytes(0));
248       p.add(A, A, A);
249       table.put(p);
250       table.flushCommits();
251       p = new Put(Bytes.toBytes(0));
252       p.add(A, A, A);
253       verifyMethodResult(SimpleRegionObserver.class,
254           new String[] {"hadPreCheckAndPut", 
255               "hadPreCheckAndPutAfterRowLock", "hadPostCheckAndPut"},
256           tableName,
257           new Boolean[] {false, false, false}
258           );
259       table.checkAndPut(Bytes.toBytes(0), A, A, A, p);
260       verifyMethodResult(SimpleRegionObserver.class,
261           new String[] {"hadPreCheckAndPut", 
262               "hadPreCheckAndPutAfterRowLock", "hadPostCheckAndPut"},
263           tableName,
264           new Boolean[] {true, true, true}
265           );
266     } finally {
267       util.deleteTable(tableName);
268       table.close();
269     }
270   }
271 
272   @Test
273   public void testCheckAndDeleteHooks() throws IOException {
274     TableName tableName = 
275         TableName.valueOf(TEST_TABLE.getNameAsString() + ".testCheckAndDeleteHooks");
276     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
277     try {
278       Put p = new Put(Bytes.toBytes(0));
279       p.add(A, A, A);
280       table.put(p);
281       table.flushCommits();
282       Delete d = new Delete(Bytes.toBytes(0));
283       table.delete(d);
284       verifyMethodResult(SimpleRegionObserver.class,
285           new String[] {"hadPreCheckAndDelete", 
286               "hadPreCheckAndDeleteAfterRowLock", "hadPostCheckAndDelete"},
287           tableName,
288           new Boolean[] {false, false, false}
289           );
290       table.checkAndDelete(Bytes.toBytes(0), A, A, A, d);
291       verifyMethodResult(SimpleRegionObserver.class,
292           new String[] {"hadPreCheckAndDelete", 
293               "hadPreCheckAndDeleteAfterRowLock", "hadPostCheckAndDelete"},
294           tableName,
295           new Boolean[] {true, true, true}
296           );
297     } finally {
298       util.deleteTable(tableName);
299       table.close();
300     }
301   }
302 
303   @Test
304   public void testAppendHook() throws IOException {
305     TableName tableName = TableName.valueOf(TEST_TABLE.getNameAsString() + ".testAppendHook");
306     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
307     try {
308       Append app = new Append(Bytes.toBytes(0));
309       app.add(A, A, A);
310 
311       verifyMethodResult(SimpleRegionObserver.class,
312           new String[] {"hadPreAppend", "hadPostAppend", "hadPreAppendAfterRowLock"},
313           tableName,
314           new Boolean[] {false, false, false}
315           );
316 
317       table.append(app);
318 
319       verifyMethodResult(SimpleRegionObserver.class,
320           new String[] {"hadPreAppend", "hadPostAppend", "hadPreAppendAfterRowLock"},
321           tableName,
322           new Boolean[] {true, true, true}
323           );
324     } finally {
325       util.deleteTable(tableName);
326       table.close();
327     }
328   }
329 
330   @Test
331   // HBase-3583
332   public void testHBase3583() throws IOException {
333     TableName tableName =
334         TableName.valueOf("testHBase3583");
335     util.createTable(tableName, new byte[][] {A, B, C});
336     util.waitUntilAllRegionsAssigned(tableName);
337 
338     verifyMethodResult(SimpleRegionObserver.class,
339         new String[] {"hadPreGet", "hadPostGet", "wasScannerNextCalled",
340             "wasScannerCloseCalled"},
341         tableName,
342         new Boolean[] {false, false, false, false}
343     );
344 
345     HTable table = new HTable(util.getConfiguration(), tableName);
346     Put put = new Put(ROW);
347     put.add(A, A, A);
348     table.put(put);
349 
350     Get get = new Get(ROW);
351     get.addColumn(A, A);
352     table.get(get);
353 
354     // verify that scannerNext and scannerClose upcalls won't be invoked
355     // when we perform get().
356     verifyMethodResult(SimpleRegionObserver.class,
357         new String[] {"hadPreGet", "hadPostGet", "wasScannerNextCalled",
358             "wasScannerCloseCalled"},
359         tableName,
360         new Boolean[] {true, true, false, false}
361     );
362 
363     Scan s = new Scan();
364     ResultScanner scanner = table.getScanner(s);
365     try {
366       for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
367       }
368     } finally {
369       scanner.close();
370     }
371 
372     // now scanner hooks should be invoked.
373     verifyMethodResult(SimpleRegionObserver.class,
374         new String[] {"wasScannerNextCalled", "wasScannerCloseCalled"},
375         tableName,
376         new Boolean[] {true, true}
377     );
378     util.deleteTable(tableName);
379     table.close();
380   }
381 
382   @Test
383   public void testHBASE14489() throws IOException {
384     TableName tableName =
385         TableName.valueOf("testHBASE14489");
386     HTable table = util.createTable(tableName, new byte[][] {A});
387     Put put = new Put(ROW);
388     put.add(A, A, A);
389     table.put(put);
390 
391     Scan s = new Scan();
392     s.setFilter(new FilterAllFilter());
393     ResultScanner scanner = table.getScanner(s);
394     try {
395       for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
396       }
397     } finally {
398       scanner.close();
399     }
400     verifyMethodResult(SimpleRegionObserver.class,
401       new String[] {"wasScannerFilterRowCalled"},
402       tableName,
403       new Boolean[] {true}
404   );
405   util.deleteTable(tableName);
406   table.close();
407 
408   }
409 
410   @Test
411   // HBase-3758
412   public void testHBase3758() throws IOException {
413     TableName tableName =
414         TableName.valueOf("testHBase3758");
415     util.createTable(tableName, new byte[][] {A, B, C});
416 
417     verifyMethodResult(SimpleRegionObserver.class,
418         new String[] {"hadDeleted", "wasScannerOpenCalled"},
419         tableName,
420         new Boolean[] {false, false}
421     );
422 
423     HTable table = new HTable(util.getConfiguration(), tableName);
424     Put put = new Put(ROW);
425     put.add(A, A, A);
426     table.put(put);
427 
428     Delete delete = new Delete(ROW);
429     table.delete(delete);
430 
431     verifyMethodResult(SimpleRegionObserver.class,
432         new String[] {"hadDeleted", "wasScannerOpenCalled"},
433         tableName,
434         new Boolean[] {true, false}
435     );
436 
437     Scan s = new Scan();
438     ResultScanner scanner = table.getScanner(s);
439     try {
440       for (Result rr = scanner.next(); rr != null; rr = scanner.next()) {
441       }
442     } finally {
443       scanner.close();
444     }
445 
446     // now scanner hooks should be invoked.
447     verifyMethodResult(SimpleRegionObserver.class,
448         new String[] {"wasScannerOpenCalled"},
449         tableName,
450         new Boolean[] {true}
451     );
452     util.deleteTable(tableName);
453     table.close();
454   }
455 
456   /* Overrides compaction to only output rows with keys that are even numbers */
457   public static class EvenOnlyCompactor extends BaseRegionObserver {
458     long lastCompaction;
459     long lastFlush;
460 
461     @Override
462     public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
463         Store store, final InternalScanner scanner, final ScanType scanType) {
464       return new InternalScanner() {
465         @Override
466         public boolean next(List<Cell> results) throws IOException {
467           return next(results, -1);
468         }
469 
470         @Override
471         public boolean next(List<Cell> results, int limit)
472             throws IOException{
473           List<Cell> internalResults = new ArrayList<Cell>();
474           boolean hasMore;
475           do {
476             hasMore = scanner.next(internalResults, limit);
477             if (!internalResults.isEmpty()) {
478               long row = Bytes.toLong(CellUtil.cloneValue(internalResults.get(0)));
479               if (row % 2 == 0) {
480                 // return this row
481                 break;
482               }
483               // clear and continue
484               internalResults.clear();
485             }
486           } while (hasMore);
487 
488           if (!internalResults.isEmpty()) {
489             results.addAll(internalResults);
490           }
491           return hasMore;
492         }
493 
494         @Override
495         public void close() throws IOException {
496           scanner.close();
497         }
498       };
499     }
500 
501     @Override
502     public void postCompact(ObserverContext<RegionCoprocessorEnvironment> e,
503         Store store, StoreFile resultFile) {
504       lastCompaction = EnvironmentEdgeManager.currentTimeMillis();
505     }
506 
507     @Override
508     public void postFlush(ObserverContext<RegionCoprocessorEnvironment> e) {
509       lastFlush = EnvironmentEdgeManager.currentTimeMillis();
510     }
511   }
512   /**
513    * Tests overriding compaction handling via coprocessor hooks
514    * @throws Exception
515    */
516   @Test
517   public void testCompactionOverride() throws Exception {
518     byte[] compactTable = Bytes.toBytes("TestCompactionOverride");
519     HBaseAdmin admin = util.getHBaseAdmin();
520     if (admin.tableExists(compactTable)) {
521       admin.disableTable(compactTable);
522       admin.deleteTable(compactTable);
523     }
524 
525     HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(compactTable));
526     htd.addFamily(new HColumnDescriptor(A));
527     htd.addCoprocessor(EvenOnlyCompactor.class.getName());
528     admin.createTable(htd);
529 
530     HTable table = new HTable(util.getConfiguration(), compactTable);
531     for (long i=1; i<=10; i++) {
532       byte[] iBytes = Bytes.toBytes(i);
533       Put put = new Put(iBytes);
534       put.setDurability(Durability.SKIP_WAL);
535       put.add(A, A, iBytes);
536       table.put(put);
537     }
538 
539     HRegion firstRegion = cluster.getRegions(compactTable).get(0);
540     Coprocessor cp = firstRegion.getCoprocessorHost().findCoprocessor(
541         EvenOnlyCompactor.class.getName());
542     assertNotNull("EvenOnlyCompactor coprocessor should be loaded", cp);
543     EvenOnlyCompactor compactor = (EvenOnlyCompactor)cp;
544 
545     // force a compaction
546     long ts = System.currentTimeMillis();
547     admin.flush(compactTable);
548     // wait for flush
549     for (int i=0; i<10; i++) {
550       if (compactor.lastFlush >= ts) {
551         break;
552       }
553       Thread.sleep(1000);
554     }
555     assertTrue("Flush didn't complete", compactor.lastFlush >= ts);
556     LOG.debug("Flush complete");
557 
558     ts = compactor.lastFlush;
559     admin.majorCompact(compactTable);
560     // wait for compaction
561     for (int i=0; i<30; i++) {
562       if (compactor.lastCompaction >= ts) {
563         break;
564       }
565       Thread.sleep(1000);
566     }
567     LOG.debug("Last compaction was at "+compactor.lastCompaction);
568     assertTrue("Compaction didn't complete", compactor.lastCompaction >= ts);
569 
570     // only even rows should remain
571     ResultScanner scanner = table.getScanner(new Scan());
572     try {
573       for (long i=2; i<=10; i+=2) {
574         Result r = scanner.next();
575         assertNotNull(r);
576         assertFalse(r.isEmpty());
577         byte[] iBytes = Bytes.toBytes(i);
578         assertArrayEquals("Row should be "+i, r.getRow(), iBytes);
579         assertArrayEquals("Value should be "+i, r.getValue(A, A), iBytes);
580       }
581     } finally {
582       scanner.close();
583     }
584     table.close();
585   }
586 
587   @Test
588   public void bulkLoadHFileTest() throws Exception {
589     String testName = TestRegionObserverInterface.class.getName()+".bulkLoadHFileTest";
590     TableName tableName = TableName.valueOf(TEST_TABLE.getNameAsString() + ".bulkLoadHFileTest");
591     Configuration conf = util.getConfiguration();
592     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
593     try {
594       verifyMethodResult(SimpleRegionObserver.class,
595           new String[] {"hadPreBulkLoadHFile", "hadPostBulkLoadHFile"},
596           tableName,
597           new Boolean[] {false, false}
598           );
599 
600       FileSystem fs = util.getTestFileSystem();
601       final Path dir = util.getDataTestDirOnTestFS(testName).makeQualified(fs);
602       Path familyDir = new Path(dir, Bytes.toString(A));
603 
604       createHFile(util.getConfiguration(), fs, new Path(familyDir,Bytes.toString(A)), A, A);
605 
606       //Bulk load
607       new LoadIncrementalHFiles(conf).doBulkLoad(dir, new HTable(conf, tableName));
608 
609       verifyMethodResult(SimpleRegionObserver.class,
610           new String[] {"hadPreBulkLoadHFile", "hadPostBulkLoadHFile"},
611           tableName,
612           new Boolean[] {true, true}
613           );
614     } finally {
615       util.deleteTable(tableName);
616       table.close();
617     }
618   }
619 
620   @Test
621   public void testRecovery() throws Exception {
622     LOG.info(TestRegionObserverInterface.class.getName() +".testRecovery");
623     TableName tableName = TableName.valueOf(TEST_TABLE.getNameAsString() + ".testRecovery");
624     HTable table = util.createTable(tableName, new byte[][] {A, B, C});
625     try {
626       JVMClusterUtil.RegionServerThread rs1 = cluster.startRegionServer();
627       ServerName sn2 = rs1.getRegionServer().getServerName();
628       String regEN = table.getRegionLocations().firstEntry().getKey().getEncodedName();
629 
630       util.getHBaseAdmin().move(regEN.getBytes(), sn2.getServerName().getBytes());
631       while (!sn2.equals(table.getRegionLocations().firstEntry().getValue() )){
632         Thread.sleep(100);
633       }
634 
635       Put put = new Put(ROW);
636       put.add(A, A, A);
637       put.add(B, B, B);
638       put.add(C, C, C);
639       table.put(put);
640 
641       verifyMethodResult(SimpleRegionObserver.class,
642           new String[] {"hadPreGet", "hadPostGet", "hadPrePut", "hadPostPut",
643         "hadPreBatchMutate", "hadPostBatchMutate", "hadDelete"},
644         tableName,
645         new Boolean[] {false, false, true, true, true, true, false}
646           );
647 
648       verifyMethodResult(SimpleRegionObserver.class,
649           new String[] {"getCtPreWALRestore", "getCtPostWALRestore", "getCtPrePut", "getCtPostPut"},
650           tableName,
651           new Integer[] {0, 0, 1, 1});
652 
653       cluster.killRegionServer(rs1.getRegionServer().getServerName());
654       Threads.sleep(1000); // Let the kill soak in.
655       util.waitUntilAllRegionsAssigned(tableName);
656       LOG.info("All regions assigned");
657 
658       verifyMethodResult(SimpleRegionObserver.class,
659           new String[]{"getCtPrePut", "getCtPostPut"},
660           tableName,
661           new Integer[]{0, 0});
662     } finally {
663       util.deleteTable(tableName);
664       table.close();
665     }
666   }
667 
668   @Test
669   public void testPreWALRestoreSkip() throws Exception {
670     LOG.info(TestRegionObserverInterface.class.getName() + ".testPreWALRestoreSkip");
671     TableName tableName = TableName.valueOf(SimpleRegionObserver.TABLE_SKIPPED);
672     HTable table = util.createTable(tableName, new byte[][] { A, B, C });
673 
674     JVMClusterUtil.RegionServerThread rs1 = cluster.startRegionServer();
675     ServerName sn2 = rs1.getRegionServer().getServerName();
676     String regEN = table.getRegionLocations().firstEntry().getKey().getEncodedName();
677 
678     util.getHBaseAdmin().move(regEN.getBytes(), sn2.getServerName().getBytes());
679     while (!sn2.equals(table.getRegionLocations().firstEntry().getValue())) {
680       Thread.sleep(100);
681     }
682 
683     Put put = new Put(ROW);
684     put.add(A, A, A);
685     put.add(B, B, B);
686     put.add(C, C, C);
687     table.put(put);
688     table.flushCommits();
689 
690     cluster.killRegionServer(rs1.getRegionServer().getServerName());
691     Threads.sleep(20000); // just to be sure that the kill has fully started.
692     util.waitUntilAllRegionsAssigned(tableName);
693 
694     verifyMethodResult(SimpleRegionObserver.class, new String[] { "getCtPreWALRestore",
695         "getCtPostWALRestore" }, tableName, new Integer[] { 0, 0 });
696 
697     util.deleteTable(tableName);
698     table.close();
699   }
700 
701   // check each region whether the coprocessor upcalls are called or not.
702   private void verifyMethodResult(Class<?> c, String methodName[], TableName tableName,
703                                   Object value[]) throws IOException {
704     try {
705       for (JVMClusterUtil.RegionServerThread t : cluster.getRegionServerThreads()) {
706         if (!t.isAlive() || t.getRegionServer().isAborted() || t.getRegionServer().isStopping()){
707           continue;
708         }
709         for (HRegionInfo r : ProtobufUtil.getOnlineRegions(t.getRegionServer())) {
710           if (!r.getTable().equals(tableName)) {
711             continue;
712           }
713           RegionCoprocessorHost cph = t.getRegionServer().getOnlineRegion(r.getRegionName()).
714               getCoprocessorHost();
715 
716           Coprocessor cp = cph.findCoprocessor(c.getName());
717           assertNotNull(cp);
718           for (int i = 0; i < methodName.length; ++i) {
719             Method m = c.getMethod(methodName[i]);
720             Object o = m.invoke(cp);
721             assertTrue("Result of " + c.getName() + "." + methodName[i]
722                 + " is expected to be " + value[i].toString()
723                 + ", while we get " + o.toString(), o.equals(value[i]));
724           }
725         }
726       }
727     } catch (Exception e) {
728       throw new IOException(e.toString());
729     }
730   }
731 
732   private static void createHFile(
733       Configuration conf,
734       FileSystem fs, Path path,
735       byte[] family, byte[] qualifier) throws IOException {
736     HFileContext context = new HFileContextBuilder().build();
737     HFile.Writer writer = HFile.getWriterFactory(conf, new CacheConfig(conf))
738         .withPath(fs, path)
739         .withFileContext(context)
740         .create();
741     long now = System.currentTimeMillis();
742     try {
743       for (int i =1;i<=9;i++) {
744         KeyValue kv = new KeyValue(Bytes.toBytes(i+""), family, qualifier, now, Bytes.toBytes(i+""));
745         writer.append(kv);
746       }
747     } finally {
748       writer.close();
749     }
750   }
751 
752 }