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 com.google.common.net.HostAndPort;
23  
24  import java.io.IOException;
25  import java.util.List;
26  import java.util.Set;
27  
28  import org.apache.commons.lang.ClassUtils;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.Coprocessor;
33  import org.apache.hadoop.hbase.HColumnDescriptor;
34  import org.apache.hadoop.hbase.HRegionInfo;
35  import org.apache.hadoop.hbase.HTableDescriptor;
36  import org.apache.hadoop.hbase.NamespaceDescriptor;
37  import org.apache.hadoop.hbase.ProcedureInfo;
38  import org.apache.hadoop.hbase.ServerName;
39  import org.apache.hadoop.hbase.TableName;
40  import org.apache.hadoop.hbase.classification.InterfaceAudience;
41  import org.apache.hadoop.hbase.client.MasterSwitchType;
42  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
43  import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
44  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
45  import org.apache.hadoop.hbase.coprocessor.MasterObserver;
46  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
47  import org.apache.hadoop.hbase.ipc.RpcServer;
48  import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
49  import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
50  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
51  import org.apache.hadoop.hbase.protobuf.generated.QuotaProtos.Quotas;
52  import org.apache.hadoop.hbase.security.User;
53
54  /**
55   * Provides the coprocessor framework and environment for master oriented
56   * operations.  {@link HMaster} interacts with the loaded coprocessors
57   * through this class.
58   */
59  @InterfaceAudience.Private
60  public class MasterCoprocessorHost
61      extends CoprocessorHost<MasterCoprocessorHost.MasterEnvironment> {
62  
63    private static final Log LOG = LogFactory.getLog(MasterCoprocessorHost.class);
64
65    /**
66     * Coprocessor environment extension providing access to master related
67     * services.
68     */
69    static class MasterEnvironment extends CoprocessorHost.Environment
70        implements MasterCoprocessorEnvironment {
71      private MasterServices masterServices;
72      final boolean supportGroupCPs;
73
74      public MasterEnvironment(final Class<?> implClass, final Coprocessor impl,
75          final int priority, final int seq, final Configuration conf,
76          final MasterServices services) {
77        super(impl, priority, seq, conf);
78        this.masterServices = services;
79        supportGroupCPs = !useLegacyMethod(impl.getClass(),
80            "preBalanceRSGroup", ObserverContext.class, String.class);
81      }
82
83      public MasterServices getMasterServices() {
84        return masterServices;
85      }
86    }
87  
88    private MasterServices masterServices;
89
90    public MasterCoprocessorHost(final MasterServices services, final Configuration conf) {
91      super(services);
92      this.conf = conf;
93      this.masterServices = services;
94      // Log the state of coprocessor loading here; should appear only once or
95      // twice in the daemon log, depending on HBase version, because there is
96      // only one MasterCoprocessorHost instance in the master process
97      boolean coprocessorsEnabled = conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY,
98        DEFAULT_COPROCESSORS_ENABLED);
99      LOG.info("System coprocessor loading is " + (coprocessorsEnabled ? "enabled" : "disabled"));
100     loadSystemCoprocessors(conf, MASTER_COPROCESSOR_CONF_KEY);
101   }
102
103   @Override
104   public MasterEnvironment createEnvironment(final Class<?> implClass,
105       final Coprocessor instance, final int priority, final int seq,
106       final Configuration conf) {
107     for (Object itf : ClassUtils.getAllInterfaces(implClass)) {
108       Class<?> c = (Class<?>) itf;
109       if (CoprocessorService.class.isAssignableFrom(c)) {
110         masterServices.registerService(((CoprocessorService)instance).getService());
111       }
112     }
113     return new MasterEnvironment(implClass, instance, priority, seq, conf,
114         masterServices);
115   }
116
117   public boolean preCreateNamespace(final NamespaceDescriptor ns) throws IOException {
118     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
119       @Override
120       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
121           throws IOException {
122         oserver.preCreateNamespace(ctx, ns);
123       }
124     });
125   }
126
127   public void postCreateNamespace(final NamespaceDescriptor ns) throws IOException {
128     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
129       @Override
130       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
131           throws IOException {
132         oserver.postCreateNamespace(ctx, ns);
133       }
134     });
135   }
136
137   public boolean preDeleteNamespace(final String namespaceName) throws IOException {
138     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
139       @Override
140       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
141           throws IOException {
142         oserver.preDeleteNamespace(ctx, namespaceName);
143       }
144     });
145   }
146
147   public void postDeleteNamespace(final String namespaceName) throws IOException {
148     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
149       @Override
150       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
151           throws IOException {
152         oserver.postDeleteNamespace(ctx, namespaceName);
153       }
154     });
155   }
156
157   public boolean preModifyNamespace(final NamespaceDescriptor ns) throws IOException {
158     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
159       @Override
160       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
161           throws IOException {
162         oserver.preModifyNamespace(ctx, ns);
163       }
164     });
165   }
166
167   public void postModifyNamespace(final NamespaceDescriptor ns) throws IOException {
168     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
169       @Override
170       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
171           throws IOException {
172         oserver.postModifyNamespace(ctx, ns);
173       }
174     });
175   }
176
177   public void preGetNamespaceDescriptor(final String namespaceName)
178       throws IOException {
179     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
180       @Override
181       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
182           throws IOException {
183         oserver.preGetNamespaceDescriptor(ctx, namespaceName);
184       }
185     });
186   }
187
188   public void postGetNamespaceDescriptor(final NamespaceDescriptor ns)
189       throws IOException {
190     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
191       @Override
192       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
193           throws IOException {
194         oserver.postGetNamespaceDescriptor(ctx, ns);
195       }
196     });
197   }
198
199   public boolean preListNamespaceDescriptors(final List<NamespaceDescriptor> descriptors)
200       throws IOException {
201     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
202       @Override
203       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
204           throws IOException {
205         oserver.preListNamespaceDescriptors(ctx, descriptors);
206       }
207     });
208   }
209
210   public void postListNamespaceDescriptors(final List<NamespaceDescriptor> descriptors)
211       throws IOException {
212     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
213       @Override
214       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
215           throws IOException {
216         oserver.postListNamespaceDescriptors(ctx, descriptors);
217       }
218     });
219   }
220 
221   /* Implementation of hooks for invoking MasterObservers */
222
223   public void preCreateTable(final HTableDescriptor htd, final HRegionInfo[] regions)
224       throws IOException {
225     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
226       @Override
227       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
228           throws IOException {
229         oserver.preCreateTable(ctx, htd, regions);
230       }
231     });
232   }
233
234   public void postCreateTable(final HTableDescriptor htd, final HRegionInfo[] regions)
235       throws IOException {
236     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
237       @Override
238       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
239           throws IOException {
240         oserver.postCreateTable(ctx, htd, regions);
241       }
242     });
243   }
244
245   public void preCreateTableAction(final HTableDescriptor htd, final HRegionInfo[] regions,
246                                    final User user)
247       throws IOException {
248     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
249       @Override
250       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
251           throws IOException {
252         oserver.preCreateTableHandler(ctx, htd, regions);
253         oserver.preCreateTableAction(ctx, htd, regions);
254       }
255     });
256   }
257
258   public void postCompletedCreateTableAction(
259       final HTableDescriptor htd, final HRegionInfo[] regions, final User user) throws IOException {
260     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
261       @Override
262       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
263           throws IOException {
264         oserver.postCreateTableHandler(ctx, htd, regions);
265         oserver.postCompletedCreateTableAction(ctx, htd, regions);
266       }
267     });
268   }
269
270   public void preDeleteTable(final TableName tableName) throws IOException {
271     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
272       @Override
273       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
274           throws IOException {
275         oserver.preDeleteTable(ctx, tableName);
276       }
277     });
278   }
279
280   public void postDeleteTable(final TableName tableName) throws IOException {
281     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
282       @Override
283       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
284           throws IOException {
285         oserver.postDeleteTable(ctx, tableName);
286       }
287     });
288   }
289
290   public void preDeleteTableAction(final TableName tableName, final User user) throws IOException {
291     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
292       @Override
293       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
294           throws IOException {
295         oserver.preDeleteTableHandler(ctx, tableName);
296         oserver.preDeleteTableAction(ctx, tableName);
297       }
298     });
299   }
300
301   public void postCompletedDeleteTableAction(final TableName tableName, final User user)
302       throws IOException {
303     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
304       @Override
305       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
306           throws IOException {
307         oserver.postDeleteTableHandler(ctx, tableName);
308         oserver.postCompletedDeleteTableAction(ctx, tableName);
309       }
310     });
311   }
312
313   public void preTruncateTable(final TableName tableName) throws IOException {
314     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
315       @Override
316       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
317           throws IOException {
318         oserver.preTruncateTable(ctx, tableName);
319       }
320     });
321   }
322
323   public void postTruncateTable(final TableName tableName) throws IOException {
324     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
325       @Override
326       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
327           throws IOException {
328         oserver.postTruncateTable(ctx, tableName);
329       }
330     });
331   }
332
333   public void preTruncateTableAction(final TableName tableName, final User user) throws IOException {
334     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
335       @Override
336       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
337           throws IOException {
338         oserver.preTruncateTableHandler(ctx, tableName);
339         oserver.preTruncateTableAction(ctx, tableName);
340       }
341     });
342   }
343
344   public void postCompletedTruncateTableAction(final TableName tableName, final User user)
345       throws IOException {
346     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
347       @Override
348       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
349           throws IOException {
350         oserver.postTruncateTableHandler(ctx, tableName);
351         oserver.postCompletedTruncateTableAction(ctx, tableName);
352       }
353     });
354   }
355
356   public void preModifyTable(final TableName tableName, final HTableDescriptor htd)
357       throws IOException {
358     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
359       @Override
360       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
361           throws IOException {
362         oserver.preModifyTable(ctx, tableName, htd);
363       }
364     });
365   }
366
367   public void postModifyTable(final TableName tableName, final HTableDescriptor htd)
368       throws IOException {
369     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
370       @Override
371       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
372           throws IOException {
373         oserver.postModifyTable(ctx, tableName, htd);
374       }
375     });
376   }
377
378   public void preModifyTableAction(final TableName tableName, final HTableDescriptor htd,
379                                    final User user)
380       throws IOException {
381     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
382       @Override
383       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
384           throws IOException {
385         oserver.preModifyTableHandler(ctx, tableName, htd);
386         oserver.preModifyTableAction(ctx, tableName, htd);
387       }
388     });
389   }
390
391   public void postCompletedModifyTableAction(final TableName tableName, final HTableDescriptor htd,
392                                              final User user)
393       throws IOException {
394     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
395       @Override
396       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
397           throws IOException {
398         oserver.postModifyTableHandler(ctx, tableName, htd);
399         oserver.postCompletedModifyTableAction(ctx, tableName, htd);
400       }
401     });
402   }
403
404   public boolean preAddColumn(final TableName tableName, final HColumnDescriptor columnFamily)
405       throws IOException {
406     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
407       @Override
408       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
409           throws IOException {
410         oserver.preAddColumn(ctx, tableName, columnFamily);
411         oserver.preAddColumnFamily(ctx, tableName, columnFamily);
412       }
413     });
414   }
415
416   public void postAddColumn(final TableName tableName, final HColumnDescriptor columnFamily)
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.postAddColumn(ctx, tableName, columnFamily);
423         oserver.postAddColumnFamily(ctx, tableName, columnFamily);
424       }
425     });
426   }
427
428   public boolean preAddColumnFamilyAction(
429       final TableName tableName,
430       final HColumnDescriptor columnFamily,
431       final User user)
432       throws IOException {
433     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
434       @Override
435       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
436           throws IOException {
437         oserver.preAddColumnHandler(ctx, tableName, columnFamily);
438         oserver.preAddColumnFamilyAction(ctx, tableName, columnFamily);
439       }
440     });
441   }
442
443   public void postCompletedAddColumnFamilyAction(
444       final TableName tableName,
445       final HColumnDescriptor columnFamily,
446       final User user)
447       throws IOException {
448     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
449       @Override
450       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
451           throws IOException {
452         oserver.postAddColumnHandler(ctx, tableName, columnFamily);
453         oserver.postCompletedAddColumnFamilyAction(ctx, tableName, columnFamily);
454       }
455     });
456   }
457
458   public boolean preModifyColumn(final TableName tableName, final HColumnDescriptor columnFamily)
459       throws IOException {
460     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
461       @Override
462       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
463           throws IOException {
464         oserver.preModifyColumn(ctx, tableName, columnFamily);
465         oserver.preModifyColumnFamily(ctx, tableName, columnFamily);
466       }
467     });
468   }
469
470   public void postModifyColumn(final TableName tableName, final HColumnDescriptor columnFamily)
471       throws IOException {
472     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
473       @Override
474       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
475           throws IOException {
476         oserver.postModifyColumn(ctx, tableName, columnFamily);
477         oserver.postModifyColumnFamily(ctx, tableName, columnFamily);
478       }
479     });
480   }
481
482   public boolean preModifyColumnFamilyAction(
483       final TableName tableName,
484       final HColumnDescriptor columnFamily,
485       final User user) throws IOException {
486     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
487       @Override
488       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
489           throws IOException {
490         oserver.preModifyColumnHandler(ctx, tableName, columnFamily);
491         oserver.preModifyColumnFamilyAction(ctx, tableName, columnFamily);
492       }
493     });
494   }
495
496   public void postCompletedModifyColumnFamilyAction(
497       final TableName tableName,
498       final HColumnDescriptor columnFamily,
499       final User user) throws IOException {
500     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
501       @Override
502       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
503           throws IOException {
504         oserver.postModifyColumnHandler(ctx, tableName, columnFamily);
505         oserver.postCompletedModifyColumnFamilyAction(ctx, tableName, columnFamily);
506       }
507     });
508   }
509
510   public boolean preDeleteColumn(final TableName tableName, final byte[] columnFamily)
511       throws IOException {
512     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
513       @Override
514       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
515           throws IOException {
516         oserver.preDeleteColumn(ctx, tableName, columnFamily);
517         oserver.preDeleteColumnFamily(ctx, tableName, columnFamily);
518       }
519     });
520   }
521
522   public void postDeleteColumn(final TableName tableName, final byte[] columnFamily)
523       throws IOException {
524     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
525       @Override
526       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
527           throws IOException {
528         oserver.postDeleteColumn(ctx, tableName, columnFamily);
529         oserver.postDeleteColumnFamily(ctx, tableName, columnFamily);
530       }
531     });
532   }
533
534   public boolean preDeleteColumnFamilyAction(
535       final TableName tableName,
536       final byte[] columnFamily,
537       final User user)
538       throws IOException {
539     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
540       @Override
541       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
542           throws IOException {
543         oserver.preDeleteColumnHandler(ctx, tableName, columnFamily);
544         oserver.preDeleteColumnFamilyAction(ctx, tableName, columnFamily);
545       }
546     });
547   }
548
549   public void postCompletedDeleteColumnFamilyAction(
550       final TableName tableName, final byte[] columnFamily, final User user) throws IOException {
551     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
552       @Override
553       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
554           throws IOException {
555         oserver.postDeleteColumnHandler(ctx, tableName, columnFamily);
556         oserver.postCompletedDeleteColumnFamilyAction(ctx, tableName, columnFamily);
557       }
558     });
559   }
560
561   public void preEnableTable(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.preEnableTable(ctx, tableName);
567       }
568     });
569   }
570
571   public void postEnableTable(final TableName tableName) throws IOException {
572     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
573       @Override
574       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
575           throws IOException {
576         oserver.postEnableTable(ctx, tableName);
577       }
578     });
579   }
580
581   public void preEnableTableAction(final TableName tableName, final User user) throws IOException {
582     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
583       @Override
584       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
585           throws IOException {
586         oserver.preEnableTableHandler(ctx, tableName);
587         oserver.preEnableTableAction(ctx, tableName);
588       }
589     });
590   }
591
592   public void postCompletedEnableTableAction(final TableName tableName, final User user)
593       throws IOException {
594     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
595       @Override
596       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
597           throws IOException {
598         oserver.postEnableTableHandler(ctx, tableName);
599         oserver.postCompletedEnableTableAction(ctx, tableName);
600       }
601     });
602   }
603
604   public void preDisableTable(final TableName tableName) throws IOException {
605     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
606       @Override
607       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
608           throws IOException {
609         oserver.preDisableTable(ctx, tableName);
610       }
611     });
612   }
613
614   public void postDisableTable(final TableName tableName) throws IOException {
615     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
616       @Override
617       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
618           throws IOException {
619         oserver.postDisableTable(ctx, tableName);
620       }
621     });
622   }
623
624   public void preDisableTableAction(final TableName tableName, final User user) throws IOException {
625     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
626       @Override
627       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
628           throws IOException {
629         oserver.preDisableTableHandler(ctx, tableName);
630         oserver.preDisableTableAction(ctx, tableName);
631       }
632     });
633   }
634
635   public void postCompletedDisableTableAction(final TableName tableName, final User user)
636       throws IOException {
637     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation(user) {
638       @Override
639       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
640           throws IOException {
641         oserver.postDisableTableHandler(ctx, tableName);
642         oserver.postCompletedDisableTableAction(ctx, tableName);
643       }
644     });
645   }
646
647   public boolean preAbortProcedure(
648       final ProcedureExecutor<MasterProcedureEnv> procEnv,
649       final long procId) throws IOException {
650     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
651       @Override
652       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
653           throws IOException {
654         oserver.preAbortProcedure(ctx, procEnv, procId);
655       }
656     });
657   }
658
659   public void postAbortProcedure() throws IOException {
660     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
661       @Override
662       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
663           throws IOException {
664         oserver.postAbortProcedure(ctx);
665       }
666     });
667   }
668
669   public boolean preListProcedures() throws IOException {
670     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
671       @Override
672       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
673           throws IOException {
674         oserver.preListProcedures(ctx);
675       }
676     });
677   }
678
679   public void postListProcedures(final List<ProcedureInfo> procInfoList) throws IOException {
680     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
681       @Override
682       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
683           throws IOException {
684         oserver.postListProcedures(ctx, procInfoList);
685       }
686     });
687   }
688
689   public boolean preMove(final HRegionInfo region, final ServerName srcServer,
690       final ServerName destServer) throws IOException {
691     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
692       @Override
693       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
694           throws IOException {
695         oserver.preMove(ctx, region, srcServer, destServer);
696       }
697     });
698   }
699
700   public void postMove(final HRegionInfo region, final ServerName srcServer,
701       final ServerName destServer) throws IOException {
702     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
703       @Override
704       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
705           throws IOException {
706         oserver.postMove(ctx, region, srcServer, destServer);
707       }
708     });
709   }
710
711   public boolean preAssign(final HRegionInfo regionInfo) throws IOException {
712     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
713       @Override
714       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
715           throws IOException {
716         oserver.preAssign(ctx, regionInfo);
717       }
718     });
719   }
720
721   public void postAssign(final HRegionInfo regionInfo) throws IOException {
722     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
723       @Override
724       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
725           throws IOException {
726         oserver.postAssign(ctx, regionInfo);
727       }
728     });
729   }
730
731   public boolean preUnassign(final HRegionInfo regionInfo, final boolean force)
732       throws IOException {
733     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
734       @Override
735       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
736           throws IOException {
737         oserver.preUnassign(ctx, regionInfo, force);
738       }
739     });
740   }
741
742   public void postUnassign(final HRegionInfo regionInfo, final boolean force) throws IOException {
743     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
744       @Override
745       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
746           throws IOException {
747         oserver.postUnassign(ctx, regionInfo, force);
748       }
749     });
750   }
751
752   public void preRegionOffline(final HRegionInfo regionInfo) throws IOException {
753     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
754       @Override
755       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
756           throws IOException {
757         oserver.preRegionOffline(ctx, regionInfo);
758       }
759     });
760   }
761
762   public void postRegionOffline(final HRegionInfo regionInfo) throws IOException {
763     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
764       @Override
765       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
766           throws IOException {
767         oserver.postRegionOffline(ctx, regionInfo);
768       }
769     });
770   }
771
772   public void preDispatchMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB)
773       throws IOException {
774     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
775       @Override
776       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
777           throws IOException {
778         oserver.preDispatchMerge(ctx, regionInfoA, regionInfoB);
779       }
780     });
781   }
782
783   public void postDispatchMerge(final HRegionInfo regionInfoA, final HRegionInfo regionInfoB)
784       throws IOException {
785     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
786       @Override
787       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
788           throws IOException {
789         oserver.postDispatchMerge(ctx, regionInfoA, regionInfoB);
790       }
791     });
792   }
793
794   public boolean preBalance() throws IOException {
795     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
796       @Override
797       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
798           throws IOException {
799         oserver.preBalance(ctx);
800       }
801     });
802   }
803
804   public void postBalance(final List<RegionPlan> plans) throws IOException {
805     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
806       @Override
807       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
808           throws IOException {
809         oserver.postBalance(ctx, plans);
810       }
811     });
812   }
813
814   public boolean preSetSplitOrMergeEnabled(final boolean newValue,
815       final MasterSwitchType switchType) throws IOException {
816     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
817       @Override
818       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
819           throws IOException {
820         oserver.preSetSplitOrMergeEnabled(ctx, newValue, switchType);
821       }
822     });
823   }
824
825   public void postSetSplitOrMergeEnabled(final boolean newValue,
826       final MasterSwitchType switchType) throws IOException {
827     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
828       @Override
829       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
830           throws IOException {
831         oserver.postSetSplitOrMergeEnabled(ctx, newValue, switchType);
832       }
833     });
834   }
835
836   public boolean preBalanceSwitch(final boolean b) throws IOException {
837     return execOperationWithResult(b, coprocessors.isEmpty() ? null :
838         new CoprocessorOperationWithResult<Boolean>() {
839       @Override
840       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
841           throws IOException {
842         setResult(oserver.preBalanceSwitch(ctx, getResult()));
843       }
844     });
845   }
846
847   public void postBalanceSwitch(final boolean oldValue, final boolean newValue)
848       throws IOException {
849     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
850       @Override
851       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
852           throws IOException {
853         oserver.postBalanceSwitch(ctx, oldValue, newValue);
854       }
855     });
856   }
857
858   public void preShutdown() throws IOException {
859     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
860       @Override
861       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
862           throws IOException {
863         oserver.preShutdown(ctx);
864       }
865       @Override
866       public void postEnvCall(MasterEnvironment env) {
867         // invoke coprocessor stop method
868         shutdown(env);
869       }
870     });
871   }
872
873   public void preStopMaster() throws IOException {
874     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
875       @Override
876       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
877           throws IOException {
878         oserver.preStopMaster(ctx);
879       }
880       @Override
881       public void postEnvCall(MasterEnvironment env) {
882         // invoke coprocessor stop method
883         shutdown(env);
884       }
885     });
886   }
887
888   public void preMasterInitialization() throws IOException {
889     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
890       @Override
891       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
892           throws IOException {
893         oserver.preMasterInitialization(ctx);
894       }
895     });
896   }
897
898   public void postStartMaster() 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.postStartMaster(ctx);
904       }
905     });
906   }
907
908   public void preSnapshot(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.preSnapshot(ctx, snapshot, hTableDescriptor);
915       }
916     });
917   }
918
919   public void postSnapshot(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.postSnapshot(ctx, snapshot, hTableDescriptor);
926       }
927     });
928   }
929
930   public void preListSnapshot(final SnapshotDescription snapshot) throws IOException {
931     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
932       @Override
933       public void call(MasterObserver observer, ObserverContext<MasterCoprocessorEnvironment> ctx)
934           throws IOException {
935         observer.preListSnapshot(ctx, snapshot);
936       }
937     });
938   }
939
940   public void postListSnapshot(final SnapshotDescription snapshot) throws IOException {
941     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
942       @Override
943       public void call(MasterObserver observer, ObserverContext<MasterCoprocessorEnvironment> ctx)
944           throws IOException {
945         observer.postListSnapshot(ctx, snapshot);
946       }
947     });
948   }
949
950   public void preCloneSnapshot(final SnapshotDescription snapshot,
951       final HTableDescriptor hTableDescriptor) 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.preCloneSnapshot(ctx, snapshot, hTableDescriptor);
957       }
958     });
959   }
960
961   public void postCloneSnapshot(final SnapshotDescription snapshot,
962       final HTableDescriptor hTableDescriptor) throws IOException {
963     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
964       @Override
965       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
966           throws IOException {
967         oserver.postCloneSnapshot(ctx, snapshot, hTableDescriptor);
968       }
969     });
970   }
971
972   public void preRestoreSnapshot(final SnapshotDescription snapshot,
973       final HTableDescriptor hTableDescriptor) throws IOException {
974     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
975       @Override
976       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
977           throws IOException {
978         oserver.preRestoreSnapshot(ctx, snapshot, hTableDescriptor);
979       }
980     });
981   }
982
983   public void postRestoreSnapshot(final SnapshotDescription snapshot,
984       final HTableDescriptor hTableDescriptor) throws IOException {
985     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
986       @Override
987       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
988           throws IOException {
989         oserver.postRestoreSnapshot(ctx, snapshot, hTableDescriptor);
990       }
991     });
992   }
993
994   public void preDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
995     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
996       @Override
997       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
998           throws IOException {
999         oserver.preDeleteSnapshot(ctx, snapshot);
1000       }
1001     });
1002   }
1003
1004   public void postDeleteSnapshot(final SnapshotDescription snapshot) throws IOException {
1005     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1006       @Override
1007       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1008           throws IOException {
1009         oserver.postDeleteSnapshot(ctx, snapshot);
1010       }
1011     });
1012   }
1013
1014   public boolean preGetTableDescriptors(final List<TableName> tableNamesList,
1015       final List<HTableDescriptor> descriptors, final String regex) throws IOException {
1016     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1017       @Override
1018       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1019           throws IOException {
1020         oserver.preGetTableDescriptors(ctx, tableNamesList, descriptors, regex);
1021       }
1022     });
1023   }
1024
1025   public void postGetTableDescriptors(final List<TableName> tableNamesList,
1026       final List<HTableDescriptor> descriptors, final String regex) throws IOException {
1027     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1028       @Override
1029       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1030           throws IOException {
1031         oserver.postGetTableDescriptors(ctx, tableNamesList, descriptors, regex);
1032       }
1033     });
1034   }
1035
1036   public boolean preGetTableNames(final List<HTableDescriptor> descriptors,
1037       final String regex) throws IOException {
1038     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1039       @Override
1040       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1041           throws IOException {
1042         oserver.preGetTableNames(ctx, descriptors, regex);
1043       }
1044     });
1045   }
1046
1047   public void postGetTableNames(final List<HTableDescriptor> descriptors,
1048       final String regex) throws IOException {
1049     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1050       @Override
1051       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1052           throws IOException {
1053         oserver.postGetTableNames(ctx, descriptors, regex);
1054       }
1055     });
1056   }
1057
1058   public void preTableFlush(final TableName tableName) throws IOException {
1059     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1060       @Override
1061       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1062           throws IOException {
1063         oserver.preTableFlush(ctx, tableName);
1064       }
1065     });
1066   }
1067
1068   public void postTableFlush(final TableName tableName) throws IOException {
1069     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1070       @Override
1071       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1072           throws IOException {
1073         oserver.postTableFlush(ctx, tableName);
1074       }
1075     });
1076   }
1077
1078   public void preSetUserQuota(final String user, final Quotas quotas) throws IOException {
1079     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1080       @Override
1081       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1082           throws IOException {
1083         oserver.preSetUserQuota(ctx, user, quotas);
1084       }
1085     });
1086   }
1087
1088   public void postSetUserQuota(final String user, final Quotas quotas) throws IOException {
1089     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1090       @Override
1091       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1092           throws IOException {
1093         oserver.postSetUserQuota(ctx, user, quotas);
1094       }
1095     });
1096   }
1097
1098   public void preSetUserQuota(final String user, final TableName table, final Quotas quotas)
1099       throws IOException {
1100     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1101       @Override
1102       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1103           throws IOException {
1104         oserver.preSetUserQuota(ctx, user, table, quotas);
1105       }
1106     });
1107   }
1108
1109   public void postSetUserQuota(final String user, final TableName table, final Quotas quotas)
1110       throws IOException {
1111     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1112       @Override
1113       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1114           throws IOException {
1115         oserver.postSetUserQuota(ctx, user, table, quotas);
1116       }
1117     });
1118   }
1119
1120   public void preSetUserQuota(final String user, final String namespace, final Quotas quotas)
1121       throws IOException {
1122     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1123       @Override
1124       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1125           throws IOException {
1126         oserver.preSetUserQuota(ctx, user, namespace, quotas);
1127       }
1128     });
1129   }
1130
1131   public void postSetUserQuota(final String user, final String namespace, final Quotas quotas)
1132       throws IOException {
1133     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1134       @Override
1135       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1136           throws IOException {
1137         oserver.postSetUserQuota(ctx, user, namespace, quotas);
1138       }
1139     });
1140   }
1141
1142   public void preSetTableQuota(final TableName table, final Quotas quotas) throws IOException {
1143     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1144       @Override
1145       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1146           throws IOException {
1147         oserver.preSetTableQuota(ctx, table, quotas);
1148       }
1149     });
1150   }
1151
1152   public void postSetTableQuota(final TableName table, final Quotas quotas) throws IOException {
1153     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1154       @Override
1155       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1156           throws IOException {
1157         oserver.postSetTableQuota(ctx, table, quotas);
1158       }
1159     });
1160   }
1161
1162   public void preSetNamespaceQuota(final String namespace, final Quotas quotas) throws IOException {
1163     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1164       @Override
1165       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1166           throws IOException {
1167         oserver.preSetNamespaceQuota(ctx, namespace, quotas);
1168       }
1169     });
1170   }
1171
1172   public void postSetNamespaceQuota(final String namespace, final Quotas quotas) throws IOException{
1173     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1174       @Override
1175       public void call(MasterObserver oserver, ObserverContext<MasterCoprocessorEnvironment> ctx)
1176           throws IOException {
1177         oserver.postSetNamespaceQuota(ctx, namespace, quotas);
1178       }
1179     });
1180   }
1181
1182   private static abstract class CoprocessorOperation
1183       extends ObserverContext<MasterCoprocessorEnvironment> {
1184     public CoprocessorOperation() {
1185       this(RpcServer.getRequestUser());
1186     }
1187
1188     public CoprocessorOperation(User user) {
1189       super(user);
1190     }
1191
1192     public abstract void call(MasterObserver oserver,
1193         ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException;
1194
1195     public void postEnvCall(MasterEnvironment env) {
1196     }
1197   }
1198
1199   private static abstract class CoprocessorOperationWithResult<T> extends CoprocessorOperation {
1200     private T result = null;
1201     public void setResult(final T result) { this.result = result; }
1202     public T getResult() { return this.result; }
1203   }
1204
1205   private <T> T execOperationWithResult(final T defaultValue,
1206       final CoprocessorOperationWithResult<T> ctx) throws IOException {
1207     if (ctx == null) return defaultValue;
1208     ctx.setResult(defaultValue);
1209     execOperation(ctx);
1210     return ctx.getResult();
1211   }
1212
1213   private boolean execOperation(final CoprocessorOperation ctx) throws IOException {
1214     if (ctx == null) return false;
1215     boolean bypass = false;
1216     List<MasterEnvironment> envs = coprocessors.get();
1217     for (int i = 0; i < envs.size(); i++) {
1218       MasterEnvironment env = envs.get(i);
1219       if (env.getInstance() instanceof MasterObserver) {
1220         ctx.prepare(env);
1221         Thread currentThread = Thread.currentThread();
1222         ClassLoader cl = currentThread.getContextClassLoader();
1223         try {
1224           currentThread.setContextClassLoader(env.getClassLoader());
1225           ctx.call((MasterObserver)env.getInstance(), ctx);
1226         } catch (Throwable e) {
1227           handleCoprocessorThrowable(env, e);
1228         } finally {
1229           currentThread.setContextClassLoader(cl);
1230         }
1231         bypass |= ctx.shouldBypass();
1232         if (ctx.shouldComplete()) {
1233           break;
1234         }
1235       }
1236       ctx.postEnvCall(env);
1237     }
1238     return bypass;
1239   }
1240
1241   public void preMoveServers(final Set<HostAndPort> servers, final String targetGroup)
1242       throws IOException {
1243     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1244       @Override
1245       public void call(MasterObserver oserver,
1246           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1247         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1248           oserver.preMoveServers(ctx, servers, targetGroup);
1249         }
1250       }
1251     });
1252   }
1253
1254   public void postMoveServers(final Set<HostAndPort> servers, final String targetGroup)
1255       throws IOException {
1256     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1257       @Override
1258       public void call(MasterObserver oserver,
1259           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1260         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1261           oserver.postMoveServers(ctx, servers, targetGroup);
1262         }
1263       }
1264     });
1265   }
1266
1267   public void preMoveTables(final Set<TableName> tables, final String targetGroup)
1268       throws IOException {
1269     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1270       @Override
1271       public void call(MasterObserver oserver,
1272           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1273         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1274           oserver.preMoveTables(ctx, tables, targetGroup);
1275         }
1276       }
1277     });
1278   }
1279
1280   public void postMoveTables(final Set<TableName> tables, final String targetGroup)
1281       throws IOException {
1282     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1283       @Override
1284       public void call(MasterObserver oserver,
1285           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1286         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1287           oserver.postMoveTables(ctx, tables, targetGroup);
1288         }
1289       }
1290     });
1291   }
1292
1293   public void preAddRSGroup(final String name)
1294       throws IOException {
1295     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1296       @Override
1297       public void call(MasterObserver oserver,
1298           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1299         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1300           oserver.preAddRSGroup(ctx, name);
1301         }
1302       }
1303     });
1304   }
1305
1306   public void postAddRSGroup(final String name)
1307       throws IOException {
1308     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1309       @Override
1310       public void call(MasterObserver oserver,
1311           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1312         if (((MasterEnvironment) ctx.getEnvironment()).supportGroupCPs) {
1313           oserver.postAddRSGroup(ctx, name);
1314         }
1315       }
1316     });
1317   }
1318
1319   public void preRemoveRSGroup(final String name)
1320       throws IOException {
1321     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1322       @Override
1323       public void call(MasterObserver oserver,
1324           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1325         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1326           oserver.preRemoveRSGroup(ctx, name);
1327         }
1328       }
1329     });
1330   }
1331
1332   public void postRemoveRSGroup(final String name)
1333       throws IOException {
1334     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1335       @Override
1336       public void call(MasterObserver oserver,
1337           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1338         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1339           oserver.postRemoveRSGroup(ctx, name);
1340         }
1341       }
1342     });
1343   }
1344
1345   public void preBalanceRSGroup(final String name)
1346       throws IOException {
1347     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1348       @Override
1349       public void call(MasterObserver oserver,
1350           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1351         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1352           oserver.preBalanceRSGroup(ctx, name);
1353         }
1354       }
1355     });
1356   }
1357
1358   public void postBalanceRSGroup(final String name, final boolean balanceRan)
1359       throws IOException {
1360     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
1361       @Override
1362       public void call(MasterObserver oserver,
1363           ObserverContext<MasterCoprocessorEnvironment> ctx) throws IOException {
1364         if(((MasterEnvironment)ctx.getEnvironment()).supportGroupCPs) {
1365           oserver.postBalanceRSGroup(ctx, name, balanceRan);
1366         }
1367       }
1368     });
1369   }
1370
1371 }