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   */
79  public class DemuxingIoHandler extends IoHandlerAdapter {
80      
81      private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlerCache =
82          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
83  
84      private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlers =
85          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
86  
87      private final Map<Class<?>, MessageHandler<?>> sentMessageHandlerCache =
88          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
89  
90      private final Map<Class<?>, MessageHandler<?>> sentMessageHandlers =
91          new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
92  
93      private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlerCache =
94          new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
95  
96      private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlers =
97          new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
98  
99      /**
100      * Creates a new instance with no registered {@link MessageHandler}s.
101      */
102     public DemuxingIoHandler() {
103         // Do nothing
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      * <b>Warning !</b> If you are to overload this method, be aware that you 
224      * _must_ call the messageHandler in your own method, otherwise it won't 
225      * be called.
226      */
227     @Override
228     public void messageReceived(IoSession session, Object message)
229             throws Exception {
230         MessageHandler<Object> handler = findReceivedMessageHandler(message.getClass());
231         if (handler != null) {
232             handler.handleMessage(session, message);
233         } else {
234             throw new UnknownMessageTypeException(
235                     "No message handler found for message type: " +
236                     message.getClass().getSimpleName());
237         }
238     }
239 
240     /**
241      * Invoked when a message written by IoSession.write(Object) is sent out.
242      * 
243      * <b>Warning !</b> If you are to overload this method, be aware that you 
244      * _must_ call the messageHandler in your own method, otherwise it won't 
245      * be called.
246      */
247     @Override
248     public void messageSent(IoSession session, Object message) throws Exception {
249         MessageHandler<Object> handler = findSentMessageHandler(message.getClass());
250         if (handler != null) {
251             handler.handleMessage(session, message);
252         } else {
253             throw new UnknownMessageTypeException(
254                     "No handler found for message type: " +
255                     message.getClass().getSimpleName());
256         }
257     }
258 
259     /**
260      * Invoked when any exception is thrown by user IoHandler implementation 
261      * or by MINA. If cause is an instance of IOException, MINA will close the 
262      * connection automatically.
263      *
264      * <b>Warning !</b> If you are to overload this method, be aware that you 
265      * _must_ call the messageHandler in your own method, otherwise it won't 
266      * be called.
267      */
268     @Override
269     public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
270         ExceptionHandler<Throwable> handler = findExceptionHandler(cause.getClass());
271         if (handler != null) {
272             handler.exceptionCaught(session, cause);
273         } else {
274             throw new UnknownMessageTypeException(
275                     "No handler found for exception type: " +
276                     cause.getClass().getSimpleName());
277         }
278     }
279 
280     protected MessageHandler<Object> findReceivedMessageHandler(Class<?> type) {
281         return findReceivedMessageHandler(type, null);
282     }
283 
284     protected MessageHandler<Object> findSentMessageHandler(Class<?> type) {
285         return findSentMessageHandler(type, null);
286     }
287 
288     protected ExceptionHandler<Throwable> findExceptionHandler(Class<? extends Throwable> type) {
289         return findExceptionHandler(type, null);
290     }
291 
292     @SuppressWarnings("unchecked")
293     private MessageHandler<Object> findReceivedMessageHandler(
294             Class type, Set<Class> triedClasses) {
295         
296         return (MessageHandler<Object>) findHandler(
297                 receivedMessageHandlers, receivedMessageHandlerCache, type, triedClasses);
298     }
299 
300     @SuppressWarnings("unchecked")
301     private MessageHandler<Object> findSentMessageHandler(
302             Class type, Set<Class> triedClasses) {
303         
304         return (MessageHandler<Object>) findHandler(
305                 sentMessageHandlers, sentMessageHandlerCache, type, triedClasses);
306     }
307 
308     @SuppressWarnings("unchecked")
309     private ExceptionHandler<Throwable> findExceptionHandler(
310             Class type, Set<Class> triedClasses) {
311         
312         return (ExceptionHandler<Throwable>) findHandler(
313                 exceptionHandlers, exceptionHandlerCache, type, triedClasses);
314     }
315 
316     @SuppressWarnings("unchecked")
317     private Object findHandler(
318             Map handlers, Map handlerCache,
319             Class type, Set<Class> triedClasses) {
320 
321         Object handler = null;
322 
323         if (triedClasses != null && triedClasses.contains(type)) {
324             return null;
325         }
326 
327         /*
328          * Try the cache first.
329          */
330         handler = handlerCache.get(type);
331         if (handler != null) {
332             return handler;
333         }
334 
335         /*
336          * Try the registered handlers for an immediate match.
337          */
338         handler = handlers.get(type);
339 
340         if (handler == null) {
341             /*
342              * No immediate match could be found. Search the type's interfaces.
343              */
344 
345             if (triedClasses == null) {
346                 triedClasses = new IdentityHashSet<Class>();
347             }
348             triedClasses.add(type);
349 
350             Class[] interfaces = type.getInterfaces();
351             for (Class element : interfaces) {
352                 handler = findHandler(handlers, handlerCache, element, triedClasses);
353                 if (handler != null) {
354                     break;
355                 }
356             }
357         }
358 
359         if (handler == null) {
360             /*
361              * No match in type's interfaces could be found. Search the
362              * superclass.
363              */
364             Class superclass = type.getSuperclass();
365             if (superclass != null) {
366                 handler = findHandler(handlers, handlerCache, superclass, null);
367             }
368         }
369 
370         /*
371          * Make sure the handler is added to the cache. By updating the cache
372          * here all the types (superclasses and interfaces) in the path which
373          * led to a match will be cached along with the immediate message type.
374          */
375         if (handler != null) {
376             handlerCache.put(type, handler);
377         }
378 
379         return handler;
380     }
381 }