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 <a href="http://mina.apache.org">Apache MINA Project</a>
78 */
79 public class DemuxingIoHandler extends IoHandlerAdapter {
80
81 private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlerCache = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
82
83 private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlers = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
84
85 private final Map<Class<?>, MessageHandler<?>> sentMessageHandlerCache = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
86
87 private final Map<Class<?>, MessageHandler<?>> sentMessageHandlers = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
88
89 private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlerCache = new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
90
91 private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlers = new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
92
93 /**
94 * Creates a new instance with no registered {@link MessageHandler}s.
95 */
96 public DemuxingIoHandler() {
97 // Do nothing
98 }
99
100 /**
101 * Registers a {@link MessageHandler} that handles the received messages of
102 * the specified <code>type</code>.
103 *
104 * @return the old handler if there is already a registered handler for
105 * the specified <tt>type</tt>. <tt>null</tt> otherwise.
106 */
107 @SuppressWarnings("unchecked")
108 public <E> MessageHandler<? super E> addReceivedMessageHandler(Class<E> type, MessageHandler<? super E> handler) {
109 receivedMessageHandlerCache.clear();
110 return (MessageHandler<? super E>) receivedMessageHandlers.put(type, handler);
111 }
112
113 /**
114 * Deregisters a {@link MessageHandler} that handles the received messages
115 * of the specified <code>type</code>.
116 *
117 * @return the removed handler if successfully removed. <tt>null</tt> otherwise.
118 */
119 @SuppressWarnings("unchecked")
120 public <E> MessageHandler<? super E> removeReceivedMessageHandler(Class<E> type) {
121 receivedMessageHandlerCache.clear();
122 return (MessageHandler<? super E>) receivedMessageHandlers.remove(type);
123 }
124
125 /**
126 * Registers a {@link MessageHandler} that handles the sent messages of the
127 * specified <code>type</code>.
128 *
129 * @return the old handler if there is already a registered handler for
130 * the specified <tt>type</tt>. <tt>null</tt> otherwise.
131 */
132 @SuppressWarnings("unchecked")
133 public <E> MessageHandler<? super E> addSentMessageHandler(Class<E> type, MessageHandler<? super E> handler) {
134 sentMessageHandlerCache.clear();
135 return (MessageHandler<? super E>) sentMessageHandlers.put(type, handler);
136 }
137
138 /**
139 * Deregisters a {@link MessageHandler} that handles the sent messages of
140 * the specified <code>type</code>.
141 *
142 * @return the removed handler if successfully removed. <tt>null</tt> otherwise.
143 */
144 @SuppressWarnings("unchecked")
145 public <E> MessageHandler<? super E> removeSentMessageHandler(Class<E> type) {
146 sentMessageHandlerCache.clear();
147 return (MessageHandler<? super E>) sentMessageHandlers.remove(type);
148 }
149
150 /**
151 * Registers a {@link MessageHandler} that receives the messages of
152 * the specified <code>type</code>.
153 *
154 * @return the old handler if there is already a registered handler for
155 * the specified <tt>type</tt>. <tt>null</tt> otherwise.
156 */
157 @SuppressWarnings("unchecked")
158 public <E extends Throwable> ExceptionHandler<? super E> addExceptionHandler(Class<E> type,
159 ExceptionHandler<? super E> handler) {
160 exceptionHandlerCache.clear();
161 return (ExceptionHandler<? super E>) exceptionHandlers.put(type, handler);
162 }
163
164 /**
165 * Deregisters a {@link MessageHandler} that receives the messages of
166 * the specified <code>type</code>.
167 *
168 * @return the removed handler if successfully removed. <tt>null</tt> otherwise.
169 */
170 @SuppressWarnings("unchecked")
171 public <E extends Throwable> ExceptionHandler<? super E> removeExceptionHandler(Class<E> type) {
172 exceptionHandlerCache.clear();
173 return (ExceptionHandler<? super E>) exceptionHandlers.remove(type);
174 }
175
176 /**
177 * Returns the {@link MessageHandler} which is registered to process
178 * the specified <code>type</code>.
179 */
180 @SuppressWarnings("unchecked")
181 public <E> MessageHandler<? super E> getMessageHandler(Class<E> type) {
182 return (MessageHandler<? super E>) receivedMessageHandlers.get(type);
183 }
184
185 /**
186 * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
187 * pairs registered to this handler for received messages.
188 */
189 public Map<Class<?>, MessageHandler<?>> getReceivedMessageHandlerMap() {
190 return Collections.unmodifiableMap(receivedMessageHandlers);
191 }
192
193 /**
194 * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
195 * pairs registered to this handler for sent messages.
196 */
197 public Map<Class<?>, MessageHandler<?>> getSentMessageHandlerMap() {
198 return Collections.unmodifiableMap(sentMessageHandlers);
199 }
200
201 /**
202 * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
203 * pairs registered to this handler.
204 */
205 public Map<Class<?>, ExceptionHandler<?>> getExceptionHandlerMap() {
206 return Collections.unmodifiableMap(exceptionHandlers);
207 }
208
209 /**
210 * Forwards the received events into the appropriate {@link MessageHandler}
211 * which is registered by {@link #addReceivedMessageHandler(Class, MessageHandler)}.
212 *
213 * <b>Warning !</b> If you are to overload this method, be aware that you
214 * _must_ call the messageHandler in your own method, otherwise it won't
215 * be called.
216 */
217 @Override
218 public void messageReceived(IoSession session, Object message) throws Exception {
219 MessageHandler<Object> handler = findReceivedMessageHandler(message.getClass());
220 if (handler != null) {
221 handler.handleMessage(session, message);
222 } else {
223 throw new UnknownMessageTypeException("No message handler found for message type: "
224 + message.getClass().getSimpleName());
225 }
226 }
227
228 /**
229 * Invoked when a message written by IoSession.write(Object) is sent out.
230 *
231 * <b>Warning !</b> If you are to overload this method, be aware that you
232 * _must_ call the messageHandler in your own method, otherwise it won't
233 * be called.
234 */
235 @Override
236 public void messageSent(IoSession session, Object message) throws Exception {
237 MessageHandler<Object> handler = findSentMessageHandler(message.getClass());
238 if (handler != null) {
239 handler.handleMessage(session, message);
240 } else {
241 throw new UnknownMessageTypeException("No handler found for message type: "
242 + message.getClass().getSimpleName());
243 }
244 }
245
246 /**
247 * Invoked when any exception is thrown by user IoHandler implementation
248 * or by MINA. If cause is an instance of IOException, MINA will close the
249 * connection automatically.
250 *
251 * <b>Warning !</b> If you are to overload this method, be aware that you
252 * _must_ call the messageHandler in your own method, otherwise it won't
253 * be called.
254 */
255 @Override
256 public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
257 ExceptionHandler<Throwable> handler = findExceptionHandler(cause.getClass());
258 if (handler != null) {
259 handler.exceptionCaught(session, cause);
260 } else {
261 throw new UnknownMessageTypeException("No handler found for exception type: "
262 + cause.getClass().getSimpleName());
263 }
264 }
265
266 protected MessageHandler<Object> findReceivedMessageHandler(Class<?> type) {
267 return findReceivedMessageHandler(type, null);
268 }
269
270 protected MessageHandler<Object> findSentMessageHandler(Class<?> type) {
271 return findSentMessageHandler(type, null);
272 }
273
274 protected ExceptionHandler<Throwable> findExceptionHandler(Class<? extends Throwable> type) {
275 return findExceptionHandler(type, null);
276 }
277
278 @SuppressWarnings("unchecked")
279 private MessageHandler<Object> findReceivedMessageHandler(Class type, Set<Class> triedClasses) {
280
281 return (MessageHandler<Object>) findHandler(receivedMessageHandlers, receivedMessageHandlerCache, type,
282 triedClasses);
283 }
284
285 @SuppressWarnings("unchecked")
286 private MessageHandler<Object> findSentMessageHandler(Class type, Set<Class> triedClasses) {
287
288 return (MessageHandler<Object>) findHandler(sentMessageHandlers, sentMessageHandlerCache, type, triedClasses);
289 }
290
291 @SuppressWarnings("unchecked")
292 private ExceptionHandler<Throwable> findExceptionHandler(Class type, Set<Class> triedClasses) {
293
294 return (ExceptionHandler<Throwable>) findHandler(exceptionHandlers, exceptionHandlerCache, type, triedClasses);
295 }
296
297 @SuppressWarnings("unchecked")
298 private Object findHandler(Map handlers, Map handlerCache, Class type, Set<Class> triedClasses) {
299
300 Object handler = null;
301
302 if (triedClasses != null && triedClasses.contains(type)) {
303 return null;
304 }
305
306 /*
307 * Try the cache first.
308 */
309 handler = handlerCache.get(type);
310 if (handler != null) {
311 return handler;
312 }
313
314 /*
315 * Try the registered handlers for an immediate match.
316 */
317 handler = handlers.get(type);
318
319 if (handler == null) {
320 /*
321 * No immediate match could be found. Search the type's interfaces.
322 */
323
324 if (triedClasses == null) {
325 triedClasses = new IdentityHashSet<Class>();
326 }
327 triedClasses.add(type);
328
329 Class[] interfaces = type.getInterfaces();
330 for (Class element : interfaces) {
331 handler = findHandler(handlers, handlerCache, element, triedClasses);
332 if (handler != null) {
333 break;
334 }
335 }
336 }
337
338 if (handler == null) {
339 /*
340 * No match in type's interfaces could be found. Search the
341 * superclass.
342 */
343 Class superclass = type.getSuperclass();
344 if (superclass != null) {
345 handler = findHandler(handlers, handlerCache, superclass, null);
346 }
347 }
348
349 /*
350 * Make sure the handler is added to the cache. By updating the cache
351 * here all the types (superclasses and interfaces) in the path which
352 * led to a match will be cached along with the immediate message type.
353 */
354 if (handler != null) {
355 handlerCache.put(type, handler);
356 }
357
358 return handler;
359 }
360 }