View Javadoc

1   /*
2    Copyright 2008 Ramon Servadei
3   
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7   
8    http://www.apache.org/licenses/LICENSE-2.0
9   
10   Unless required by applicable law or agreed to in writing, software
11   distributed under the License is distributed on an "AS IS" BASIS,
12   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   See the License for the specific language governing permissions and
14   limitations under the License.
15   */
16  package fulmine.event.listener;
17  
18  import static fulmine.util.Utils.logException;
19  
20  import java.lang.reflect.ParameterizedType;
21  import java.lang.reflect.TypeVariable;
22  import java.util.Arrays;
23  import java.util.Map;
24  
25  import fulmine.event.EventProcessor;
26  import fulmine.event.IEvent;
27  import fulmine.event.IEventManager;
28  import fulmine.event.IEventSource;
29  import fulmine.util.collection.CollectionFactory;
30  import fulmine.util.collection.CollectionUtils;
31  import fulmine.util.log.AsyncLog;
32  import fulmine.util.reference.AutoCreatingStore;
33  import fulmine.util.reference.IAutoCreatingStore;
34  import fulmine.util.reference.IObjectBuilder;
35  
36  /**
37   * Base class for an object that handles a specific type of {@link IEvent}. This
38   * is generally used by an {@link IEventListener} to delegate processing of a
39   * specific event. Handlers are not designed to maintain state so do not
40   * implement the life-cycle interface.
41   * <p>
42   * This class and sub-classes are thread safe if the event generic parameter is
43   * a system event (system events are bound to a single {@link EventProcessor}).
44   * 
45   * @author Ramon Servadei
46   * 
47   * @param <T>
48   *            the {@link IEvent} type for this handler.
49   * @see MultiEventListener
50   */
51  public abstract class AbstractEventHandler<T extends IEvent> implements
52      IEventListener
53  {
54      private final static AsyncLog LOG =
55          new AsyncLog(AbstractEventHandler.class);
56  
57      /** The event filter for this instance */
58      final Class<? extends IEvent>[] eventFilter;
59  
60      /**
61       * Builds single element arrays with the specified class
62       * 
63       * @author Ramon Servadei
64       */
65      private final static class EventFilterBuilder implements
66          IObjectBuilder<Class<? extends IEvent>, Class<? extends IEvent>[]>
67      {
68          @SuppressWarnings("unchecked")
69          public Class<? extends IEvent>[] create(Class<? extends IEvent> key)
70          {
71              Class<? extends IEvent>[] filter = new Class[1];
72              filter[0] = key;
73              return filter;
74          }
75      }
76  
77      /** The store of event filters */
78      private final static IAutoCreatingStore<Class<? extends IEvent>, Class<? extends IEvent>[]> eventFilterStore =
79          new AutoCreatingStore<Class<? extends IEvent>, Class<? extends IEvent>[]>(
80              new EventFilterBuilder());
81  
82      @SuppressWarnings("unchecked")
83      public AbstractEventHandler()
84      {
85          super();
86          // uses reflection to get the type of event
87          final ParameterizedType ptype = getParameterizedType(this);
88          if (ptype.getActualTypeArguments()[0] instanceof TypeVariable)
89          {
90              throw new IllegalArgumentException("Must be a parameterised class");
91          }
92          this.eventFilter =
93              eventFilterStore.get((Class<? extends IEvent>) ptype.getActualTypeArguments()[0]);
94      }
95  
96      /**
97       * Given a group of {@link AbstractEventHandler} instances, this method
98       * returns a map of the handlers indexed by the {@link IEvent} class each
99       * one handles. The event for each handler is determined from the generic
100      * type argument of the handler implementation.
101      * 
102      * @param handlers
103      *            the handlers to build an {@link IEvent} indexed map of
104      * @return a {@link Map} of the handlers, indexed by the {@link IEvent} each
105      *         one handles
106      */
107     @SuppressWarnings("unchecked")
108     public static Map<Class<? extends IEvent>, IEventListener> getEventHandlerMappings(
109         AbstractEventHandler<? extends IEvent>... handlers)
110     {
111         Map<Class<? extends IEvent>, IEventListener> listeners =
112             CollectionFactory.newMap();
113         for (AbstractEventHandler<? extends IEvent> handler : handlers)
114         {
115             try
116             {
117                 // use reflection to get the generic event type to map the
118                 // handler
119                 ParameterizedType ptype = getParameterizedType(handler);
120                 if (ptype.getActualTypeArguments()[0] instanceof TypeVariable)
121                 {
122                     throw new IllegalArgumentException(
123                         "Must be a parameterised class");
124                 }
125                 listeners.put(
126                     (Class<? extends IEvent>) ptype.getActualTypeArguments()[0],
127                     handler);
128             }
129             catch (Exception e)
130             {
131                 logException(null, handler, e);
132             }
133         }
134         if (LOG.isTraceEnabled())
135         {
136             LOG.trace("Handler map for " + Arrays.toString(handlers) + " is "
137                 + CollectionUtils.toFormattedString(listeners));
138         }
139         return listeners;
140     }
141 
142     /**
143      * Walks up the class hierarchy until a parameterised class type is found.
144      * 
145      * @param handler
146      *            the handler to process
147      * @return the parameterised type
148      * @throws ClassCastException
149      *             if the handler does not have a generic type in its hierarchy
150      */
151     @SuppressWarnings("unchecked")
152     private static ParameterizedType getParameterizedType(
153         AbstractEventHandler<? extends IEvent> handler)
154     {
155         Class<? extends AbstractEventHandler> clazz = handler.getClass();
156         while (!(clazz.getGenericSuperclass() instanceof ParameterizedType))
157         {
158             clazz =
159                 (Class<? extends AbstractEventHandler>) clazz.getSuperclass();
160         }
161         return (ParameterizedType) clazz.getGenericSuperclass();
162     }
163 
164     @SuppressWarnings("unchecked")
165     public final void update(IEvent event)
166     {
167         if (getLog().isDebugEnabled())
168         {
169             getLog().debug("update event=" + event);
170         }
171         handle((T) event);
172     }
173 
174     public void addedAsListenerFor(IEventSource source)
175     {
176     }
177 
178     public void removedAsListenerFrom(IEventSource source)
179     {
180     }
181 
182     public final Class<? extends IEvent>[] getEventTypeFilter()
183     {
184         return this.eventFilter;
185     }
186 
187     /**
188      * Handle the event. This will be executed using the context's
189      * {@link EventProcessor} instance that is bound to the event's source.
190      * 
191      * @see IEventManager#queueEvent(IEvent)
192      * @param event
193      *            the event
194      */
195     public abstract void handle(T event);
196 
197     /**
198      * Get the log to use
199      * 
200      * @return the log to use
201      */
202     protected abstract AsyncLog getLog();
203 
204 }