1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.coprocessor;
22  
23  import static org.junit.Assert.assertArrayEquals;
24  import static org.junit.Assert.assertEquals;
25  import static org.junit.Assert.assertNotNull;
26  import static org.junit.Assert.assertTrue;
27  
28  import java.io.IOException;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Arrays;
32  import java.util.NavigableSet;
33  
34  import com.google.common.collect.ImmutableList;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.hadoop.hbase.CoprocessorEnvironment;
38  import org.apache.hadoop.hbase.KeyValue;
39  import org.apache.hadoop.hbase.client.Get;
40  import org.apache.hadoop.hbase.client.Mutation;
41  import org.apache.hadoop.hbase.client.Put;
42  import org.apache.hadoop.hbase.client.Delete;
43  import org.apache.hadoop.hbase.client.Increment;
44  import org.apache.hadoop.hbase.client.Result;
45  import org.apache.hadoop.hbase.client.Scan;
46  import org.apache.hadoop.hbase.regionserver.HRegion;
47  import org.apache.hadoop.hbase.regionserver.InternalScanner;
48  import org.apache.hadoop.hbase.regionserver.KeyValueScanner;
49  import org.apache.hadoop.hbase.regionserver.Leases;
50  import org.apache.hadoop.hbase.regionserver.MiniBatchOperationInProgress;
51  import org.apache.hadoop.hbase.regionserver.RegionScanner;
52  import org.apache.hadoop.hbase.regionserver.ScanType;
53  import org.apache.hadoop.hbase.regionserver.Store;
54  import org.apache.hadoop.hbase.regionserver.StoreFile;
55  import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
56  import org.apache.hadoop.hbase.util.Bytes;
57  import org.apache.hadoop.hbase.util.Pair;
58  
59  /**
60   * A sample region observer that tests the RegionObserver interface.
61   * It works with TestRegionObserverInterface to provide the test case.
62   */
63  public class SimpleRegionObserver extends BaseRegionObserver {
64    static final Log LOG = LogFactory.getLog(TestRegionObserverInterface.class);
65  
66    boolean beforeDelete = true;
67    boolean scannerOpened = false;
68    boolean hadPreOpen;
69    boolean hadPostOpen;
70    boolean hadPreClose;
71    boolean hadPostClose;
72    boolean hadPreFlush;
73    boolean hadPreFlushScannerOpen;
74    boolean hadPostFlush;
75    boolean hadPreSplit;
76    boolean hadPostSplit;
77    boolean hadPreCompactSelect;
78    boolean hadPostCompactSelect;
79    boolean hadPreCompactScanner;
80    boolean hadPreCompact;
81    boolean hadPostCompact;
82    boolean hadPreGet = false;
83    boolean hadPostGet = false;
84    boolean hadPrePut = false;
85    boolean hadPostPut = false;
86    boolean hadPreDeleted = false;
87    boolean hadPostDeleted = false;
88    boolean hadPreGetClosestRowBefore = false;
89    boolean hadPostGetClosestRowBefore = false;
90    boolean hadPreIncrement = false;
91    boolean hadPostIncrement = false;
92    boolean hadPreWALRestored = false;
93    boolean hadPostWALRestored = false;
94    boolean hadPreScannerNext = false;
95    boolean hadPostScannerNext = false;
96    boolean hadPreScannerClose = false;
97    boolean hadPostScannerClose = false;
98    boolean hadPreScannerOpen = false;
99    boolean hadPreStoreScannerOpen = false;
100   boolean hadPostScannerOpen = false;
101   boolean hadPreBulkLoadHFile = false;
102   boolean hadPostBulkLoadHFile = false;
103   boolean hadPreBatchMutate = false;
104   boolean hadPostBatchMutate = false;
105   
106   @Override
107   public void start(CoprocessorEnvironment e) throws IOException {
108     // this only makes sure that leases and locks are available to coprocessors
109     // from external packages
110     RegionCoprocessorEnvironment re = (RegionCoprocessorEnvironment)e;
111     Leases leases = re.getRegionServerServices().getLeases();
112     leases.createLease("x", null);
113     leases.cancelLease("x");
114   }
115 
116   @Override
117   public void preOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
118     hadPreOpen = true;
119   }
120 
121   @Override
122   public void postOpen(ObserverContext<RegionCoprocessorEnvironment> c) {
123     hadPostOpen = true;
124   }
125 
126   public boolean wasOpened() {
127     return hadPreOpen && hadPostOpen;
128   }
129 
130   @Override
131   public void preClose(ObserverContext<RegionCoprocessorEnvironment> c, boolean abortRequested) {
132     hadPreClose = true;
133   }
134 
135   @Override
136   public void postClose(ObserverContext<RegionCoprocessorEnvironment> c, boolean abortRequested) {
137     hadPostClose = true;
138   }
139 
140   public boolean wasClosed() {
141     return hadPreClose && hadPostClose;
142   }
143 
144   @Override
145   public InternalScanner preFlush(ObserverContext<RegionCoprocessorEnvironment> c, Store store, InternalScanner scanner) {
146     hadPreFlush = true;
147     return scanner;
148   }
149 
150   @Override
151   public InternalScanner preFlushScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
152       Store store, KeyValueScanner memstoreScanner, InternalScanner s) throws IOException {
153     hadPreFlushScannerOpen = true;
154     return null;
155   }
156 
157   @Override
158   public void postFlush(ObserverContext<RegionCoprocessorEnvironment> c, Store store, StoreFile resultFile) {
159     hadPostFlush = true;
160   }
161 
162   public boolean wasFlushed() {
163     return hadPreFlush && hadPostFlush;
164   }
165 
166   @Override
167   public void preSplit(ObserverContext<RegionCoprocessorEnvironment> c) {
168     hadPreSplit = true;
169   }
170 
171   @Override
172   public void postSplit(ObserverContext<RegionCoprocessorEnvironment> c, HRegion l, HRegion r) {
173     hadPostSplit = true;
174   }
175 
176   public boolean wasSplit() {
177     return hadPreSplit && hadPostSplit;
178   }
179 
180   @Override
181   public void preCompactSelection(ObserverContext<RegionCoprocessorEnvironment> c,
182       Store store, List<StoreFile> candidates) {
183     hadPreCompactSelect = true;
184   }
185 
186   @Override
187   public void postCompactSelection(ObserverContext<RegionCoprocessorEnvironment> c,
188       Store store, ImmutableList<StoreFile> selected) {
189     hadPostCompactSelect = true;
190   }
191 
192   @Override
193   public InternalScanner preCompact(ObserverContext<RegionCoprocessorEnvironment> e,
194       Store store, InternalScanner scanner) {
195     hadPreCompact = true;
196     return scanner;
197   }
198 
199   @Override
200   public InternalScanner preCompactScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
201       Store store, List<? extends KeyValueScanner> scanners, ScanType scanType, long earliestPutTs,
202       InternalScanner s) throws IOException {
203     hadPreCompactScanner = true;
204     return null;
205   }
206 
207   @Override
208   public void postCompact(ObserverContext<RegionCoprocessorEnvironment> e,
209       Store store, StoreFile resultFile) {
210     hadPostCompact = true;
211   }
212 
213   public boolean wasCompacted() {
214     return hadPreCompact && hadPostCompact;
215   }
216 
217   @Override
218   public RegionScanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
219       final Scan scan,
220       final RegionScanner s) throws IOException {
221     hadPreScannerOpen = true;
222     return null;
223   }
224 
225   @Override
226   public KeyValueScanner preStoreScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
227       final Store store, final Scan scan, final NavigableSet<byte[]> targetCols,
228       final KeyValueScanner s) throws IOException {
229     hadPreStoreScannerOpen = true;
230     return null;
231   }
232 
233   @Override
234   public RegionScanner postScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> c,
235       final Scan scan, final RegionScanner s)
236       throws IOException {
237     hadPostScannerOpen = true;
238     return s;
239   }
240 
241   @Override
242   public boolean preScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
243       final InternalScanner s, final List<Result> results,
244       final int limit, final boolean hasMore) throws IOException {
245     hadPreScannerNext = true;
246     return hasMore;
247   }
248 
249   @Override
250   public boolean postScannerNext(final ObserverContext<RegionCoprocessorEnvironment> c,
251       final InternalScanner s, final List<Result> results, final int limit,
252       final boolean hasMore) throws IOException {
253     hadPostScannerNext = true;
254     return hasMore;
255   }
256 
257   @Override
258   public void preScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
259       final InternalScanner s) throws IOException {
260     hadPreScannerClose = true;
261   }
262 
263   @Override
264   public void postScannerClose(final ObserverContext<RegionCoprocessorEnvironment> c,
265       final InternalScanner s) throws IOException {
266     hadPostScannerClose = true;
267   }
268 
269   @Override
270   public void preGet(final ObserverContext<RegionCoprocessorEnvironment> c, final Get get,
271       final List<KeyValue> results) throws IOException {
272     RegionCoprocessorEnvironment e = c.getEnvironment();
273     assertNotNull(e);
274     assertNotNull(e.getRegion());
275     assertNotNull(get);
276     assertNotNull(results);
277     hadPreGet = true;
278   }
279 
280   @Override
281   public void postGet(final ObserverContext<RegionCoprocessorEnvironment> c, final Get get,
282       final List<KeyValue> results) {
283     RegionCoprocessorEnvironment e = c.getEnvironment();
284     assertNotNull(e);
285     assertNotNull(e.getRegion());
286     assertNotNull(get);
287     assertNotNull(results);
288     if (Arrays.equals(e.getRegion().getTableDesc().getName(),
289         TestRegionObserverInterface.TEST_TABLE)) {
290       boolean foundA = false;
291       boolean foundB = false;
292       boolean foundC = false;
293       for (KeyValue kv: results) {
294         if (Bytes.equals(kv.getFamily(), TestRegionObserverInterface.A)) {
295           foundA = true;
296         }
297         if (Bytes.equals(kv.getFamily(), TestRegionObserverInterface.B)) {
298           foundB = true;
299         }
300         if (Bytes.equals(kv.getFamily(), TestRegionObserverInterface.C)) {
301           foundC = true;
302         }
303       }
304       assertTrue(foundA);
305       assertTrue(foundB);
306       assertTrue(foundC);
307     }
308     hadPostGet = true;
309   }
310 
311   @Override
312   public void prePut(final ObserverContext<RegionCoprocessorEnvironment> c, 
313       final Put put, final WALEdit edit,
314       final boolean writeToWAL) throws IOException {
315     Map<byte[], List<KeyValue>> familyMap  = put.getFamilyMap();
316     RegionCoprocessorEnvironment e = c.getEnvironment();
317     assertNotNull(e);
318     assertNotNull(e.getRegion());
319     assertNotNull(familyMap);
320     if (Arrays.equals(e.getRegion().getTableDesc().getName(),
321         TestRegionObserverInterface.TEST_TABLE)) {
322       List<KeyValue> kvs = familyMap.get(TestRegionObserverInterface.A);
323       assertNotNull(kvs);
324       assertNotNull(kvs.get(0));
325       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
326           TestRegionObserverInterface.A));
327       kvs = familyMap.get(TestRegionObserverInterface.B);
328       assertNotNull(kvs);
329       assertNotNull(kvs.get(0));
330       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
331           TestRegionObserverInterface.B));
332       kvs = familyMap.get(TestRegionObserverInterface.C);
333       assertNotNull(kvs);
334       assertNotNull(kvs.get(0));
335       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
336           TestRegionObserverInterface.C));
337     }
338     hadPrePut = true;
339   }
340 
341   @Override
342   public void postPut(final ObserverContext<RegionCoprocessorEnvironment> c,
343       final Put put, final WALEdit edit,
344       final boolean writeToWAL) throws IOException {
345     Map<byte[], List<KeyValue>> familyMap  = put.getFamilyMap();
346     RegionCoprocessorEnvironment e = c.getEnvironment();
347     assertNotNull(e);
348     assertNotNull(e.getRegion());
349     assertNotNull(familyMap);
350     List<KeyValue> kvs = familyMap.get(TestRegionObserverInterface.A);
351     if (Arrays.equals(e.getRegion().getTableDesc().getName(),
352         TestRegionObserverInterface.TEST_TABLE)) {
353       assertNotNull(kvs);
354       assertNotNull(kvs.get(0));
355       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
356           TestRegionObserverInterface.A));
357       kvs = familyMap.get(TestRegionObserverInterface.B);
358       assertNotNull(kvs);
359       assertNotNull(kvs.get(0));
360       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
361           TestRegionObserverInterface.B));
362       kvs = familyMap.get(TestRegionObserverInterface.C);
363       assertNotNull(kvs);
364       assertNotNull(kvs.get(0));
365       assertTrue(Bytes.equals(kvs.get(0).getQualifier(),
366           TestRegionObserverInterface.C));
367     }
368     hadPostPut = true;
369   }
370 
371   @Override
372   public void preDelete(final ObserverContext<RegionCoprocessorEnvironment> c, 
373       final Delete delete, final WALEdit edit,
374       final boolean writeToWAL) throws IOException {
375     Map<byte[], List<KeyValue>> familyMap  = delete.getFamilyMap();
376     RegionCoprocessorEnvironment e = c.getEnvironment();
377     assertNotNull(e);
378     assertNotNull(e.getRegion());
379     assertNotNull(familyMap);
380     if (beforeDelete) {
381       hadPreDeleted = true;
382     }
383   }
384 
385   @Override
386   public void postDelete(final ObserverContext<RegionCoprocessorEnvironment> c, 
387       final Delete delete, final WALEdit edit,
388       final boolean writeToWAL) throws IOException {
389     Map<byte[], List<KeyValue>> familyMap  = delete.getFamilyMap();
390     RegionCoprocessorEnvironment e = c.getEnvironment();
391     assertNotNull(e);
392     assertNotNull(e.getRegion());
393     assertNotNull(familyMap);
394     beforeDelete = false;
395     hadPostDeleted = true;
396   }
397 
398   @Override
399   public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
400       MiniBatchOperationInProgress<Pair<Mutation, Integer>> miniBatchOp) throws IOException {
401     RegionCoprocessorEnvironment e = c.getEnvironment();
402     assertNotNull(e);
403     assertNotNull(e.getRegion());
404     assertNotNull(miniBatchOp);
405     hadPreBatchMutate = true;
406   }
407 
408   @Override
409   public void postBatchMutate(final ObserverContext<RegionCoprocessorEnvironment> c,
410       final MiniBatchOperationInProgress<Pair<Mutation, Integer>> miniBatchOp) throws IOException {
411     RegionCoprocessorEnvironment e = c.getEnvironment();
412     assertNotNull(e);
413     assertNotNull(e.getRegion());
414     assertNotNull(miniBatchOp);
415     hadPostBatchMutate = true;
416   }
417   
418   @Override
419   public void preGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
420       final byte[] row, final byte[] family, final Result result)
421       throws IOException {
422     RegionCoprocessorEnvironment e = c.getEnvironment();
423     assertNotNull(e);
424     assertNotNull(e.getRegion());
425     assertNotNull(row);
426     assertNotNull(result);
427     if (beforeDelete) {
428       hadPreGetClosestRowBefore = true;
429     }
430   }
431 
432   @Override
433   public void postGetClosestRowBefore(final ObserverContext<RegionCoprocessorEnvironment> c,
434       final byte[] row, final byte[] family, final Result result)
435       throws IOException {
436     RegionCoprocessorEnvironment e = c.getEnvironment();
437     assertNotNull(e);
438     assertNotNull(e.getRegion());
439     assertNotNull(row);
440     assertNotNull(result);
441     hadPostGetClosestRowBefore = true;
442   }
443 
444   @Override
445   public Result preIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
446       final Increment increment) throws IOException {
447     hadPreIncrement = true;
448     return null;
449   }
450 
451   @Override
452   public Result postIncrement(final ObserverContext<RegionCoprocessorEnvironment> c,
453       final Increment increment, final Result result) throws IOException {
454     hadPostIncrement = true;
455     return result;
456   }
457 
458   @Override
459   public void preBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
460                                List<Pair<byte[], String>> familyPaths) throws IOException {
461     RegionCoprocessorEnvironment e = ctx.getEnvironment();
462     assertNotNull(e);
463     assertNotNull(e.getRegion());
464     if (Arrays.equals(e.getRegion().getTableDesc().getName(),
465         TestRegionObserverInterface.TEST_TABLE)) {
466       assertNotNull(familyPaths);
467       assertEquals(1,familyPaths.size());
468       assertArrayEquals(familyPaths.get(0).getFirst(), TestRegionObserverInterface.A);
469       String familyPath = familyPaths.get(0).getSecond();
470       String familyName = Bytes.toString(TestRegionObserverInterface.A);
471       assertEquals(familyPath.substring(familyPath.length()-familyName.length()-1),"/"+familyName);
472     }
473     hadPreBulkLoadHFile = true;
474   }
475 
476   @Override
477   public boolean postBulkLoadHFile(ObserverContext<RegionCoprocessorEnvironment> ctx,
478                                    List<Pair<byte[], String>> familyPaths, boolean hasLoaded) throws IOException {
479     RegionCoprocessorEnvironment e = ctx.getEnvironment();
480     assertNotNull(e);
481     assertNotNull(e.getRegion());
482     if (Arrays.equals(e.getRegion().getTableDesc().getName(),
483         TestRegionObserverInterface.TEST_TABLE)) {
484       assertNotNull(familyPaths);
485       assertEquals(1,familyPaths.size());
486       assertArrayEquals(familyPaths.get(0).getFirst(), TestRegionObserverInterface.A);
487       String familyPath = familyPaths.get(0).getSecond();
488       String familyName = Bytes.toString(TestRegionObserverInterface.A);
489       assertEquals(familyPath.substring(familyPath.length()-familyName.length()-1),"/"+familyName);
490     }
491     hadPostBulkLoadHFile = true;
492     return hasLoaded;
493   }
494 
495   public boolean hadPreGet() {
496     return hadPreGet;
497   }
498 
499   public boolean hadPostGet() {
500     return hadPostGet;
501   }
502 
503   public boolean hadPrePut() {
504     return hadPrePut;
505   }
506 
507   public boolean hadPostPut() {
508     return hadPostPut;
509   }
510   
511   public boolean hadPreBatchMutate() {
512     return hadPreBatchMutate;
513   }
514 
515   public boolean hadPostBatchMutate() {
516     return hadPostBatchMutate;
517   }
518   
519   public boolean hadDelete() {
520     return !beforeDelete;
521   }
522 
523   public boolean hadPreIncrement() {
524     return hadPreIncrement;
525   }
526 
527   public boolean hadPostIncrement() {
528     return hadPostIncrement;
529   }
530 
531   public boolean hadPreWALRestored() {
532     return hadPreWALRestored;
533   }
534 
535   public boolean hadPostWALRestored() {
536     return hadPostWALRestored;
537   }
538   public boolean wasScannerNextCalled() {
539     return hadPreScannerNext && hadPostScannerNext;
540   }
541   public boolean wasScannerCloseCalled() {
542     return hadPreScannerClose && hadPostScannerClose;
543   }
544   public boolean wasScannerOpenCalled() {
545     return hadPreScannerOpen && hadPostScannerOpen;
546   }
547   public boolean hadDeleted() {
548     return hadPreDeleted && hadPostDeleted;
549   }
550 
551   public boolean hadPostBulkLoadHFile() {
552     return hadPostBulkLoadHFile;
553   }
554 
555   public boolean hadPreBulkLoadHFile() {
556     return hadPreBulkLoadHFile;
557   }
558 }