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.master;
21  
22  import java.io.IOException;
23  import java.util.List;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.conf.Configuration;
28  import org.apache.hadoop.hbase.Coprocessor;
29  import org.apache.hadoop.hbase.HColumnDescriptor;
30  import org.apache.hadoop.hbase.HRegionInfo;
31  import org.apache.hadoop.hbase.HTableDescriptor;
32  import org.apache.hadoop.hbase.NamespaceDescriptor;
33  import org.apache.hadoop.hbase.ServerName;
34  import org.apache.hadoop.hbase.TableName;
35  import org.apache.hadoop.hbase.classification.InterfaceAudience;
36  import org.apache.hadoop.hbase.coprocessor.*;
37  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
38  
39  import java.io.IOException;
40  import java.util.List;
41  
42  /**
43   * Provides the coprocessor framework and environment for master oriented
44   * operations.  {@link HMaster} interacts with the loaded coprocessors
45   * through this class.
46   */
47  @InterfaceAudience.Private
48  public class MasterCoprocessorHost
49      extends CoprocessorHost<MasterCoprocessorHost.MasterEnvironment> {
50  
51    private static final Log LOG = LogFactory.getLog(MasterCoprocessorHost.class);
52  
53    /**
54     * Coprocessor environment extension providing access to master related
55     * services.
56     */
57    static class MasterEnvironment extends CoprocessorHost.Environment
58        implements MasterCoprocessorEnvironment {
59      private MasterServices masterServices;
60  
61      public MasterEnvironment(final Class<?> implClass, final Coprocessor impl,
62          final int priority, final int seq, final Configuration conf,
63          final MasterServices services) {
64        super(impl, priority, seq, conf);
65        this.masterServices = services;
66      }
67  
68      public MasterServices getMasterServices() {
69        return masterServices;
70      }
71    }
72  
73    private MasterServices masterServices;
74  
75    public MasterCoprocessorHost(final MasterServices services, final Configuration conf) {
76      super(services);
77      this.conf = conf;
78      this.masterServices = services;
79      // Log the state of coprocessor loading here; should appear only once or
80      // twice in the daemon log, depending on HBase version, because there is
81      // only one MasterCoprocessorHost instance in the master process
82      boolean coprocessorsEnabled = conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY,
83        DEFAULT_COPROCESSORS_ENABLED);
84      LOG.info("System coprocessor loading is " + (coprocessorsEnabled ? "enabled" : "disabled"));
85      loadSystemCoprocessors(conf, MASTER_COPROCESSOR_CONF_KEY);
86    }
87  
88    @Override
89    public MasterEnvironment createEnvironment(final Class<?> implClass,
90        final Coprocessor instance, final int priority, final int seq,
91        final Configuration conf) {
92      for (Class<?> c : implClass.getInterfaces()) {
93        if (CoprocessorService.class.isAssignableFrom(c)) {
94          masterServices.registerService(((CoprocessorService)instance).getService());
95        }
96      }
97      return new MasterEnvironment(implClass, instance, priority, seq, conf,
98          masterServices);
99    }
100 
101   public boolean preCreateNamespace(final NamespaceDescriptor ns) throws IOException {
102     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
103       @Override
104       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
105           throws IOException {
106         oserver.preCreateNamespace(ctx, ns);
107       }
108     });
109   }
110 
111   public void postCreateNamespace(final NamespaceDescriptor ns) throws IOException {
112     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
113       @Override
114       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
115           throws IOException {
116         oserver.postCreateNamespace(ctx, ns);
117       }
118     });
119   }
120 
121   public boolean preDeleteNamespace(final String namespaceName) throws IOException {
122     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
123       @Override
124       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
125           throws IOException {
126         oserver.preDeleteNamespace(ctx, namespaceName);
127       }
128     });
129   }
130 
131   public void postDeleteNamespace(final String namespaceName) throws IOException {
132     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
133       @Override
134       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
135           throws IOException {
136         oserver.postDeleteNamespace(ctx, namespaceName);
137       }
138     });
139   }
140 
141   public boolean preModifyNamespace(final NamespaceDescriptor ns) throws IOException {
142     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
143       @Override
144       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
145           throws IOException {
146         oserver.preModifyNamespace(ctx, ns);
147       }
148     });
149   }
150 
151   public void postModifyNamespace(final NamespaceDescriptor ns) throws IOException {
152     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
153       @Override
154       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
155           throws IOException {
156         oserver.postModifyNamespace(ctx, ns);
157       }
158     });
159   }
160 
161   /* Implementation of hooks for invoking MasterObservers */
162 
163   public void preCreateTable(final HTableDescriptor htd, final HRegionInfo[] regions)
164       throws IOException {
165     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
166       @Override
167       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
168           throws IOException {
169         oserver.preCreateTable(ctx, htd, regions);
170       }
171     });
172   }
173 
174   public void postCreateTable(final HTableDescriptor htd, final HRegionInfo[] regions)
175       throws IOException {
176     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
177       @Override
178       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
179           throws IOException {
180         oserver.postCreateTable(ctx, htd, regions);
181       }
182     });
183   }
184 
185   public void preCreateTableHandler(final HTableDescriptor htd, final HRegionInfo[] regions)
186       throws IOException {
187     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
188       @Override
189       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
190           throws IOException {
191         oserver.preCreateTableHandler(ctx, htd, regions);
192       }
193     });
194   }
195 
196   public void postCreateTableHandler(final HTableDescriptor htd, final HRegionInfo[] regions)
197       throws IOException {
198     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
199       @Override
200       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
201           throws IOException {
202         oserver.postCreateTableHandler(ctx, htd, regions);
203       }
204     });
205   }
206 
207   public void preDeleteTable(final TableName tableName) throws IOException {
208     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
209       @Override
210       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
211           throws IOException {
212         oserver.preDeleteTable(ctx, tableName);
213       }
214     });
215   }
216 
217   public void postDeleteTable(final TableName tableName) throws IOException {
218     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
219       @Override
220       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
221           throws IOException {
222         oserver.postDeleteTable(ctx, tableName);
223       }
224     });
225   }
226 
227   public void preDeleteTableHandler(final TableName tableName) throws IOException {
228     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
229       @Override
230       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
231           throws IOException {
232         oserver.preDeleteTableHandler(ctx, tableName);
233       }
234     });
235   }
236 
237   public void postDeleteTableHandler(final TableName tableName) throws IOException {
238     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
239       @Override
240       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
241           throws IOException {
242         oserver.postDeleteTableHandler(ctx, tableName);
243       }
244     });
245   }
246 
247   public void preTruncateTable(TableName tableName) throws IOException {
248     ObserverContext<MasterCoprocessorEnvironment> ctx = null;
249     for (MasterEnvironment env: coprocessors) {
250       if (env.getInstance() instanceof MasterObserver) {
251         ctx = ObserverContext.createAndPrepare(env, ctx);
252         try {
253           ((MasterObserver)env.getInstance()).preTruncateTable(ctx, tableName);
254         } catch (Throwable e) {
255           handleCoprocessorThrowable(env, e);
256         }
257         if (ctx.shouldComplete()) {
258           break;
259         }
260       }
261     }
262   }
263 
264   public void postTruncateTable(TableName tableName) throws IOException {
265     ObserverContext<MasterCoprocessorEnvironment> ctx = null;
266     for (MasterEnvironment env: coprocessors) {
267       if (env.getInstance() instanceof MasterObserver) {
268         ctx = ObserverContext.createAndPrepare(env, ctx);
269         try {
270           ((MasterObserver)env.getInstance()).postTruncateTable(ctx, tableName);
271         } catch (Throwable e) {
272           handleCoprocessorThrowable(env, e);
273         }
274         if (ctx.shouldComplete()) {
275           break;
276         }
277       }
278     }
279   }
280 
281   public void preTruncateTableHandler(TableName tableName) throws IOException {
282     ObserverContext<MasterCoprocessorEnvironment> ctx = null;
283     for (MasterEnvironment env : coprocessors) {
284       if (env.getInstance() instanceof MasterObserver) {
285         ctx = ObserverContext.createAndPrepare(env, ctx);
286         try {
287           ((MasterObserver) env.getInstance()).preTruncateTableHandler(ctx, tableName);
288         } catch (Throwable e) {
289           handleCoprocessorThrowable(env, e);
290         }
291         if (ctx.shouldComplete()) {
292           break;
293         }
294       }
295     }
296   }
297 
298   public void postTruncateTableHandler(TableName tableName) throws IOException {
299     ObserverContext<MasterCoprocessorEnvironment> ctx = null;
300     for (MasterEnvironment env : coprocessors) {
301       if (env.getInstance() instanceof MasterObserver) {
302         ctx = ObserverContext.createAndPrepare(env, ctx);
303         try {
304           ((MasterObserver) env.getInstance()).postTruncateTableHandler(ctx, tableName);
305         } catch (Throwable e) {
306           handleCoprocessorThrowable(env, e);
307         }
308         if (ctx.shouldComplete()) {
309           break;
310         }
311       }
312     }
313   }
314 
315   public void preModifyTable(final TableName tableName, final HTableDescriptor htd)
316       throws IOException {
317     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
318       @Override
319       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
320           throws IOException {
321         oserver.preModifyTable(ctx, tableName, htd);
322       }
323     });
324   }
325 
326   public void postModifyTable(final TableName tableName, final HTableDescriptor htd)
327       throws IOException {
328     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
329       @Override
330       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
331           throws IOException {
332         oserver.postModifyTable(ctx, tableName, htd);
333       }
334     });
335   }
336 
337   public void preModifyTableHandler(final TableName tableName, final HTableDescriptor htd)
338       throws IOException {
339     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
340       @Override
341       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
342           throws IOException {
343         oserver.preModifyTableHandler(ctx, tableName, htd);
344       }
345     });
346   }
347 
348   public void postModifyTableHandler(final TableName tableName, final HTableDescriptor htd)
349       throws IOException {
350     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
351       @Override
352       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
353           throws IOException {
354         oserver.postModifyTableHandler(ctx, tableName, htd);
355       }
356     });
357   }
358 
359   public boolean preAddColumn(final TableName tableName, final HColumnDescriptor column)
360       throws IOException {
361     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
362       @Override
363       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
364           throws IOException {
365         oserver.preAddColumn(ctx, tableName, column);
366       }
367     });
368   }
369 
370   public void postAddColumn(final TableName tableName, final HColumnDescriptor column)
371       throws IOException {
372     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
373       @Override
374       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
375           throws IOException {
376         oserver.postAddColumn(ctx, tableName, column);
377       }
378     });
379   }
380 
381   public boolean preAddColumnHandler(final TableName tableName, final HColumnDescriptor column)
382       throws IOException {
383     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
384       @Override
385       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
386           throws IOException {
387         oserver.preAddColumnHandler(ctx, tableName, column);
388       }
389     });
390   }
391 
392   public void postAddColumnHandler(final TableName tableName, final HColumnDescriptor column)
393       throws IOException {
394     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
395       @Override
396       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
397           throws IOException {
398         oserver.postAddColumnHandler(ctx, tableName, column);
399       }
400     });
401   }
402 
403   public boolean preModifyColumn(final TableName tableName, final HColumnDescriptor descriptor)
404       throws IOException {
405     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
406       @Override
407       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
408           throws IOException {
409         oserver.preModifyColumn(ctx, tableName, descriptor);
410       }
411     });
412   }
413 
414   public void postModifyColumn(final TableName tableName, final HColumnDescriptor descriptor)
415       throws IOException {
416     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
417       @Override
418       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
419           throws IOException {
420         oserver.postModifyColumn(ctx, tableName, descriptor);
421       }
422     });
423   }
424 
425   public boolean preModifyColumnHandler(final TableName tableName,
426       final HColumnDescriptor descriptor) throws IOException {
427     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
428       @Override
429       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
430           throws IOException {
431         oserver.preModifyColumnHandler(ctx, tableName, descriptor);
432       }
433     });
434   }
435 
436   public void postModifyColumnHandler(final TableName tableName,
437       final HColumnDescriptor descriptor) throws IOException {
438     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
439       @Override
440       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
441           throws IOException {
442         oserver.postModifyColumnHandler(ctx, tableName, descriptor);
443       }
444     });
445   }
446 
447   public boolean preDeleteColumn(final TableName tableName, final byte [] c) throws IOException {
448     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
449       @Override
450       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
451           throws IOException {
452         oserver.preDeleteColumn(ctx, tableName, c);
453       }
454     });
455   }
456 
457   public void postDeleteColumn(final TableName tableName, final byte [] c) throws IOException {
458     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
459       @Override
460       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
461           throws IOException {
462         oserver.postDeleteColumn(ctx, tableName, c);
463       }
464     });
465   }
466 
467   public boolean preDeleteColumnHandler(final TableName tableName, final byte[] c)
468       throws IOException {
469     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
470       @Override
471       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
472           throws IOException {
473         oserver.preDeleteColumnHandler(ctx, tableName, c);
474       }
475     });
476   }
477 
478   public void postDeleteColumnHandler(final TableName tableName, final byte[] c)
479       throws IOException {
480     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
481       @Override
482       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
483           throws IOException {
484         oserver.postDeleteColumnHandler(ctx, tableName, c);
485       }
486     });
487   }
488 
489   public void preEnableTable(final TableName tableName) throws IOException {
490     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
491       @Override
492       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
493           throws IOException {
494         oserver.preEnableTable(ctx, tableName);
495       }
496     });
497   }
498 
499   public void postEnableTable(final TableName tableName) throws IOException {
500     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
501       @Override
502       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
503           throws IOException {
504         oserver.postEnableTable(ctx, tableName);
505       }
506     });
507   }
508 
509   public void preEnableTableHandler(final TableName tableName) throws IOException {
510     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
511       @Override
512       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
513           throws IOException {
514         oserver.preEnableTableHandler(ctx, tableName);
515       }
516     });
517   }
518 
519   public void postEnableTableHandler(final TableName tableName) throws IOException {
520     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
521       @Override
522       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
523           throws IOException {
524         oserver.postEnableTableHandler(ctx, tableName);
525       }
526     });
527   }
528 
529   public void preDisableTable(final TableName tableName) throws IOException {
530     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
531       @Override
532       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
533           throws IOException {
534         oserver.preDisableTable(ctx, tableName);
535       }
536     });
537   }
538 
539   public void postDisableTable(final TableName tableName) throws IOException {
540     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
541       @Override
542       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
543           throws IOException {
544         oserver.postDisableTable(ctx, tableName);
545       }
546     });
547   }
548 
549   public void preDisableTableHandler(final TableName tableName) throws IOException {
550     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
551       @Override
552       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
553           throws IOException {
554         oserver.preDisableTableHandler(ctx, tableName);
555       }
556     });
557   }
558 
559   public void postDisableTableHandler(final TableName tableName) throws IOException {
560     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
561       @Override
562       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
563           throws IOException {
564         oserver.postDisableTableHandler(ctx, tableName);
565       }
566     });
567   }
568 
569   public boolean preMove(final HRegionInfo region, final ServerName srcServer,
570       final ServerName destServer) throws IOException {
571     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
572       @Override
573       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
574           throws IOException {
575         oserver.preMove(ctx, region, srcServer, destServer);
576       }
577     });
578   }
579 
580   public void postMove(final HRegionInfo region, final ServerName srcServer,
581       final ServerName destServer) throws IOException {
582     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
583       @Override
584       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
585           throws IOException {
586         oserver.postMove(ctx, region, srcServer, destServer);
587       }
588     });
589   }
590 
591   public boolean preAssign(final HRegionInfo regionInfo) throws IOException {
592     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
593       @Override
594       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
595           throws IOException {
596         oserver.preAssign(ctx, regionInfo);
597       }
598     });
599   }
600 
601   public void postAssign(final HRegionInfo regionInfo) throws IOException {
602     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
603       @Override
604       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
605           throws IOException {
606         oserver.postAssign(ctx, regionInfo);
607       }
608     });
609   }
610 
611   public boolean preUnassign(final HRegionInfo regionInfo, final boolean force)
612       throws IOException {
613     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
614       @Override
615       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
616           throws IOException {
617         oserver.preUnassign(ctx, regionInfo, force);
618       }
619     });
620   }
621 
622   public void postUnassign(final HRegionInfo regionInfo, final boolean force) throws IOException {
623     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
624       @Override
625       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
626           throws IOException {
627         oserver.postUnassign(ctx, regionInfo, force);
628       }
629     });
630   }
631 
632   public void preRegionOffline(final HRegionInfo regionInfo) throws IOException {
633     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
634       @Override
635       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
636           throws IOException {
637         oserver.preRegionOffline(ctx, regionInfo);
638       }
639     });
640   }
641 
642   public void postRegionOffline(final HRegionInfo regionInfo) throws IOException {
643     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
644       @Override
645       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
646           throws IOException {
647         oserver.postRegionOffline(ctx, regionInfo);
648       }
649     });
650   }
651 
652   public boolean preBalance() throws IOException {
653     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
654       @Override
655       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
656           throws IOException {
657         oserver.preBalance(ctx);
658       }
659     });
660   }
661 
662   public void postBalance(final List<RegionPlan> plans) throws IOException {
663     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
664       @Override
665       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
666           throws IOException {
667         oserver.postBalance(ctx, plans);
668       }
669     });
670   }
671 
672   public boolean preBalanceSwitch(final boolean b) throws IOException {
673     return execOperationWithResult(b, coprocessors.isEmpty() ? null :
674         new CoprocessorOperationWithResult<Boolean>() {
675       @Override
676       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
677           throws IOException {
678         setResult(oserver.preBalanceSwitch(ctx, getResult()));
679       }
680     });
681   }
682 
683   public void postBalanceSwitch(final boolean oldValue, final boolean newValue)
684       throws IOException {
685     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
686       @Override
687       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
688           throws IOException {
689         oserver.postBalanceSwitch(ctx, oldValue, newValue);
690       }
691     });
692   }
693 
694   public void preShutdown() throws IOException {
695     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
696       @Override
697       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
698           throws IOException {
699         oserver.preShutdown(ctx);
700       }
701       @Override
702       public void postEnvCall(MasterEnvironment env) {
703         // invoke coprocessor stop method
704         shutdown(env);
705       }
706     });
707   }
708 
709   public void preStopMaster() throws IOException {
710     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
711       @Override
712       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
713           throws IOException {
714         oserver.preStopMaster(ctx);
715       }
716       @Override
717       public void postEnvCall(MasterEnvironment env) {
718         // invoke coprocessor stop method
719         shutdown(env);
720       }
721     });
722   }
723 
724   public void preMasterInitialization() throws IOException {
725     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
726       @Override
727       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
728           throws IOException {
729         oserver.preMasterInitialization(ctx);
730       }
731     });
732   }
733 
734   public void postStartMaster() throws IOException {
735     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
736       @Override
737       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
738           throws IOException {
739         oserver.postStartMaster(ctx);
740       }
741     });
742   }
743 
744   public void preSnapshot(final SnapshotDescription snapshot,
745       final HTableDescriptor hTableDescriptor) throws IOException {
746     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
747       @Override
748       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
749           throws IOException {
750         oserver.preSnapshot(ctx, snapshot, hTableDescriptor);
751       }
752     });
753   }
754 
755   public void postSnapshot(final SnapshotDescription snapshot,
756       final HTableDescriptor hTableDescriptor) throws IOException {
757     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
758       @Override
759       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
760           throws IOException {
761         oserver.postSnapshot(ctx, snapshot, hTableDescriptor);
762       }
763     });
764   }
765 
766   public void preCloneSnapshot(final SnapshotDescription snapshot,
767       final HTableDescriptor hTableDescriptor) throws IOException {
768     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
769       @Override
770       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
771           throws IOException {
772         oserver.preCloneSnapshot(ctx, snapshot, hTableDescriptor);
773       }
774     });
775   }
776 
777   public void postCloneSnapshot(final SnapshotDescription snapshot,
778       final HTableDescriptor hTableDescriptor) throws IOException {
779     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
780       @Override
781       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
782           throws IOException {
783         oserver.postCloneSnapshot(ctx, snapshot, hTableDescriptor);
784       }
785     });
786   }
787 
788   public void preRestoreSnapshot(final SnapshotDescription snapshot,
789       final HTableDescriptor hTableDescriptor) throws IOException {
790     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
791       @Override
792       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
793           throws IOException {
794         oserver.preRestoreSnapshot(ctx, snapshot, hTableDescriptor);
795       }
796     });
797   }
798 
799   public void postRestoreSnapshot(final SnapshotDescription snapshot,
800       final HTableDescriptor hTableDescriptor) throws IOException {
801     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
802       @Override
803       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
804           throws IOException {
805         oserver.postRestoreSnapshot(ctx, snapshot, hTableDescriptor);
806       }
807     });
808   }
809 
810   public void preDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
811     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
812       @Override
813       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
814           throws IOException {
815         oserver.preDeleteSnapshot(ctx, snapshot);
816       }
817     });
818   }
819 
820   public void postDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
821     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
822       @Override
823       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
824           throws IOException {
825         oserver.postDeleteSnapshot(ctx, snapshot);
826       }
827     });
828   }
829 
830   public boolean preGetTableDescriptors(final List<TableName> tableNamesList,
831       final List<HTableDescriptor> descriptors) throws IOException {
832     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
833       @Override
834       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
835           throws IOException {
836         oserver.preGetTableDescriptors(ctx, tableNamesList, descriptors);
837       }
838     });
839   }
840 
841   public void postGetTableDescriptors(final List<HTableDescriptor> descriptors)
842       throws IOException {
843     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
844       @Override
845       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
846           throws IOException {
847         oserver.postGetTableDescriptors(ctx, descriptors);
848       }
849     });
850   }
851 
852   private static abstract class CoprocessorOperation
853       extends ObserverContext<MasterCoprocessorEnvironment> {
854     public CoprocessorOperation() {
855     }
856 
857     public abstract void call(MasterObserver oserver,
858         ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException;
859 
860     public void postEnvCall(MasterEnvironment env) {
861     }
862   }
863 
864   private static abstract class CoprocessorOperationWithResult<T> extends CoprocessorOperation {
865     private T result = null;
866     public void setResult(final T result) { this.result = result; }
867     public T getResult() { return this.result; }
868   }
869 
870   private <T> T execOperationWithResult(final T defaultValue,
871       final CoprocessorOperationWithResult<T> ctx) throws IOException {
872     if (ctx == null) return defaultValue;
873     ctx.setResult(defaultValue);
874     execOperation(ctx);
875     return ctx.getResult();
876   }
877 
878   private boolean execOperation(final CoprocessorOperation ctx) throws IOException {
879     if (ctx == null) return false;
880     boolean bypass = false;
881     for (MasterEnvironment env: coprocessors) {
882       if (env.getInstance() instanceof MasterObserver) {
883         ctx.prepare(env);
884         Thread currentThread = Thread.currentThread();
885         ClassLoader cl = currentThread.getContextClassLoader();
886         try {
887           currentThread.setContextClassLoader(env.getClassLoader());
888           ctx.call((MasterObserver)env.getInstance(), ctx);
889         } catch (Throwable e) {
890           handleCoprocessorThrowable(env, e);
891         } finally {
892           currentThread.setContextClassLoader(cl);
893         }
894         bypass |= ctx.shouldBypass();
895         if (ctx.shouldComplete()) {
896           break;
897         }
898       }
899       ctx.postEnvCall(env);
900     }
901     return bypass;
902   }
903 }