001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: *
019: */
020: package org.apache.mina.handler.demux;
021:
022: import java.util.Collections;
023: import java.util.Map;
024: import java.util.Set;
025: import java.util.concurrent.ConcurrentHashMap;
026:
027: import org.apache.mina.common.IoHandler;
028: import org.apache.mina.common.IoHandlerAdapter;
029: import org.apache.mina.common.IoSession;
030: import org.apache.mina.common.UnknownMessageTypeException;
031: import org.apache.mina.util.IdentityHashSet;
032:
033: /**
034: * A {@link IoHandler} that demuxes <code>messageReceived</code> events
035: * to the appropriate {@link MessageHandler}.
036: * <p>
037: * You can freely register and deregister {@link MessageHandler}s using
038: * {@link #addReceivedMessageHandler(Class, MessageHandler)} and
039: * {@link #removeReceivedMessageHandler(Class)}.
040: * </p>
041: * <p>
042: * When <code>message</code> is received through a call to
043: * {@link #messageReceived(IoSession, Object)} the class of the
044: * <code>message</code> object will be used to find a {@link MessageHandler} for
045: * that particular message type. If no {@link MessageHandler} instance can be
046: * found for the immediate class (i.e. <code>message.getClass()</code>) the
047: * interfaces implemented by the immediate class will be searched in depth-first
048: * order. If no match can be found for any of the interfaces the search will be
049: * repeated recursively for the superclass of the immediate class
050: * (i.e. <code>message.getClass().getSuperclass()</code>).
051: * </p>
052: * <p>
053: * Consider the following type hierarchy (<code>Cx</code> are classes while
054: * <code>Ix</code> are interfaces):
055: * <pre>
056: * C3 - I7 - I9
057: * | | /\
058: * | I8 I3 I4
059: * |
060: * C2 - I5 - I6
061: * |
062: * C1 - I1 - I2 - I4
063: * | |
064: * | I3
065: * Object
066: * </pre>
067: * When <code>message</code> is of type <code>C3</code> this hierarchy will be
068: * searched in the following order:
069: * <code>C3, I7, I8, I9, I3, I4, C2, I5, I6, C1, I1, I2, I3, I4, Object</code>.
070: * </p>
071: * <p>
072: * For efficiency searches will be cached. Calls to
073: * {@link #addReceivedMessageHandler(Class, MessageHandler)} and
074: * {@link #removeReceivedMessageHandler(Class)} clear this cache.
075: * </p>
076: *
077: * @author The Apache MINA Project (dev@mina.apache.org)
078: * @version $Rev: 612349 $, $Date: 2008-01-15 21:39:03 -0700 (Tue, 15 Jan 2008) $
079: */
080: public class DemuxingIoHandler extends IoHandlerAdapter {
081:
082: private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlerCache = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
083:
084: private final Map<Class<?>, MessageHandler<?>> receivedMessageHandlers = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
085:
086: private final Map<Class<?>, MessageHandler<?>> sentMessageHandlerCache = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
087:
088: private final Map<Class<?>, MessageHandler<?>> sentMessageHandlers = new ConcurrentHashMap<Class<?>, MessageHandler<?>>();
089:
090: private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlerCache = new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
091:
092: private final Map<Class<?>, ExceptionHandler<?>> exceptionHandlers = new ConcurrentHashMap<Class<?>, ExceptionHandler<?>>();
093:
094: /**
095: * Creates a new instance with no registered {@link MessageHandler}s.
096: */
097: public DemuxingIoHandler() {
098: }
099:
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(
109: Class<E> type, MessageHandler<? super E> handler) {
110: receivedMessageHandlerCache.clear();
111: return (MessageHandler<? super E>) receivedMessageHandlers.put(
112: type, handler);
113: }
114:
115: /**
116: * Deregisters a {@link MessageHandler} that handles the received messages
117: * of the specified <code>type</code>.
118: *
119: * @return the removed handler if successfully removed. <tt>null</tt> otherwise.
120: */
121: @SuppressWarnings("unchecked")
122: public <E> MessageHandler<? super E> removeReceivedMessageHandler(
123: Class<E> type) {
124: receivedMessageHandlerCache.clear();
125: return (MessageHandler<? super E>) receivedMessageHandlers
126: .remove(type);
127: }
128:
129: /**
130: * Registers a {@link MessageHandler} that handles the sent messages of the
131: * specified <code>type</code>.
132: *
133: * @return the old handler if there is already a registered handler for
134: * the specified <tt>type</tt>. <tt>null</tt> otherwise.
135: */
136: @SuppressWarnings("unchecked")
137: public <E> MessageHandler<? super E> addSentMessageHandler(
138: Class<E> type, MessageHandler<? super E> handler) {
139: sentMessageHandlerCache.clear();
140: return (MessageHandler<? super E>) sentMessageHandlers.put(
141: type, handler);
142: }
143:
144: /**
145: * Deregisters a {@link MessageHandler} that handles the sent messages of
146: * the specified <code>type</code>.
147: *
148: * @return the removed handler if successfully removed. <tt>null</tt> otherwise.
149: */
150: @SuppressWarnings("unchecked")
151: public <E> MessageHandler<? super E> removeSentMessageHandler(
152: Class<E> type) {
153: sentMessageHandlerCache.clear();
154: return (MessageHandler<? super E>) sentMessageHandlers
155: .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> ExceptionHandler<? super E> addExceptionHandler(
167: Class<E> type, ExceptionHandler<? super E> handler) {
168: exceptionHandlerCache.clear();
169: return (ExceptionHandler<? super E>) exceptionHandlers.put(
170: 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> removeExceptionHandler(
181: Class<E> type) {
182: exceptionHandlerCache.clear();
183: return (ExceptionHandler<? super E>) exceptionHandlers
184: .remove(type);
185: }
186:
187: /**
188: * Returns the {@link MessageHandler} which is registered to process
189: * the specified <code>type</code>.
190: */
191: @SuppressWarnings("unchecked")
192: public <E> MessageHandler<? super E> getMessageHandler(Class<E> type) {
193: return (MessageHandler<? super E>) receivedMessageHandlers
194: .get(type);
195: }
196:
197: /**
198: * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
199: * pairs registered to this handler for received messages.
200: */
201: public Map<Class<?>, MessageHandler<?>> getReceivedMessageHandlerMap() {
202: return Collections.unmodifiableMap(receivedMessageHandlers);
203: }
204:
205: /**
206: * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
207: * pairs registered to this handler for sent messages.
208: */
209: public Map<Class<?>, MessageHandler<?>> getSentMessageHandlerMap() {
210: return Collections.unmodifiableMap(sentMessageHandlers);
211: }
212:
213: /**
214: * Returns the {@link Map} which contains all messageType-{@link MessageHandler}
215: * pairs registered to this handler.
216: */
217: public Map<Class<?>, ExceptionHandler<?>> getExceptionHandlerMap() {
218: return Collections.unmodifiableMap(exceptionHandlers);
219: }
220:
221: /**
222: * Forwards the received events into the appropriate {@link MessageHandler}
223: * which is registered by {@link #addReceivedMessageHandler(Class, MessageHandler)}.
224: */
225: @Override
226: public final void messageReceived(IoSession session, Object message)
227: throws Exception {
228: MessageHandler<Object> handler = findReceivedMessageHandler(message
229: .getClass());
230: if (handler != null) {
231: handler.handleMessage(session, message);
232: } else {
233: throw new UnknownMessageTypeException(
234: "No message handler found for message type: "
235: + message.getClass().getSimpleName());
236: }
237: }
238:
239: @Override
240: public final void messageSent(IoSession session, Object message)
241: throws Exception {
242: MessageHandler<Object> handler = findSentMessageHandler(message
243: .getClass());
244: if (handler != null) {
245: handler.handleMessage(session, message);
246: } else {
247: throw new UnknownMessageTypeException(
248: "No handler found for message type: "
249: + message.getClass().getSimpleName());
250: }
251: }
252:
253: @Override
254: public final void exceptionCaught(IoSession session, Throwable cause)
255: throws Exception {
256: ExceptionHandler<Throwable> handler = findExceptionHandler(cause
257: .getClass());
258: if (handler != null) {
259: handler.exceptionCaught(session, cause);
260: } else {
261: throw new UnknownMessageTypeException(
262: "No handler found for exception type: "
263: + cause.getClass().getSimpleName());
264: }
265: }
266:
267: protected MessageHandler<Object> findReceivedMessageHandler(
268: Class<?> type) {
269: return findReceivedMessageHandler(type, null);
270: }
271:
272: protected MessageHandler<Object> findSentMessageHandler(
273: Class<?> type) {
274: return findSentMessageHandler(type, null);
275: }
276:
277: protected ExceptionHandler<Throwable> findExceptionHandler(
278: Class<? extends Throwable> type) {
279: return findExceptionHandler(type, null);
280: }
281:
282: @SuppressWarnings("unchecked")
283: private MessageHandler<Object> findReceivedMessageHandler(
284: Class type, Set<Class> triedClasses) {
285:
286: return (MessageHandler<Object>) findHandler(
287: receivedMessageHandlers, receivedMessageHandlerCache,
288: type, triedClasses);
289: }
290:
291: @SuppressWarnings("unchecked")
292: private MessageHandler<Object> findSentMessageHandler(Class type,
293: Set<Class> triedClasses) {
294:
295: return (MessageHandler<Object>) findHandler(
296: sentMessageHandlers, sentMessageHandlerCache, type,
297: triedClasses);
298: }
299:
300: @SuppressWarnings("unchecked")
301: private ExceptionHandler<Throwable> findExceptionHandler(
302: Class type, Set<Class> triedClasses) {
303:
304: return (ExceptionHandler<Throwable>) findHandler(
305: exceptionHandlers, exceptionHandlerCache, type,
306: triedClasses);
307: }
308:
309: @SuppressWarnings("unchecked")
310: private Object findHandler(Map handlers, Map handlerCache,
311: Class type, Set<Class> triedClasses) {
312:
313: Object handler = null;
314:
315: if (triedClasses != null && triedClasses.contains(type)) {
316: return null;
317: }
318:
319: /*
320: * Try the cache first.
321: */
322: handler = handlerCache.get(type);
323: if (handler != null) {
324: return handler;
325: }
326:
327: /*
328: * Try the registered handlers for an immediate match.
329: */
330: handler = handlers.get(type);
331:
332: if (handler == null) {
333: /*
334: * No immediate match could be found. Search the type's interfaces.
335: */
336:
337: if (triedClasses == null) {
338: triedClasses = new IdentityHashSet<Class>();
339: }
340: triedClasses.add(type);
341:
342: Class[] interfaces = type.getInterfaces();
343: for (Class element : interfaces) {
344: handler = findHandler(handlers, handlerCache, element,
345: triedClasses);
346: if (handler != null) {
347: break;
348: }
349: }
350: }
351:
352: if (handler == null) {
353: /*
354: * No match in type's interfaces could be found. Search the
355: * superclass.
356: */
357: Class super class = type.getSuperclass();
358: if (super class != null) {
359: handler = findHandler(handlers, handlerCache,
360: super class, null);
361: }
362: }
363:
364: /*
365: * Make sure the handler is added to the cache. By updating the cache
366: * here all the types (superclasses and interfaces) in the path which
367: * led to a match will be cached along with the immediate message type.
368: */
369: if (handler != null) {
370: handlerCache.put(type, handler);
371: }
372:
373: return handler;
374: }
375: }
|