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.ProcedureInfo;
35  import org.apache.hadoop.hbase.ServerName;
36  import org.apache.hadoop.hbase.TableName;
37  import org.apache.hadoop.hbase.classification.InterfaceAudience;
38  import org.apache.hadoop.hbase.client.Admin;
39  import org.apache.hadoop.hbase.coprocessor.*;
40  import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
41  import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
42  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
43  import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
44  
45  /**
46   * Provides the coprocessor framework and environment for master oriented
47   * operations.  {@link HMaster} interacts with the loaded coprocessors
48   * through this class.
49   */
50  @InterfaceAudience.Private
51  public class MasterCoprocessorHost
52      extends CoprocessorHost<MasterCoprocessorHost.MasterEnvironment> {
53  
54    private static final Log LOG = LogFactory.getLog(MasterCoprocessorHost.class);
55  
56    /**
57     * Coprocessor environment extension providing access to master related
58     * services.
59     */
60    static class MasterEnvironment extends CoprocessorHost.Environment
61        implements MasterCoprocessorEnvironment {
62      private MasterServices masterServices;
63  
64      public MasterEnvironment(final Class<?> implClass, final Coprocessor impl,
65          final int priority, final int seq, final Configuration conf,
66          final MasterServices services) {
67        super(impl, priority, seq, conf);
68        this.masterServices = services;
69      }
70  
71      public MasterServices getMasterServices() {
72        return masterServices;
73      }
74    }
75  
76    private MasterServices masterServices;
77  
78    public MasterCoprocessorHost(final MasterServices services, final Configuration conf) {
79      super(services);
80      this.conf = conf;
81      this.masterServices = services;
82      // Log the state of coprocessor loading here; should appear only once or
83      // twice in the daemon log, depending on HBase version, because there is
84      // only one MasterCoprocessorHost instance in the master process
85      boolean coprocessorsEnabled = conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY,
86        DEFAULT_COPROCESSORS_ENABLED);
87      LOG.info("System coprocessor loading is " + (coprocessorsEnabled ? "enabled" : "disabled"));
88      loadSystemCoprocessors(conf, MASTER_COPROCESSOR_CONF_KEY);
89    }
90  
91    @Override
92    public MasterEnvironment createEnvironment(final Class<?> implClass,
93        final Coprocessor instance, final int priority, final int seq,
94        final Configuration conf) {
95      for (Object itf : ClassUtils.getAllInterfaces(implClass)) {
96        Class<?> c = (Class<?>) itf;
97        if (CoprocessorService.class.isAssignableFrom(c)) {
98          masterServices.registerService(((CoprocessorService)instance).getService());
99        }
100     }
101     return new MasterEnvironment(implClass, instance, priority, seq, conf,
102         masterServices);
103   }
104 
105   public boolean preCreateNamespace(final NamespaceDescriptor ns) throws IOException {
106     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
107       @Override
108       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
109           throws IOException {
110         oserver.preCreateNamespace(ctx, ns);
111       }
112     });
113   }
114 
115   public void postCreateNamespace(final NamespaceDescriptor ns) throws IOException {
116     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
117       @Override
118       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
119           throws IOException {
120         oserver.postCreateNamespace(ctx, ns);
121       }
122     });
123   }
124 
125   public boolean preDeleteNamespace(final String namespaceName) throws IOException {
126     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
127       @Override
128       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
129           throws IOException {
130         oserver.preDeleteNamespace(ctx, namespaceName);
131       }
132     });
133   }
134 
135   public void postDeleteNamespace(final String namespaceName) throws IOException {
136     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
137       @Override
138       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
139           throws IOException {
140         oserver.postDeleteNamespace(ctx, namespaceName);
141       }
142     });
143   }
144 
145   public boolean preModifyNamespace(final NamespaceDescriptor ns) throws IOException {
146     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
147       @Override
148       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
149           throws IOException {
150         oserver.preModifyNamespace(ctx, ns);
151       }
152     });
153   }
154 
155   public void postModifyNamespace(final NamespaceDescriptor ns) throws IOException {
156     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
157       @Override
158       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
159           throws IOException {
160         oserver.postModifyNamespace(ctx, ns);
161       }
162     });
163   }
164 
165   public void preGetNamespaceDescriptor(final String namespaceName)
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.preGetNamespaceDescriptor(ctx, namespaceName);
172       }
173     });
174   }
175 
176   public void postGetNamespaceDescriptor(final NamespaceDescriptor ns)
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.postGetNamespaceDescriptor(ctx, ns);
183       }
184     });
185   }
186 
187   public boolean preListNamespaceDescriptors(final List<NamespaceDescriptor> descriptors)
188       throws IOException {
189     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
190       @Override
191       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
192           throws IOException {
193         oserver.preListNamespaceDescriptors(ctx, descriptors);
194       }
195     });
196   }
197 
198   public void postListNamespaceDescriptors(final List<NamespaceDescriptor> descriptors)
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.postListNamespaceDescriptors(ctx, descriptors);
205       }
206     });
207   }
208 
209   /* Implementation of hooks for invoking MasterObservers */
210 
211   public void preCreateTable(final HTableDescriptor htd, final HRegionInfo[] regions)
212       throws IOException {
213     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
214       @Override
215       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
216           throws IOException {
217         oserver.preCreateTable(ctx, htd, regions);
218       }
219     });
220   }
221 
222   public void postCreateTable(final HTableDescriptor htd, final HRegionInfo[] regions)
223       throws IOException {
224     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
225       @Override
226       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
227           throws IOException {
228         oserver.postCreateTable(ctx, htd, regions);
229       }
230     });
231   }
232 
233   public void preCreateTableHandler(final HTableDescriptor htd, final HRegionInfo[] regions)
234       throws IOException {
235     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
236       @Override
237       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
238           throws IOException {
239         oserver.preCreateTableHandler(ctx, htd, regions);
240       }
241     });
242   }
243 
244   public void postCreateTableHandler(final HTableDescriptor htd, final HRegionInfo[] regions)
245       throws IOException {
246     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
247       @Override
248       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
249           throws IOException {
250         oserver.postCreateTableHandler(ctx, htd, regions);
251       }
252     });
253   }
254 
255   public void preDeleteTable(final TableName tableName) throws IOException {
256     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
257       @Override
258       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
259           throws IOException {
260         oserver.preDeleteTable(ctx, tableName);
261       }
262     });
263   }
264 
265   public void postDeleteTable(final TableName tableName) throws IOException {
266     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
267       @Override
268       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
269           throws IOException {
270         oserver.postDeleteTable(ctx, tableName);
271       }
272     });
273   }
274 
275   public void preDeleteTableHandler(final TableName tableName) throws IOException {
276     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
277       @Override
278       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
279           throws IOException {
280         oserver.preDeleteTableHandler(ctx, tableName);
281       }
282     });
283   }
284 
285   public void postDeleteTableHandler(final TableName tableName) throws IOException {
286     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
287       @Override
288       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
289           throws IOException {
290         oserver.postDeleteTableHandler(ctx, tableName);
291       }
292     });
293   }
294 
295   public void preTruncateTable(final TableName tableName) throws IOException {
296     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
297       @Override
298       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
299           throws IOException {
300         oserver.preTruncateTable(ctx, tableName);
301       }
302     });
303   }
304 
305   public void postTruncateTable(final TableName tableName) throws IOException {
306     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
307       @Override
308       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
309           throws IOException {
310         oserver.postTruncateTable(ctx, tableName);
311       }
312     });
313   }
314 
315   public void preTruncateTableHandler(final TableName tableName) throws IOException {
316     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
317       @Override
318       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
319           throws IOException {
320         oserver.preTruncateTableHandler(ctx, tableName);
321       }
322     });
323   }
324 
325   public void postTruncateTableHandler(final TableName tableName) throws IOException {
326     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
327       @Override
328       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
329           throws IOException {
330         oserver.postTruncateTableHandler(ctx, tableName);
331       }
332     });
333   }
334 
335   public void preModifyTable(final TableName tableName, final HTableDescriptor htd)
336       throws IOException {
337     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
338       @Override
339       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
340           throws IOException {
341         oserver.preModifyTable(ctx, tableName, htd);
342       }
343     });
344   }
345 
346   public void postModifyTable(final TableName tableName, final HTableDescriptor htd)
347       throws IOException {
348     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
349       @Override
350       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
351           throws IOException {
352         oserver.postModifyTable(ctx, tableName, htd);
353       }
354     });
355   }
356 
357   public void preModifyTableHandler(final TableName tableName, final HTableDescriptor htd)
358       throws IOException {
359     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
360       @Override
361       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
362           throws IOException {
363         oserver.preModifyTableHandler(ctx, tableName, htd);
364       }
365     });
366   }
367 
368   public void postModifyTableHandler(final TableName tableName, final HTableDescriptor htd)
369       throws IOException {
370     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
371       @Override
372       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
373           throws IOException {
374         oserver.postModifyTableHandler(ctx, tableName, htd);
375       }
376     });
377   }
378 
379   public boolean preAddColumn(final TableName tableName, final HColumnDescriptor column)
380       throws IOException {
381     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
382       @Override
383       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
384           throws IOException {
385         oserver.preAddColumn(ctx, tableName, column);
386       }
387     });
388   }
389 
390   public void postAddColumn(final TableName tableName, final HColumnDescriptor column)
391       throws IOException {
392     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
393       @Override
394       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
395           throws IOException {
396         oserver.postAddColumn(ctx, tableName, column);
397       }
398     });
399   }
400 
401   public boolean preAddColumnHandler(final TableName tableName, final HColumnDescriptor column)
402       throws IOException {
403     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
404       @Override
405       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
406           throws IOException {
407         oserver.preAddColumnHandler(ctx, tableName, column);
408       }
409     });
410   }
411 
412   public void postAddColumnHandler(final TableName tableName, final HColumnDescriptor column)
413       throws IOException {
414     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
415       @Override
416       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
417           throws IOException {
418         oserver.postAddColumnHandler(ctx, tableName, column);
419       }
420     });
421   }
422 
423   public boolean preModifyColumn(final TableName tableName, final HColumnDescriptor descriptor)
424       throws IOException {
425     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
426       @Override
427       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
428           throws IOException {
429         oserver.preModifyColumn(ctx, tableName, descriptor);
430       }
431     });
432   }
433 
434   public void postModifyColumn(final TableName tableName, final HColumnDescriptor descriptor)
435       throws IOException {
436     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
437       @Override
438       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
439           throws IOException {
440         oserver.postModifyColumn(ctx, tableName, descriptor);
441       }
442     });
443   }
444 
445   public boolean preModifyColumnHandler(final TableName tableName,
446       final HColumnDescriptor descriptor) throws IOException {
447     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
448       @Override
449       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
450           throws IOException {
451         oserver.preModifyColumnHandler(ctx, tableName, descriptor);
452       }
453     });
454   }
455 
456   public void postModifyColumnHandler(final TableName tableName,
457       final HColumnDescriptor descriptor) 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.postModifyColumnHandler(ctx, tableName, descriptor);
463       }
464     });
465   }
466 
467   public boolean preDeleteColumn(final TableName tableName, final byte [] c) throws IOException {
468     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
469       @Override
470       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
471           throws IOException {
472         oserver.preDeleteColumn(ctx, tableName, c);
473       }
474     });
475   }
476 
477   public void postDeleteColumn(final TableName tableName, final byte [] c) throws IOException {
478     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
479       @Override
480       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
481           throws IOException {
482         oserver.postDeleteColumn(ctx, tableName, c);
483       }
484     });
485   }
486 
487   public boolean preDeleteColumnHandler(final TableName tableName, final byte[] c)
488       throws IOException {
489     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
490       @Override
491       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
492           throws IOException {
493         oserver.preDeleteColumnHandler(ctx, tableName, c);
494       }
495     });
496   }
497 
498   public void postDeleteColumnHandler(final TableName tableName, final byte[] c)
499       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.postDeleteColumnHandler(ctx, tableName, c);
505       }
506     });
507   }
508 
509   public void preEnableTable(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.preEnableTable(ctx, tableName);
515       }
516     });
517   }
518 
519   public void postEnableTable(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.postEnableTable(ctx, tableName);
525       }
526     });
527   }
528 
529   public void preEnableTableHandler(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.preEnableTableHandler(ctx, tableName);
535       }
536     });
537   }
538 
539   public void postEnableTableHandler(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.postEnableTableHandler(ctx, tableName);
545       }
546     });
547   }
548 
549   public void preDisableTable(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.preDisableTable(ctx, tableName);
555       }
556     });
557   }
558 
559   public void postDisableTable(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.postDisableTable(ctx, tableName);
565       }
566     });
567   }
568 
569   public void preDisableTableHandler(final TableName tableName) throws IOException {
570     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
571       @Override
572       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
573           throws IOException {
574         oserver.preDisableTableHandler(ctx, tableName);
575       }
576     });
577   }
578 
579   public void postDisableTableHandler(final TableName tableName) throws IOException {
580     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
581       @Override
582       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
583           throws IOException {
584         oserver.postDisableTableHandler(ctx, tableName);
585       }
586     });
587   }
588 
589   public boolean preAbortProcedure(
590       final ProcedureExecutor<MasterProcedureEnv> procEnv,
591       final long procId) 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.preAbortProcedure(ctx, procEnv, procId);
597       }
598     });
599   }
600 
601   public void postAbortProcedure() 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.postAbortProcedure(ctx);
607       }
608     });
609   }
610 
611   public boolean preListProcedures() throws IOException {
612     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
613       @Override
614       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
615           throws IOException {
616         oserver.preListProcedures(ctx);
617       }
618     });
619   }
620 
621   public void postListProcedures(final List<ProcedureInfo> procInfoList) throws IOException {
622     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
623       @Override
624       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
625           throws IOException {
626         oserver.postListProcedures(ctx, procInfoList);
627       }
628     });
629   }
630 
631   public boolean preMove(final HRegionInfo region, final ServerName srcServer,
632       final ServerName destServer) throws IOException {
633     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
634       @Override
635       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
636           throws IOException {
637         oserver.preMove(ctx, region, srcServer, destServer);
638       }
639     });
640   }
641 
642   public void postMove(final HRegionInfo region, final ServerName srcServer,
643       final ServerName destServer) throws IOException {
644     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
645       @Override
646       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
647           throws IOException {
648         oserver.postMove(ctx, region, srcServer, destServer);
649       }
650     });
651   }
652 
653   public boolean preAssign(final HRegionInfo regionInfo) throws IOException {
654     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
655       @Override
656       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
657           throws IOException {
658         oserver.preAssign(ctx, regionInfo);
659       }
660     });
661   }
662 
663   public void postAssign(final HRegionInfo regionInfo) throws IOException {
664     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
665       @Override
666       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
667           throws IOException {
668         oserver.postAssign(ctx, regionInfo);
669       }
670     });
671   }
672 
673   public boolean preUnassign(final HRegionInfo regionInfo, final boolean force)
674       throws IOException {
675     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
676       @Override
677       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
678           throws IOException {
679         oserver.preUnassign(ctx, regionInfo, force);
680       }
681     });
682   }
683 
684   public void postUnassign(final HRegionInfo regionInfo, final boolean force) 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.postUnassign(ctx, regionInfo, force);
690       }
691     });
692   }
693 
694   public void preRegionOffline(final HRegionInfo regionInfo) 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.preRegionOffline(ctx, regionInfo);
700       }
701     });
702   }
703 
704   public void postRegionOffline(final HRegionInfo regionInfo) throws IOException {
705     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
706       @Override
707       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
708           throws IOException {
709         oserver.postRegionOffline(ctx, regionInfo);
710       }
711     });
712   }
713 
714   public void preDispatchMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB)
715       throws IOException {
716     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
717       @Override
718       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
719           throws IOException {
720         oserver.preDispatchMerge(ctx, regionInfoA, regionInfoB);
721       }
722     });
723   }
724 
725   public void postDispatchMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB)
726       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.postDispatchMerge(ctx, regionInfoA, regionInfoB);
732       }
733     });
734   }
735 
736   public boolean preBalance() throws IOException {
737     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
738       @Override
739       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
740           throws IOException {
741         oserver.preBalance(ctx);
742       }
743     });
744   }
745 
746   public void postBalance(final List<RegionPlan> plans) throws IOException {
747     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
748       @Override
749       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
750           throws IOException {
751         oserver.postBalance(ctx, plans);
752       }
753     });
754   }
755 
756   public boolean preSetSplitOrMergeEnabled(final boolean newValue,
757       final Admin.MasterSwitchType switchType) throws IOException {
758     return execOperation(coprocessors.isEmpty() ? null :
759       new CoprocessorOperation() {
760       @Override
761       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
762           throws IOException {
763         oserver.preSetSplitOrMergeEnabled(ctx, newValue, switchType);
764       }
765     });
766   }
767 
768   public void postSetSplitOrMergeEnabled(final boolean newValue,
769       final Admin.MasterSwitchType switchType) 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.postSetSplitOrMergeEnabled(ctx, newValue, switchType);
775       }
776     });
777   }
778 
779   public boolean preBalanceSwitch(final boolean b) throws IOException {
780     return execOperationWithResult(b, coprocessors.isEmpty() ? null :
781         new CoprocessorOperationWithResult<Boolean>() {
782       @Override
783       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
784           throws IOException {
785         setResult(oserver.preBalanceSwitch(ctx, getResult()));
786       }
787     });
788   }
789 
790   public void postBalanceSwitch(final boolean oldValue, final boolean newValue)
791       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.postBalanceSwitch(ctx, oldValue, newValue);
797       }
798     });
799   }
800 
801   public void preShutdown() throws IOException {
802     // While stopping the cluster all coprocessors method should be executed first then the
803     // coprocessor should be cleaned up.
804     execShutdown(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
805       @Override
806       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
807           throws IOException {
808         oserver.preShutdown(ctx);
809       }
810       @Override
811       public void postEnvCall(MasterEnvironment env) {
812         // invoke coprocessor stop method
813         shutdown(env);
814       }
815     });
816   }
817 
818   public void preStopMaster() throws IOException {
819     // While stopping master all coprocessors method should be executed first then the coprocessor
820     // environment should be cleaned up.
821     execShutdown(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
822       @Override
823       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
824           throws IOException {
825         oserver.preStopMaster(ctx);
826       }
827       @Override
828       public void postEnvCall(MasterEnvironment env) {
829         // invoke coprocessor stop method
830         shutdown(env);
831       }
832     });
833   }
834 
835   public void preMasterInitialization() throws IOException {
836     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
837       @Override
838       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
839           throws IOException {
840         oserver.preMasterInitialization(ctx);
841       }
842     });
843   }
844 
845   public void postStartMaster() throws IOException {
846     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
847       @Override
848       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
849           throws IOException {
850         oserver.postStartMaster(ctx);
851       }
852     });
853   }
854 
855   public void preSnapshot(final SnapshotDescription snapshot,
856       final HTableDescriptor hTableDescriptor) throws IOException {
857     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
858       @Override
859       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
860           throws IOException {
861         oserver.preSnapshot(ctx, snapshot, hTableDescriptor);
862       }
863     });
864   }
865 
866   public void postSnapshot(final SnapshotDescription snapshot,
867       final HTableDescriptor hTableDescriptor) throws IOException {
868     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
869       @Override
870       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
871           throws IOException {
872         oserver.postSnapshot(ctx, snapshot, hTableDescriptor);
873       }
874     });
875   }
876 
877   public void preListSnapshot(final SnapshotDescription snapshot) throws IOException {
878     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
879       @Override
880       public void call(MasterObserver observer, ObserverContext<MasterCoprocessorEnvironment> ctx)
881           throws IOException {
882         observer.preListSnapshot(ctx, snapshot);
883       }
884     });
885   }
886 
887   public void postListSnapshot(final SnapshotDescription snapshot) throws IOException {
888     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
889       @Override
890       public void call(MasterObserver observer, ObserverContext<MasterCoprocessorEnvironment> ctx)
891           throws IOException {
892         observer.postListSnapshot(ctx, snapshot);
893       }
894     });
895   }
896   
897   public void preCloneSnapshot(final SnapshotDescription snapshot,
898       final HTableDescriptor hTableDescriptor) throws IOException {
899     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
900       @Override
901       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
902           throws IOException {
903         oserver.preCloneSnapshot(ctx, snapshot, hTableDescriptor);
904       }
905     });
906   }
907 
908   public void postCloneSnapshot(final SnapshotDescription snapshot,
909       final HTableDescriptor hTableDescriptor) throws IOException {
910     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
911       @Override
912       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
913           throws IOException {
914         oserver.postCloneSnapshot(ctx, snapshot, hTableDescriptor);
915       }
916     });
917   }
918 
919   public void preRestoreSnapshot(final SnapshotDescription snapshot,
920       final HTableDescriptor hTableDescriptor) throws IOException {
921     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
922       @Override
923       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
924           throws IOException {
925         oserver.preRestoreSnapshot(ctx, snapshot, hTableDescriptor);
926       }
927     });
928   }
929 
930   public void postRestoreSnapshot(final SnapshotDescription snapshot,
931       final HTableDescriptor hTableDescriptor) throws IOException {
932     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
933       @Override
934       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
935           throws IOException {
936         oserver.postRestoreSnapshot(ctx, snapshot, hTableDescriptor);
937       }
938     });
939   }
940 
941   public void preDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
942     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
943       @Override
944       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
945           throws IOException {
946         oserver.preDeleteSnapshot(ctx, snapshot);
947       }
948     });
949   }
950 
951   public void postDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
952     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
953       @Override
954       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
955           throws IOException {
956         oserver.postDeleteSnapshot(ctx, snapshot);
957       }
958     });
959   }
960 
961   @Deprecated
962   public boolean preGetTableDescriptors(final List<TableName> tableNamesList,
963     final List<HTableDescriptor> descriptors) throws IOException {
964     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
965       @Override
966       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
967           throws IOException {
968         oserver.preGetTableDescriptors(ctx, tableNamesList, descriptors);
969       }
970     });
971   }
972 
973   @Deprecated
974   public void postGetTableDescriptors(final List<HTableDescriptor> descriptors)
975       throws IOException {
976     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
977       @Override
978       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
979           throws IOException {
980         oserver.postGetTableDescriptors(ctx, descriptors);
981       }
982     });
983   }
984 
985   public boolean preGetTableDescriptors(final List<TableName> tableNamesList,
986       final List<HTableDescriptor> descriptors, final String regex) throws IOException {
987     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
988       @Override
989       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
990           throws IOException {
991         oserver.preGetTableDescriptors(ctx, tableNamesList, descriptors, regex);
992       }
993     });
994   }
995 
996   public void postGetTableDescriptors(final List<TableName> tableNamesList,
997       final List<HTableDescriptor> descriptors, final String regex) throws IOException {
998     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
999       @Override
1000       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1001           throws IOException {
1002         oserver.postGetTableDescriptors(ctx, tableNamesList, descriptors, regex);
1003       }
1004     });
1005   }
1006 
1007   public boolean preGetTableNames(final List<HTableDescriptor> descriptors,
1008       final String regex) throws IOException {
1009     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1010       @Override
1011       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1012           throws IOException {
1013         oserver.preGetTableNames(ctx, descriptors, regex);
1014       }
1015     });
1016   }
1017 
1018   public void postGetTableNames(final List<HTableDescriptor> descriptors,
1019       final String regex) throws IOException {
1020     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1021       @Override
1022       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1023           throws IOException {
1024         oserver.postGetTableNames(ctx, descriptors, regex);
1025       }
1026     });
1027   }
1028 
1029   public void preTableFlush(final TableName tableName) throws IOException {
1030     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1031       @Override
1032       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1033           throws IOException {
1034         oserver.preTableFlush(ctx, tableName);
1035       }
1036     });
1037   }
1038 
1039   public void postTableFlush(final TableName tableName) throws IOException {
1040     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1041       @Override
1042       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1043           throws IOException {
1044         oserver.postTableFlush(ctx, tableName);
1045       }
1046     });
1047   }
1048   
1049   public void preSetUserQuota(final String user, final Quotas quotas) throws IOException {
1050     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1051       @Override
1052       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1053           throws IOException {
1054         oserver.preSetUserQuota(ctx, user, quotas);
1055       }
1056     });
1057   }
1058 
1059   public void postSetUserQuota(final String user, final Quotas quotas) throws IOException {
1060     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1061       @Override
1062       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1063           throws IOException {
1064         oserver.postSetUserQuota(ctx, user, quotas);
1065       }
1066     });
1067   }
1068 
1069   public void preSetUserQuota(final String user, final TableName table, final Quotas quotas)
1070       throws IOException {
1071     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1072       @Override
1073       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1074           throws IOException {
1075         oserver.preSetUserQuota(ctx, user, table, quotas);
1076       }
1077     });
1078   }
1079 
1080   public void postSetUserQuota(final String user, final TableName table, final Quotas quotas)
1081       throws IOException {
1082     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1083       @Override
1084       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1085           throws IOException {
1086         oserver.postSetUserQuota(ctx, user, table, quotas);
1087       }
1088     });
1089   }
1090 
1091   public void preSetUserQuota(final String user, final String namespace, final Quotas quotas)
1092       throws IOException {
1093     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1094       @Override
1095       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1096           throws IOException {
1097         oserver.preSetUserQuota(ctx, user, namespace, quotas);
1098       }
1099     });
1100   }
1101 
1102   public void postSetUserQuota(final String user, final String namespace, final Quotas quotas)
1103       throws IOException {
1104     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1105       @Override
1106       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1107           throws IOException {
1108         oserver.postSetUserQuota(ctx, user, namespace, quotas);
1109       }
1110     });
1111   }
1112 
1113   public void preSetTableQuota(final TableName table, final Quotas quotas) throws IOException {
1114     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1115       @Override
1116       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1117           throws IOException {
1118         oserver.preSetTableQuota(ctx, table, quotas);
1119       }
1120     });
1121   }
1122 
1123   public void postSetTableQuota(final TableName table, final Quotas quotas) throws IOException {
1124     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1125       @Override
1126       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1127           throws IOException {
1128         oserver.postSetTableQuota(ctx, table, quotas);
1129       }
1130     });
1131   }
1132 
1133   public void preSetNamespaceQuota(final String namespace, final Quotas quotas) throws IOException {
1134     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1135       @Override
1136       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1137           throws IOException {
1138         oserver.preSetNamespaceQuota(ctx, namespace, quotas);
1139       }
1140     });
1141   }
1142 
1143   public void postSetNamespaceQuota(final String namespace, final Quotas quotas) 
1144       throws IOException {
1145     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1146       @Override
1147       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1148           throws IOException {
1149         oserver.postSetNamespaceQuota(ctx, namespace, quotas);
1150       }
1151     });
1152   }
1153 
1154   private static abstract class CoprocessorOperation
1155       extends ObserverContext<MasterCoprocessorEnvironment> {
1156     public CoprocessorOperation() {
1157     }
1158 
1159     public abstract void call(MasterObserver oserver,
1160         ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException;
1161 
1162     public void postEnvCall(MasterEnvironment env) {
1163     }
1164   }
1165 
1166   private static abstract class CoprocessorOperationWithResult<T> extends CoprocessorOperation {
1167     private T result = null;
1168     public void setResult(final T result) { this.result = result; }
1169     public T getResult() { return this.result; }
1170   }
1171 
1172   private <T> T execOperationWithResult(final T defaultValue,
1173       final CoprocessorOperationWithResult<T> ctx) throws IOException {
1174     if (ctx == null) return defaultValue;
1175     ctx.setResult(defaultValue);
1176     execOperation(ctx);
1177     return ctx.getResult();
1178   }
1179 
1180   private boolean execOperation(final CoprocessorOperation ctx) throws IOException {
1181     if (ctx == null) return false;
1182     boolean bypass = false;
1183     List<MasterEnvironment> envs = coprocessors.get();
1184     for (int i = 0; i < envs.size(); i++) {
1185       MasterEnvironment env = envs.get(i);
1186       if (env.getInstance() instanceof MasterObserver) {
1187         ctx.prepare(env);
1188         Thread currentThread = Thread.currentThread();
1189         ClassLoader cl = currentThread.getContextClassLoader();
1190         try {
1191           currentThread.setContextClassLoader(env.getClassLoader());
1192           ctx.call((MasterObserver)env.getInstance(), ctx);
1193         } catch (Throwable e) {
1194           handleCoprocessorThrowable(env, e);
1195         } finally {
1196           currentThread.setContextClassLoader(cl);
1197         }
1198         bypass |= ctx.shouldBypass();
1199         if (ctx.shouldComplete()) {
1200           break;
1201         }
1202       }
1203       ctx.postEnvCall(env);
1204     }
1205     return bypass;
1206   }
1207 
1208   /**
1209    * Master coprocessor classes can be configured in any order, based on that priority is set and
1210    * chained in a sorted order. For preStopMaster()/preShutdown(), coprocessor methods are invoked
1211    * in call() and environment is shutdown in postEnvCall(). <br>
1212    * Need to execute all coprocessor methods first then postEnvCall(), otherwise some coprocessors
1213    * may remain shutdown if any exception occurs during next coprocessor execution which prevent
1214    * Master stop or cluster shutdown. (Refer:
1215    * <a href="https://issues.apache.org/jira/browse/HBASE-16663">HBASE-16663</a>
1216    * @param ctx CoprocessorOperation
1217    * @return true if bypaas coprocessor execution, false if not.
1218    * @throws IOException
1219    */
1220   private boolean execShutdown(final CoprocessorOperation ctx) throws IOException {
1221     if (ctx == null) return false;
1222     boolean bypass = false;
1223     List<MasterEnvironment> envs = coprocessors.get();
1224     int envsSize = envs.size();
1225     // Iterate the coprocessors and execute CoprocessorOperation's call()
1226     for (int i = 0; i < envsSize; i++) {
1227       MasterEnvironment env = envs.get(i);
1228       if (env.getInstance() instanceof MasterObserver) {
1229         ctx.prepare(env);
1230         Thread currentThread = Thread.currentThread();
1231         ClassLoader cl = currentThread.getContextClassLoader();
1232         try {
1233           currentThread.setContextClassLoader(env.getClassLoader());
1234           ctx.call((MasterObserver) env.getInstance(), ctx);
1235         } catch (Throwable e) {
1236           handleCoprocessorThrowable(env, e);
1237         } finally {
1238           currentThread.setContextClassLoader(cl);
1239         }
1240         bypass |= ctx.shouldBypass();
1241         if (ctx.shouldComplete()) {
1242           break;
1243         }
1244       }
1245     }
1246 
1247     // Iterate the coprocessors and execute CoprocessorOperation's postEnvCall()
1248     for (int i = 0; i < envsSize; i++) {
1249       MasterEnvironment env = envs.get(i);
1250       ctx.postEnvCall(env);
1251     }
1252     return bypass;
1253   }
1254 
1255 }