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