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.executor;
20  
21  import java.io.IOException;
22  import java.util.concurrent.atomic.AtomicLong;
23  
24  import org.apache.commons.logging.Log;
25  import org.apache.commons.logging.LogFactory;
26  import org.apache.hadoop.classification.InterfaceAudience;
27  import org.apache.hadoop.hbase.Server;
28  import org.cloudera.htrace.Sampler;
29  import org.cloudera.htrace.Span;
30  import org.cloudera.htrace.Trace;
31  
32  
33  /**
34   * Abstract base class for all HBase event handlers. Subclasses should
35   * implement the {@link #process()} and {@link #prepare()} methods.  Subclasses
36   * should also do all necessary checks up in their prepare() if possible -- check
37   * table exists, is disabled, etc. -- so they fail fast rather than later when process
38   * is running.  Do it this way because process be invoked directly but event
39   * handlers are also
40   * run in an executor context -- i.e. asynchronously -- and in this case,
41   * exceptions thrown at process time will not be seen by the invoker, not till
42   * we implement a call-back mechanism so the client can pick them up later.
43   * <p>
44   * Event handlers have an {@link EventType}.
45   * {@link EventType} is a list of ALL handler event types.  We need to keep
46   * a full list in one place -- and as enums is a good shorthand for an
47   * implemenations -- because event handlers can be passed to executors when
48   * they are to be run asynchronously. The
49   * hbase executor, see ExecutorService, has a switch for passing
50   * event type to executor.
51   * <p>
52   * Event listeners can be installed and will be called pre- and post- process if
53   * this EventHandler is run in a Thread (its a Runnable so if its {@link #run()}
54   * method gets called).  Implement
55   * {@link EventHandlerListener}s, and registering using
56   * {@link #setListener(EventHandlerListener)}.
57   * @see ExecutorService
58   */
59  @InterfaceAudience.Private
60  public abstract class EventHandler implements Runnable, Comparable<Runnable> {
61    private static final Log LOG = LogFactory.getLog(EventHandler.class);
62  
63    // type of event this object represents
64    protected EventType eventType;
65  
66    protected Server server;
67  
68    // sequence id generator for default FIFO ordering of events
69    protected static final AtomicLong seqids = new AtomicLong(0);
70  
71    // sequence id for this event
72    private final long seqid;
73  
74    // Listener to call pre- and post- processing.  May be null.
75    private EventHandlerListener listener;
76  
77    // Time to wait for events to happen, should be kept short
78    protected int waitingTimeForEvents;
79  
80    private final Span parent;
81  
82    /**
83     * This interface provides pre- and post-process hooks for events.
84     */
85    public interface EventHandlerListener {
86      /**
87       * Called before any event is processed
88       * @param event The event handler whose process method is about to be called.
89       */
90      public void beforeProcess(EventHandler event);
91      /**
92       * Called after any event is processed
93       * @param event The event handler whose process method is about to be called.
94       */
95      public void afterProcess(EventHandler event);
96    }
97  
98    /**
99     * Default base class constructor.
100    */
101   public EventHandler(Server server, EventType eventType) {
102     this.parent = Trace.currentTrace();
103     this.server = server;
104     this.eventType = eventType;
105     seqid = seqids.incrementAndGet();
106     if (server != null) {
107       this.waitingTimeForEvents = server.getConfiguration().
108           getInt("hbase.master.event.waiting.time", 1000);
109     }
110   }
111 
112   /**
113    * Event handlers should do all the necessary checks in this method (rather than
114    * in the constructor, or in process()) so that the caller, which is mostly executed
115    * in the ipc context can fail fast. Process is executed async from the client ipc,
116    * so this method gives a quick chance to do some basic checks.
117    * Should be called after constructing the EventHandler, and before process().
118    * @return the instance of this class
119    * @throws Exception when something goes wrong
120    */
121   public EventHandler prepare() throws Exception {
122     return this;
123   }
124 
125   public void run() {
126     Span chunk = Trace.startSpan(Thread.currentThread().getName(), parent,
127           Sampler.ALWAYS);
128     try {
129       if (getListener() != null) getListener().beforeProcess(this);
130       process();
131       if (getListener() != null) getListener().afterProcess(this);
132     } catch(Throwable t) {
133       LOG.error("Caught throwable while processing event " + eventType, t);
134     } finally {
135       chunk.stop();
136     }
137   }
138 
139   /**
140    * This method is the main processing loop to be implemented by the various
141    * subclasses.
142    * @throws IOException
143    */
144   public abstract void process() throws IOException;
145 
146   /**
147    * Return the event type
148    * @return The event type.
149    */
150   public EventType getEventType() {
151     return this.eventType;
152   }
153 
154   /**
155    * Get the priority level for this handler instance.  This uses natural
156    * ordering so lower numbers are higher priority.
157    * <p>
158    * Lowest priority is Integer.MAX_VALUE.  Highest priority is 0.
159    * <p>
160    * Subclasses should override this method to allow prioritizing handlers.
161    * <p>
162    * Handlers with the same priority are handled in FIFO order.
163    * <p>
164    * @return Integer.MAX_VALUE by default, override to set higher priorities
165    */
166   public int getPriority() {
167     return Integer.MAX_VALUE;
168   }
169 
170   /**
171    * @return This events' sequence id.
172    */
173   public long getSeqid() {
174     return this.seqid;
175   }
176 
177   /**
178    * Default prioritized runnable comparator which implements a FIFO ordering.
179    * <p>
180    * Subclasses should not override this.  Instead, if they want to implement
181    * priority beyond FIFO, they should override {@link #getPriority()}.
182    */
183   @Override
184   public int compareTo(Runnable o) {
185     EventHandler eh = (EventHandler)o;
186     if(getPriority() != eh.getPriority()) {
187       return (getPriority() < eh.getPriority()) ? -1 : 1;
188     }
189     return (this.seqid < eh.seqid) ? -1 : 1;
190   }
191 
192   /**
193    * @return Current listener or null if none set.
194    */
195   public synchronized EventHandlerListener getListener() {
196     return listener;
197   }
198 
199   /**
200    * @param listener Listener to call pre- and post- {@link #process()}.
201    */
202   public synchronized void setListener(EventHandlerListener listener) {
203     this.listener = listener;
204   }
205 
206   @Override
207   public String toString() {
208     return "Event #" + getSeqid() +
209       " of type " + eventType +
210       " (" + getInformativeName() + ")";
211   }
212 
213   /**
214    * Event implementations should override thie class to provide an
215    * informative name about what event they are handling. For example,
216    * event-specific information such as which region or server is
217    * being processed should be included if possible.
218    */
219   public String getInformativeName() {
220     return this.getClass().toString();
221   }
222 }