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 
267     boolean bypass = false;
268     for (RegionServerEnvironment env: coprocessors) {
269       if (env.getInstance() instanceof RegionServerObserver) {
270         ctx.prepare(env);
271         Thread currentThread = Thread.currentThread();
272         ClassLoader cl = currentThread.getContextClassLoader();
273         try {
274           currentThread.setContextClassLoader(env.getClassLoader());
275           ctx.call((RegionServerObserver)env.getInstance(), ctx);
276         } catch (Throwable e) {
277           handleCoprocessorThrowable(env, e);
278         } finally {
279           currentThread.setContextClassLoader(cl);
280         }
281         bypass |= ctx.shouldBypass();
282         if (ctx.shouldComplete()) {
283           break;
284         }
285       }
286       ctx.postEnvCall(env);
287     }
288     return bypass;
289   }
290 
291   /**
292    * Coprocessor environment extension providing access to region server
293    * related services.
294    */
295   static class RegionServerEnvironment extends CoprocessorHost.Environment
296       implements RegionServerCoprocessorEnvironment {
297 
298     private RegionServerServices regionServerServices;
299 
300     public RegionServerEnvironment(final Class<?> implClass,
301         final Coprocessor impl, final int priority, final int seq,
302         final Configuration conf, final RegionServerServices services) {
303       super(impl, priority, seq, conf);
304       this.regionServerServices = services;
305       for (Object itf : ClassUtils.getAllInterfaces(implClass)) {
306         Class<?> c = (Class<?>) itf;
307         if (SingletonCoprocessorService.class.isAssignableFrom(c)) {
308           this.regionServerServices.registerService(
309             ((SingletonCoprocessorService) impl).getService());
310           break;
311         }
312       }
313     }
314 
315     @Override
316     public RegionServerServices getRegionServerServices() {
317       return regionServerServices;
318     }
319   }
320 
321   /**
322    * Environment priority comparator. Coprocessors are chained in sorted
323    * order.
324    */
325   static class EnvironmentPriorityComparator implements
326       Comparator<CoprocessorEnvironment> {
327     public int compare(final CoprocessorEnvironment env1,
328         final CoprocessorEnvironment env2) {
329       if (env1.getPriority() < env2.getPriority()) {
330         return -1;
331       } else if (env1.getPriority() > env2.getPriority()) {
332         return 1;
333       }
334       if (env1.getLoadSequence() < env2.getLoadSequence()) {
335         return -1;
336       } else if (env1.getLoadSequence() > env2.getLoadSequence()) {
337         return 1;
338       }
339       return 0;
340     }
341   }
342 }