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