View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.handler.demux;
21  
22  import java.util.Collections;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.concurrent.ConcurrentHashMap;
26  
27  import org.apache.mina.core.service.IoHandler;
28  import org.apache.mina.core.service.IoHandlerAdapter;
29  import org.apache.mina.core.session.IoSession;
30  import org.apache.mina.core.session.UnknownMessageTypeException;
31  import org.apache.mina.util.IdentityHashSet;
32  
33  /**
34   * A {@link IoHandler} that demuxes <code>messageReceived</code> events
35   * to the appropriate {@link MessageHandler}.
36   * <p>
37   * You can freely register and deregister {@link MessageHandler}s using
38   * {@link #addReceivedMessageHandler(Class, MessageHandler)} and
39   * {@link #removeReceivedMessageHandler(Class)}.
40   * </p>
41   * <p>
42   * When <code>message</code> is received through a call to
43   * {@link #messageReceived(IoSession, Object)} the class of the
44   * <code>message</code> object will be used to find a {@link MessageHandler} for
45   * that particular message type. If no {@link MessageHandler} instance can be
46   * found for the immediate class (i.e. <code>message.getClass()</code>) the
47   * interfaces implemented by the immediate class will be searched in depth-first
48   * order. If no match can be found for any of the interfaces the search will be
49   * repeated recursively for the superclass of the immediate class
50   * (i.e. <code>message.getClass().getSuperclass()</code>).
51   * </p>
52   * <p>
53   * Consider the following type hierarchy (<code>Cx</code> are classes while
54   * <code>Ix</code> are interfaces):
55   * <pre>
56   *     C3 - I7 - I9
57   *      |    |   /\
58   *      |   I8  I3 I4
59   *      |
60   *     C2 - I5 - I6
61   *      |
62   *     C1 - I1 - I2 - I4
63   *      |         |
64   *      |        I3
65   *    Object
66   * </pre>
67   * When <code>message</code> is of type <code>C3</code> this hierarchy will be
68   * searched in the following order:
69   * <code>C3, I7, I8, I9, I3, I4, C2, I5, I6, C1, I1, I2, I3, I4, Object</code>.
70   * </p>
71   * <p>
72   * For efficiency searches will be cached. Calls to
73   * {@link #addReceivedMessageHandler(Class, MessageHandler)} and
74   * {@link #removeReceivedMessageHandler(Class)} clear this cache.
75   * </p>
76   *
77   * @author The Apache MINA Project (dev@mina.apache.org)
78   * @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (jeu, 26 jun 2008) $
79   */
80  public class DemuxingIoHandler extends IoHandlerAdapter {
81      
82      private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlerCache =
83          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
84  
85      private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlers =
86          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
87  
88      private final Map<Class<?>, MessageHandler<?>> sentMessageHandlerCache =
89          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
90  
91      private final Map<Class<?>, MessageHandler<?>> sentMessageHandlers =
92          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
93  
94      private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlerCache =
95          new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
96  
97      private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlers =
98          new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
99  
100     /**
101      * Creates a new instance with no registered {@link MessageHandler}s.
102      */
103     public DemuxingIoHandler() {
104     }
105 
106     /**
107      * Registers a {@link MessageHandler} that handles the received messages of
108      * the specified <code>type</code>.
109      *
110      * @return the old handler if there is already a registered handler for
111      *         the specified <tt>type</tt>.  <tt>null</tt> otherwise.
112      */
113     @SuppressWarnings("unchecked")
114     public <E> MessageHandler<? super E> addReceivedMessageHandler(Class<E> type,
115             MessageHandler<? super E> handler) {
116         receivedMessageHandlerCache.clear();
117         return (MessageHandler<? super E>) receivedMessageHandlers.put(type, handler);
118     }
119 
120     /**
121      * Deregisters a {@link MessageHandler} that handles the received messages
122      * of the specified <code>type</code>.
123      *
124      * @return the removed handler if successfully removed.  <tt>null</tt> otherwise.
125      */
126     @SuppressWarnings("unchecked")
127     public <E> MessageHandler<? super E> removeReceivedMessageHandler(Class<E> type) {
128         receivedMessageHandlerCache.clear();
129         return (MessageHandler<? super E>) receivedMessageHandlers.remove(type);
130     }
131 
132     /**
133      * Registers a {@link MessageHandler} that handles the sent messages of the
134      * specified <code>type</code>.
135      *
136      * @return the old handler if there is already a registered handler for
137      *         the specified <tt>type</tt>.  <tt>null</tt> otherwise.
138      */
139     @SuppressWarnings("unchecked")
140     public <E> MessageHandler<? super E> addSentMessageHandler(Class<E> type,
141             MessageHandler<? super E> handler) {
142         sentMessageHandlerCache.clear();
143         return (MessageHandler<? super E>) sentMessageHandlers.put(type, handler);
144     }
145 
146     /**
147      * Deregisters a {@link MessageHandler} that handles the sent messages of
148      * the specified <code>type</code>.
149      *
150      * @return the removed handler if successfully removed.  <tt>null</tt> otherwise.
151      */
152     @SuppressWarnings("unchecked")
153     public <E> MessageHandler<? super E> removeSentMessageHandler(Class<E> type) {
154         sentMessageHandlerCache.clear();
155         return (MessageHandler<? super E>) sentMessageHandlers.remove(type);
156     }
157     
158     /**
159      * Registers a {@link MessageHandler} that receives the messages of
160      * the specified <code>type</code>.
161      *
162      * @return the old handler if there is already a registered handler for
163      *         the specified <tt>type</tt>.  <tt>null</tt> otherwise.
164      */
165     @SuppressWarnings("unchecked")
166     public <E extends Throwable> 
167     ExceptionHandler<? super E> addExceptionHandler(
168             Class<E> type, ExceptionHandler<? super E> handler) {
169         exceptionHandlerCache.clear();
170         return (ExceptionHandler<? super E>) exceptionHandlers.put(type, handler);
171     }
172 
173     /**
174      * Deregisters a {@link MessageHandler} that receives the messages of
175      * the specified <code>type</code>.
176      *
177      * @return the removed handler if successfully removed.  <tt>null</tt> otherwise.
178      */
179     @SuppressWarnings("unchecked")
180     public <E extends Throwable> ExceptionHandler<? super E>
181     removeExceptionHandler(Class<E> type) {
182         exceptionHandlerCache.clear();
183         return (ExceptionHandler<? super E>) exceptionHandlers.remove(type);
184     }
185 
186     /**
187      * Returns the {@link MessageHandler} which is registered to process
188      * the specified <code>type</code>.
189      */
190     @SuppressWarnings("unchecked")
191     public <E> MessageHandler<? super E> getMessageHandler(Class<E> type) {
192         return (MessageHandler<? super E>) receivedMessageHandlers.get(type);
193     }
194 
195     /**
196      * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
197      * pairs registered to this handler for received messages.
198      */
199     public Map<Class<?>, MessageHandler<?>> getReceivedMessageHandlerMap() {
200         return Collections.unmodifiableMap(receivedMessageHandlers);
201     }
202 
203     /**
204      * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
205      * pairs registered to this handler for sent messages.
206      */
207     public Map<Class<?>, MessageHandler<?>> getSentMessageHandlerMap() {
208         return Collections.unmodifiableMap(sentMessageHandlers);
209     }
210 
211     /**
212      * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
213      * pairs registered to this handler.
214      */
215     public Map<Class<?>, ExceptionHandler<?>> getExceptionHandlerMap() {
216         return Collections.unmodifiableMap(exceptionHandlers);
217     }
218 
219     /**
220      * Forwards the received events into the appropriate {@link MessageHandler}
221      * which is registered by {@link #addReceivedMessageHandler(Class, MessageHandler)}.
222      */
223     @Override
224     public final void messageReceived(IoSession session, Object message)
225             throws Exception {
226         MessageHandler<Object> handler = findReceivedMessageHandler(message.getClass());
227         if (handler != null) {
228             handler.handleMessage(session, message);
229         } else {
230             throw new UnknownMessageTypeException(
231                     "No message handler found for message type: " +
232                     message.getClass().getSimpleName());
233         }
234     }
235 
236     @Override
237     public final void messageSent(IoSession session, Object message) throws Exception {
238         MessageHandler<Object> handler = findSentMessageHandler(message.getClass());
239         if (handler != null) {
240             handler.handleMessage(session, message);
241         } else {
242             throw new UnknownMessageTypeException(
243                     "No handler found for message type: " +
244                     message.getClass().getSimpleName());
245         }
246     }
247 
248     @Override
249     public final void exceptionCaught(IoSession session, Throwable cause) throws Exception {
250         ExceptionHandler<Throwable> handler = findExceptionHandler(cause.getClass());
251         if (handler != null) {
252             handler.exceptionCaught(session, cause);
253         } else {
254             throw new UnknownMessageTypeException(
255                     "No handler found for exception type: " +
256                     cause.getClass().getSimpleName());
257         }
258     }
259 
260     protected MessageHandler<Object> findReceivedMessageHandler(Class<?> type) {
261         return findReceivedMessageHandler(type, null);
262     }
263 
264     protected MessageHandler<Object> findSentMessageHandler(Class<?> type) {
265         return findSentMessageHandler(type, null);
266     }
267 
268     protected ExceptionHandler<Throwable> findExceptionHandler(Class<? extends Throwable> type) {
269         return findExceptionHandler(type, null);
270     }
271 
272     @SuppressWarnings("unchecked")
273     private MessageHandler<Object> findReceivedMessageHandler(
274             Class type, Set<Class> triedClasses) {
275         
276         return (MessageHandler<Object>) findHandler(
277                 receivedMessageHandlers, receivedMessageHandlerCache, type, triedClasses);
278     }
279 
280     @SuppressWarnings("unchecked")
281     private MessageHandler<Object> findSentMessageHandler(
282             Class type, Set<Class> triedClasses) {
283         
284         return (MessageHandler<Object>) findHandler(
285                 sentMessageHandlers, sentMessageHandlerCache, type, triedClasses);
286     }
287 
288     @SuppressWarnings("unchecked")
289     private ExceptionHandler<Throwable> findExceptionHandler(
290             Class type, Set<Class> triedClasses) {
291         
292         return (ExceptionHandler<Throwable>) findHandler(
293                 exceptionHandlers, exceptionHandlerCache, type, triedClasses);
294     }
295 
296     @SuppressWarnings("unchecked")
297     private Object findHandler(
298             Map handlers, Map handlerCache,
299             Class type, Set<Class> triedClasses) {
300 
301         Object handler = null;
302 
303         if (triedClasses != null && triedClasses.contains(type)) {
304             return null;
305         }
306 
307         /*
308          * Try the cache first.
309          */
310         handler = handlerCache.get(type);
311         if (handler != null) {
312             return handler;
313         }
314 
315         /*
316          * Try the registered handlers for an immediate match.
317          */
318         handler = handlers.get(type);
319 
320         if (handler == null) {
321             /*
322              * No immediate match could be found. Search the type's interfaces.
323              */
324 
325             if (triedClasses == null) {
326                 triedClasses = new IdentityHashSet<Class>();
327             }
328             triedClasses.add(type);
329 
330             Class[] interfaces = type.getInterfaces();
331             for (Class element : interfaces) {
332                 handler = findHandler(handlers, handlerCache, element, triedClasses);
333                 if (handler != null) {
334                     break;
335                 }
336             }
337         }
338 
339         if (handler == null) {
340             /*
341              * No match in type's interfaces could be found. Search the
342              * superclass.
343              */
344             Class superclass = type.getSuperclass();
345             if (superclass != null) {
346                 handler = findHandler(handlers, handlerCache, superclass, null);
347             }
348         }
349 
350         /*
351          * Make sure the handler is added to the cache. By updating the cache
352          * here all the types (superclasses and interfaces) in the path which
353          * led to a match will be cached along with the immediate message type.
354          */
355         if (handler != null) {
356             handlerCache.put(type, handler);
357         }
358 
359         return handler;
360     }
361 }