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 }