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.common.IoHandler;
28  import org.apache.mina.common.IoHandlerAdapter;
29  import org.apache.mina.common.IoSession;
30  import org.apache.mina.util.IdentityHashSet;
31  
32  /**
33   * A {@link IoHandler} that demuxes <code>messageReceived</code> events
34   * to the appropriate {@link MessageHandler}.
35   * <p>
36   * You can freely register and deregister {@link MessageHandler}s using
37   * {@link #addMessageHandler(Class, MessageHandler)} and
38   * {@link #removeMessageHandler(Class)}.
39   * </p>
40   * <p>
41   * When <code>message</code> is received through a call to 
42   * {@link #messageReceived(IoSession, Object)} the class of the 
43   * <code>message</code> object will be used to find a {@link MessageHandler} for 
44   * that particular message type. If no {@link MessageHandler} instance can be 
45   * found for the immediate class (i.e. <code>message.getClass()</code>) the 
46   * interfaces implemented by the immediate class will be searched in depth-first 
47   * order. If no match can be found for any of the interfaces the search will be 
48   * repeated recursively for the superclass of the immediate class 
49   * (i.e. <code>message.getClass().getSuperclass()</code>).
50   * </p>
51   * <p>
52   * Consider the following type hierarchy (<code>Cx</code> are classes while 
53   * <code>Ix</code> are interfaces):
54   * <pre>
55   *     C3 - I7 - I9
56   *      |    |   /\
57   *      |   I8  I3 I4
58   *      |
59   *     C2 - I5 - I6
60   *      |
61   *     C1 - I1 - I2 - I4
62   *      |         |
63   *      |        I3
64   *    Object          
65   * </pre>
66   * When <code>message</code> is of type <code>C3</code> this hierarchy will be 
67   * searched in the following order:
68   * <code>C3, I7, I8, I9, I3, I4, C2, I5, I6, C1, I1, I2, I3, I4, Object</code>.
69   * </p>
70   * <p>
71   * For efficiency searches will be cached. Calls to 
72   * {@link #addMessageHandler(Class, MessageHandler)} and
73   * {@link #removeMessageHandler(Class)} clear this cache.
74   * </p>
75   * 
76   * @author The Apache Directory Project (mina-dev@directory.apache.org)
77   * @version $Rev: 555855 $, $Date: 2007-07-13 12:19:00 +0900 (Fri, 13 Jul 2007) $
78   */
79  public class DemuxingIoHandler extends IoHandlerAdapter {
80      private final Map<Class, MessageHandler> findHandlerCache = new ConcurrentHashMap<Class, MessageHandler>();
81  
82      private final Map<Class, MessageHandler> type2handler = new ConcurrentHashMap<Class, MessageHandler>();
83  
84      /**
85       * Creates a new instance with no registered {@link MessageHandler}s.
86       */
87      public DemuxingIoHandler() {
88      }
89  
90      /**
91       * Registers a {@link MessageHandler} that receives the messages of
92       * the specified <code>type</code>.
93       * 
94       * @return the old handler if there is already a registered handler for
95       *         the specified <tt>type</tt>.  <tt>null</tt> otherwise.
96       */
97      @SuppressWarnings("unchecked")
98      public <E> MessageHandler<? super E> addMessageHandler(Class<E> type,
99              MessageHandler<? super E> handler) {
100         findHandlerCache.clear();
101         return type2handler.put(type, handler);
102     }
103 
104     /**
105      * Deregisters a {@link MessageHandler} that receives the messages of
106      * the specified <code>type</code>.
107      * 
108      * @return the removed handler if successfully removed.  <tt>null</tt> otherwise.
109      */
110     @SuppressWarnings("unchecked")
111     public <E> MessageHandler<? super E> removeMessageHandler(Class<E> type) {
112         findHandlerCache.clear();
113         return type2handler.remove(type);
114     }
115 
116     /**
117      * Returns the {@link MessageHandler} which is registered to process
118      * the specified <code>type</code>. 
119      */
120     @SuppressWarnings("unchecked")
121     public <E> MessageHandler<? super E> getMessageHandler(Class<E> type) {
122         return type2handler.get(type);
123     }
124 
125     /**
126      * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
127      * pairs registered to this handler.
128      */
129     public Map<Class, MessageHandler> getMessageHandlerMap() {
130         return Collections.unmodifiableMap(type2handler);
131     }
132 
133     /**
134      * Forwards the received events into the appropriate {@link MessageHandler}
135      * which is registered by {@link #addMessageHandler(Class, MessageHandler)}.
136      */
137     public void messageReceived(IoSession session, Object message)
138             throws Exception {
139         MessageHandler<Object> handler = findHandler(message.getClass());
140         if (handler != null) {
141             handler.messageReceived(session, message);
142         } else {
143             throw new UnknownMessageTypeException(
144                     "No message handler found for message: " + message);
145         }
146     }
147 
148     protected MessageHandler<Object> findHandler(Class type) {
149         return findHandler(type, null);
150     }
151 
152     @SuppressWarnings("unchecked")
153     private MessageHandler<Object> findHandler(Class type,
154             Set<Class> triedClasses) {
155         MessageHandler handler = null;
156 
157         if (triedClasses != null && triedClasses.contains(type))
158             return null;
159 
160         /*
161          * Try the cache first.
162          */
163         handler = findHandlerCache.get(type);
164         if (handler != null)
165             return handler;
166 
167         /*
168          * Try the registered handlers for an immediate match.
169          */
170         handler = type2handler.get(type);
171 
172         if (handler == null) {
173             /*
174              * No immediate match could be found. Search the type's interfaces.
175              */
176 
177             if (triedClasses == null)
178                 triedClasses = new IdentityHashSet<Class>();
179             triedClasses.add(type);
180 
181             Class[] interfaces = type.getInterfaces();
182             for (int i = 0; i < interfaces.length; i++) {
183                 handler = findHandler(interfaces[i], triedClasses);
184                 if (handler != null)
185                     break;
186             }
187         }
188 
189         if (handler == null) {
190             /*
191              * No match in type's interfaces could be found. Search the 
192              * superclass.
193              */
194 
195             Class superclass = type.getSuperclass();
196             if (superclass != null)
197                 handler = findHandler(superclass);
198         }
199 
200         /*
201          * Make sure the handler is added to the cache. By updating the cache
202          * here all the types (superclasses and interfaces) in the path which 
203          * led to a match will be cached along with the immediate message type.
204          */
205         if (handler != null)
206             findHandlerCache.put(type, handler);
207 
208         return handler;
209     }
210 }