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