View Javadoc

1   
2   /*
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.regionserver.wal;
22  
23  import java.io.IOException;
24  import java.util.List;
25  
26  import org.apache.hadoop.fs.Path;
27  import org.apache.hadoop.hbase.Coprocessor;
28  import org.apache.hadoop.hbase.HRegionInfo;
29  import org.apache.hadoop.hbase.coprocessor.*;
30  import org.apache.hadoop.hbase.classification.InterfaceAudience;
31  import org.apache.hadoop.conf.Configuration;
32  import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
33  import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
34  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
35  import org.apache.hadoop.hbase.coprocessor.WALCoprocessorEnvironment;
36  import org.apache.hadoop.hbase.coprocessor.WALObserver;
37  import org.apache.hadoop.hbase.metrics.MetricRegistry;
38  import org.apache.hadoop.hbase.wal.WAL;
39  import org.apache.hadoop.hbase.wal.WALKey;
40  
41  /**
42   * Implements the coprocessor environment and runtime support for coprocessors
43   * loaded within a {@link WAL}.
44   */
45  @InterfaceAudience.Private
46  public class WALCoprocessorHost
47      extends CoprocessorHost<WALCoprocessorHost.WALEnvironment> {
48  
49    /**
50     * Encapsulation of the environment of each coprocessor
51     */
52    static class WALEnvironment extends CoprocessorHost.Environment
53      implements WALCoprocessorEnvironment {
54  
55      private final WAL wal;
56  
57      final boolean useLegacyPre;
58      final boolean useLegacyPost;
59      private final MetricRegistry metricRegistry;
60  
61      @Override
62      public WAL getWAL() {
63        return wal;
64      }
65  
66      /**
67       * Constructor
68       * @param implClass - not used
69       * @param impl the coprocessor instance
70       * @param priority chaining priority
71       * @param seq load sequence
72       * @param conf configuration
73       * @param wal WAL
74       */
75      public WALEnvironment(Class<?> implClass, final Coprocessor impl,
76          final int priority, final int seq, final Configuration conf,
77          final WAL wal) {
78        super(impl, priority, seq, conf);
79        this.wal = wal;
80        // Pick which version of the API we'll call.
81        // This way we avoid calling the new version on older WALObservers so
82        // we can maintain binary compatibility.
83        // See notes in javadoc for WALObserver
84        useLegacyPre = useLegacyMethod(impl.getClass(), "preWALWrite", ObserverContext.class,
85            HRegionInfo.class, WALKey.class, WALEdit.class);
86        useLegacyPost = useLegacyMethod(impl.getClass(), "postWALWrite", ObserverContext.class,
87            HRegionInfo.class, WALKey.class, WALEdit.class);
88        this.metricRegistry = MetricsCoprocessor.createRegistryForWALCoprocessor(implClass.getName());
89      }
90  
91      @Override
92      public MetricRegistry getMetricRegistryForRegionServer() {
93        return metricRegistry;
94      }
95  
96      @Override
97      protected void shutdown() {
98        super.shutdown();
99        MetricsCoprocessor.removeRegistry(this.metricRegistry);
100     }
101   }
102 
103   private final WAL wal;
104 
105   /**
106    * Constructor
107    * @param log the write ahead log
108    * @param conf the configuration
109    */
110   public WALCoprocessorHost(final WAL log, final Configuration conf) {
111     // We don't want to require an Abortable passed down through (FS)HLog, so
112     // this means that a failure to load of a WAL coprocessor won't abort the
113     // server. This isn't ideal, and means that security components that
114     // utilize a WALObserver will have to check the observer initialization
115     // state manually. However, WALObservers will eventually go away so it
116     // should be an acceptable state of affairs.
117     super(null);
118     this.wal = log;
119     // load system default cp's from configuration.
120     loadSystemCoprocessors(conf, WAL_COPROCESSOR_CONF_KEY);
121   }
122 
123   @Override
124   public WALEnvironment createEnvironment(final Class<?> implClass,
125       final Coprocessor instance, final int priority, final int seq,
126       final Configuration conf) {
127     return new WALEnvironment(implClass, instance, priority, seq, conf,
128         this.wal);
129   }
130 
131   /**
132    * @param info
133    * @param logKey
134    * @param logEdit
135    * @return true if default behavior should be bypassed, false otherwise
136    * @throws IOException
137    */
138   public boolean preWALWrite(final HRegionInfo info, final WALKey logKey, final WALEdit logEdit)
139       throws IOException {
140     boolean bypass = false;
141     if (this.coprocessors == null || this.coprocessors.isEmpty()) return bypass;
142     ObserverContext<WALCoprocessorEnvironment> ctx = null;
143     List<WALEnvironment> envs = coprocessors.get();
144     for (int i = 0; i < envs.size(); i++) {
145       WALEnvironment env = envs.get(i);
146       if (env.getInstance() instanceof WALObserver) {
147         final WALObserver observer = (WALObserver)env.getInstance();
148         ctx = ObserverContext.createAndPrepare(env, ctx);
149         Thread currentThread = Thread.currentThread();
150         ClassLoader cl = currentThread.getContextClassLoader();
151         try {
152           currentThread.setContextClassLoader(env.getClassLoader());
153           if (env.useLegacyPre) {
154             if (logKey instanceof HLogKey) {
155               observer.preWALWrite(ctx, info, (HLogKey)logKey, logEdit);
156             } else {
157               legacyWarning(observer.getClass(),
158                   "There are wal keys present that are not HLogKey.");
159             }
160           } else {
161             observer.preWALWrite(ctx, info, logKey, logEdit);
162           }
163         } catch (Throwable e) {
164           handleCoprocessorThrowable(env, e);
165         } finally {
166           currentThread.setContextClassLoader(cl);
167         }
168         bypass |= ctx.shouldBypass();
169         if (ctx.shouldComplete()) {
170           break;
171         }
172       }
173     }
174     return bypass;
175   }
176 
177   /**
178    * @param info
179    * @param logKey
180    * @param logEdit
181    * @throws IOException
182    */
183   public void postWALWrite(final HRegionInfo info, final WALKey logKey, final WALEdit logEdit)
184       throws IOException {
185     if (this.coprocessors == null || this.coprocessors.isEmpty()) return;
186     ObserverContext<WALCoprocessorEnvironment> ctx = null;
187     List<WALEnvironment> envs = coprocessors.get();
188     for (int i = 0; i < envs.size(); i++) {
189       WALEnvironment env = envs.get(i);
190       if (env.getInstance() instanceof WALObserver) {
191         final WALObserver observer = (WALObserver)env.getInstance();
192         ctx = ObserverContext.createAndPrepare(env, ctx);
193         Thread currentThread = Thread.currentThread();
194         ClassLoader cl = currentThread.getContextClassLoader();
195         try {
196           currentThread.setContextClassLoader(env.getClassLoader());
197           if (env.useLegacyPost) {
198             if (logKey instanceof HLogKey) {
199               observer.postWALWrite(ctx, info, (HLogKey)logKey, logEdit);
200             } else {
201               legacyWarning(observer.getClass(),
202                   "There are wal keys present that are not HLogKey.");
203             }
204           } else {
205             observer.postWALWrite(ctx, info, logKey, logEdit);
206           }
207         } catch (Throwable e) {
208           handleCoprocessorThrowable(env, e);
209         } finally {
210           currentThread.setContextClassLoader(cl);
211         }
212         if (ctx.shouldComplete()) {
213           break;
214         }
215       }
216     }
217   }
218 
219   /**
220    * Called before rolling the current WAL
221    * @param oldPath the path of the current wal that we are replacing
222    * @param newPath the path of the wal we are going to create
223    */
224   public void preWALRoll(Path oldPath, Path newPath) throws IOException {
225     if (this.coprocessors == null || this.coprocessors.isEmpty()) return;
226     ObserverContext<WALCoprocessorEnvironment> ctx = null;
227     List<WALEnvironment> envs = coprocessors.get();
228     for (int i = 0; i < envs.size(); i++) {
229       WALEnvironment env = envs.get(i);
230       if (env.getInstance() instanceof WALObserver) {
231         final WALObserver observer = (WALObserver)env.getInstance();
232         ctx = ObserverContext.createAndPrepare(env, ctx);
233         Thread currentThread = Thread.currentThread();
234         ClassLoader cl = currentThread.getContextClassLoader();
235         try {
236           currentThread.setContextClassLoader(env.getClassLoader());
237           observer.preWALRoll(ctx, oldPath, newPath);
238         } catch (Throwable e) {
239           handleCoprocessorThrowable(env, e);
240         } finally {
241           currentThread.setContextClassLoader(cl);
242         }
243         if (ctx.shouldComplete()) {
244           break;
245         }
246       }
247     }
248   }
249 
250   /**
251    * Called after rolling the current WAL
252    * @param oldPath the path of the wal that we replaced
253    * @param newPath the path of the wal we have created and now is the current
254    */
255   public void postWALRoll(Path oldPath, Path newPath) throws IOException {
256     if (this.coprocessors == null || this.coprocessors.isEmpty()) return;
257     ObserverContext<WALCoprocessorEnvironment> ctx = null;
258     List<WALEnvironment> envs = coprocessors.get();
259     for (int i = 0; i < envs.size(); i++) {
260       WALEnvironment env = envs.get(i);
261       if (env.getInstance() instanceof WALObserver) {
262         final WALObserver observer = (WALObserver)env.getInstance();
263         ctx = ObserverContext.createAndPrepare(env, ctx);
264         Thread currentThread = Thread.currentThread();
265         ClassLoader cl = currentThread.getContextClassLoader();
266         try {
267           currentThread.setContextClassLoader(env.getClassLoader());
268           observer.postWALRoll(ctx, oldPath, newPath);
269         } catch (Throwable e) {
270           handleCoprocessorThrowable(env, e);
271         } finally {
272           currentThread.setContextClassLoader(cl);
273         }
274         if (ctx.shouldComplete()) {
275           break;
276         }
277       }
278     }
279   }
280 }