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  package org.apache.hadoop.hbase.regionserver;
20  
21  import java.io.IOException;
22  import java.util.Comparator;
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.CellScanner;
30  import org.apache.hadoop.hbase.Coprocessor;
31  import org.apache.hadoop.hbase.CoprocessorEnvironment;
32  import org.apache.hadoop.hbase.HBaseInterfaceAudience;
33  import org.apache.hadoop.hbase.MetaMutationAnnotation;
34  import org.apache.hadoop.hbase.classification.InterfaceAudience;
35  import org.apache.hadoop.hbase.classification.InterfaceStability;
36  import org.apache.hadoop.hbase.client.Mutation;
37  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
38  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
39  import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
40  import org.apache.hadoop.hbase.coprocessor.RegionServerObserver;
41  import org.apache.hadoop.hbase.coprocessor.SingletonCoprocessorService;
42  import org.apache.hadoop.hbase.protobuf.generated.AdminProtos.WALEntry;
43  import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
44  
45  @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
46  @InterfaceStability.Evolving
47  public class RegionServerCoprocessorHost extends
48      CoprocessorHost<RegionServerCoprocessorHost.RegionServerEnvironment> {
49  
50    private static final Log LOG = LogFactory.getLog(RegionServerCoprocessorHost.class);
51  
52    private RegionServerServices rsServices;
53  
54    public RegionServerCoprocessorHost(RegionServerServices rsServices,
55        Configuration conf) {
56      super(rsServices);
57      this.rsServices = rsServices;
58      this.conf = conf;
59      // Log the state of coprocessor loading here; should appear only once or
60      // twice in the daemon log, depending on HBase version, because there is
61      // only one RegionServerCoprocessorHost instance in the RS process
62      boolean coprocessorsEnabled = conf.getBoolean(COPROCESSORS_ENABLED_CONF_KEY,
63        DEFAULT_COPROCESSORS_ENABLED);
64      boolean tableCoprocessorsEnabled = conf.getBoolean(USER_COPROCESSORS_ENABLED_CONF_KEY,
65        DEFAULT_USER_COPROCESSORS_ENABLED);
66      LOG.info("System coprocessor loading is " + (coprocessorsEnabled ? "enabled" : "disabled"));
67      LOG.info("Table coprocessor loading is " +
68        ((coprocessorsEnabled && tableCoprocessorsEnabled) ? "enabled" : "disabled"));
69      loadSystemCoprocessors(conf, REGIONSERVER_COPROCESSOR_CONF_KEY);
70    }
71  
72    @Override
73    public RegionServerEnvironment createEnvironment(Class<?> implClass,
74        Coprocessor instance, int priority, int sequence, Configuration conf) {
75      return new RegionServerEnvironment(implClass, instance, priority,
76        sequence, conf, this.rsServices);
77    }
78  
79    public void preStop(String message) throws IOException {
80      execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
81        @Override
82        public void call(RegionServerObserver oserver,
83            ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
84          oserver.preStopRegionServer(ctx);
85        }
86        @Override
87        public void postEnvCall(RegionServerEnvironment env) {
88          // invoke coprocessor stop method
89          shutdown(env);
90        }
91      });
92    }
93  
94    public boolean preMerge(final HRegion regionA, final HRegion regionB) throws IOException {
95      return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
96        @Override
97        public void call(RegionServerObserver oserver,
98            ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
99          oserver.preMerge(ctx, regionA, regionB);
100       }
101     });
102   }
103 
104   public void postMerge(final HRegion regionA, final HRegion regionB, final HRegion mergedRegion)
105       throws IOException {
106     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
107       @Override
108       public void call(RegionServerObserver oserver,
109           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
110         oserver.postMerge(ctx, regionA, regionB, mergedRegion);
111       }
112     });
113   }
114 
115   public boolean preMergeCommit(final HRegion regionA, final HRegion regionB,
116       final @MetaMutationAnnotation List<Mutation> metaEntries) throws IOException {
117     return execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
118       @Override
119       public void call(RegionServerObserver oserver,
120           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
121         oserver.preMergeCommit(ctx, regionA, regionB, metaEntries);
122       }
123     });
124   }
125 
126   public void postMergeCommit(final HRegion regionA, final HRegion regionB,
127       final HRegion mergedRegion) throws IOException {
128     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
129       @Override
130       public void call(RegionServerObserver oserver,
131           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
132         oserver.postMergeCommit(ctx, regionA, regionB, mergedRegion);
133       }
134     });
135   }
136 
137   public void preRollBackMerge(final HRegion regionA, final HRegion regionB) throws IOException {
138     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
139       @Override
140       public void call(RegionServerObserver oserver,
141           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
142         oserver.preRollBackMerge(ctx, regionA, regionB);
143       }
144     });
145   }
146 
147   public void postRollBackMerge(final HRegion regionA, final HRegion regionB) throws IOException {
148     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
149       @Override
150       public void call(RegionServerObserver oserver,
151           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
152         oserver.postRollBackMerge(ctx, regionA, regionB);
153       }
154     });
155   }
156 
157   public void preRollWALWriterRequest() throws IOException {
158     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
159       @Override
160       public void call(RegionServerObserver oserver,
161           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
162         oserver.preRollWALWriterRequest(ctx);
163       }
164     });
165   }
166 
167   public void postRollWALWriterRequest() throws IOException {
168     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
169       @Override
170       public void call(RegionServerObserver oserver,
171           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
172         oserver.postRollWALWriterRequest(ctx);
173       }
174     });
175   }
176 
177   public void preReplicateLogEntries(final List<WALEntry> entries, final CellScanner cells)
178       throws IOException {
179     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
180       @Override
181       public void call(RegionServerObserver oserver,
182           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
183         oserver.preReplicateLogEntries(ctx, entries, cells);
184       }
185     });
186   }
187 
188   public void postReplicateLogEntries(final List<WALEntry> entries, final CellScanner cells)
189       throws IOException {
190     execOperation(coprocessors.isEmpty() ? null : new CoprocessorOperation() {
191       @Override
192       public void call(RegionServerObserver oserver,
193           ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
194         oserver.postReplicateLogEntries(ctx, entries, cells);
195       }
196     });
197   }
198 
199   public ReplicationEndpoint postCreateReplicationEndPoint(final ReplicationEndpoint endpoint)
200       throws IOException {
201     return execOperationWithResult(endpoint, coprocessors.isEmpty() ? null
202         : new CoprocessOperationWithResult<ReplicationEndpoint>() {
203           @Override
204           public void call(RegionServerObserver oserver,
205               ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException {
206             try {
207               oserver.getClass().getDeclaredMethod("postCreateReplicationEndPoint",
208                   ObserverContext.class, ReplicationEndpoint.class);
209             } catch (NoSuchMethodException e) {
210               LOG.warn("The RegionServer Observer class "
211                   + oserver.getClass().getName()
212                   + " does not have the"
213                   + "method postCreateReplicationEndPoint(). Consider upgrading inorder to replicate visibility"
214                   + " labels as strings");
215               setResult(endpoint);
216               return;
217             } catch (SecurityException e) {
218               LOG.warn("The RegionServer Observer class "
219                   + oserver.getClass().getName()
220                   + " does not have the"
221                   + "method postCreateReplicationEndPoint(). Consider upgrading inorder to replicate visibility"
222                   + " labels as strings");
223               setResult(endpoint);
224               return;
225             }
226             setResult(oserver.postCreateReplicationEndPoint(ctx, getResult()));
227           }
228         });
229   }
230 
231   private <T> T execOperationWithResult(final T defaultValue,
232       final CoprocessOperationWithResult<T> ctx) throws IOException {
233     if (ctx == null)
234       return defaultValue;
235     ctx.setResult(defaultValue);
236     execOperation(ctx);
237     return ctx.getResult();
238   }
239 
240   private static abstract class CoprocessorOperation extends
241       ObserverContext<RegionServerCoprocessorEnvironment> {
242     public CoprocessorOperation() {
243     }
244 
245     public abstract void call(RegionServerObserver oserver,
246         ObserverContext<RegionServerCoprocessorEnvironment> ctx) throws IOException;
247 
248     public void postEnvCall(RegionServerEnvironment env) {
249     }
250   }
251 
252   private static abstract class CoprocessOperationWithResult<T> extends CoprocessorOperation {
253     private T result = null;
254 
255     public void setResult(final T result) {
256       this.result = result;
257     }
258 
259     public T getResult() {
260       return this.result;
261     }
262   }
263 
264   private boolean execOperation(final CoprocessorOperation ctx) throws IOException {
265     if (ctx == null) return false;
266     boolean bypass = false;
267     List<RegionServerEnvironment> envs = coprocessors.get();
268     for (int i = 0; i < envs.size(); i++) {
269       RegionServerEnvironment env = envs.get(i);
270       if (env.getInstance() instanceof RegionServerObserver) {
271         ctx.prepare(env);
272         Thread currentThread = Thread.currentThread();
273         ClassLoader cl = currentThread.getContextClassLoader();
274         try {
275           currentThread.setContextClassLoader(env.getClassLoader());
276           ctx.call((RegionServerObserver)env.getInstance(), ctx);
277         } catch (Throwable e) {
278           handleCoprocessorThrowable(env, e);
279         } finally {
280           currentThread.setContextClassLoader(cl);
281         }
282         bypass |= ctx.shouldBypass();
283         if (ctx.shouldComplete()) {
284           break;
285         }
286       }
287       ctx.postEnvCall(env);
288     }
289     return bypass;
290   }
291 
292   /**
293    * Coprocessor environment extension providing access to region server
294    * related services.
295    */
296   static class RegionServerEnvironment extends CoprocessorHost.Environment
297       implements RegionServerCoprocessorEnvironment {
298 
299     private RegionServerServices regionServerServices;
300 
301     public RegionServerEnvironment(final Class<?> implClass,
302         final Coprocessor impl, final int priority, final int seq,
303         final Configuration conf, final RegionServerServices services) {
304       super(impl, priority, seq, conf);
305       this.regionServerServices = services;
306       for (Object itf : ClassUtils.getAllInterfaces(implClass)) {
307         Class<?> c = (Class<?>) itf;
308         if (SingletonCoprocessorService.class.isAssignableFrom(c)) {
309           this.regionServerServices.registerService(
310             ((SingletonCoprocessorService) impl).getService());
311           break;
312         }
313       }
314     }
315 
316     @Override
317     public RegionServerServices getRegionServerServices() {
318       return regionServerServices;
319     }
320   }
321 
322   /**
323    * Environment priority comparator. Coprocessors are chained in sorted
324    * order.
325    */
326   static class EnvironmentPriorityComparator implements
327       Comparator<CoprocessorEnvironment> {
328     public int compare(final CoprocessorEnvironment env1,
329         final CoprocessorEnvironment env2) {
330       if (env1.getPriority() < env2.getPriority()) {
331         return -1;
332       } else if (env1.getPriority() > env2.getPriority()) {
333         return 1;
334       }
335       if (env1.getLoadSequence() < env2.getLoadSequence()) {
336         return -1;
337       } else if (env1.getLoadSequence() > env2.getLoadSequence()) {
338         return 1;
339       }
340       return 0;
341     }
342   }
343 }