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 */ 020package org.apache.mina.handler.chain; 021 022import java.util.ArrayList; 023import java.util.Iterator; 024import java.util.List; 025import java.util.Map; 026import java.util.concurrent.ConcurrentHashMap; 027 028import org.apache.mina.core.session.IoSession; 029 030/** 031 * A chain of {@link IoHandlerCommand}s. 032 * 033 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 034 */ 035public class IoHandlerChain implements IoHandlerCommand { 036 private static volatile int nextId = 0; 037 038 private final int id = nextId++; 039 040 private final String NEXT_COMMAND = IoHandlerChain.class.getName() + '.' + id + ".nextCommand"; 041 042 private final Map<String, Entry> name2entry = new ConcurrentHashMap<String, Entry>(); 043 044 private final Entry head; 045 046 private final Entry tail; 047 048 /** 049 * Creates a new, empty chain of {@link IoHandlerCommand}s. 050 */ 051 public IoHandlerChain() { 052 head = new Entry(null, null, "head", createHeadCommand()); 053 tail = new Entry(head, null, "tail", createTailCommand()); 054 head.nextEntry = tail; 055 } 056 057 private IoHandlerCommand createHeadCommand() { 058 return new IoHandlerCommand() { 059 public void execute(NextCommand next, IoSession session, Object message) throws Exception { 060 next.execute(session, message); 061 } 062 }; 063 } 064 065 private IoHandlerCommand createTailCommand() { 066 return new IoHandlerCommand() { 067 public void execute(NextCommand next, IoSession session, Object message) throws Exception { 068 next = (NextCommand) session.getAttribute(NEXT_COMMAND); 069 if (next != null) { 070 next.execute(session, message); 071 } 072 } 073 }; 074 } 075 076 public Entry getEntry(String name) { 077 Entry e = name2entry.get(name); 078 if (e == null) { 079 return null; 080 } 081 return e; 082 } 083 084 public IoHandlerCommand get(String name) { 085 Entry e = getEntry(name); 086 if (e == null) { 087 return null; 088 } 089 090 return e.getCommand(); 091 } 092 093 public NextCommand getNextCommand(String name) { 094 Entry e = getEntry(name); 095 if (e == null) { 096 return null; 097 } 098 099 return e.getNextCommand(); 100 } 101 102 public synchronized void addFirst(String name, IoHandlerCommand command) { 103 checkAddable(name); 104 register(head, name, command); 105 } 106 107 public synchronized void addLast(String name, IoHandlerCommand command) { 108 checkAddable(name); 109 register(tail.prevEntry, name, command); 110 } 111 112 public synchronized void addBefore(String baseName, String name, IoHandlerCommand command) { 113 Entry baseEntry = checkOldName(baseName); 114 checkAddable(name); 115 register(baseEntry.prevEntry, name, command); 116 } 117 118 public synchronized void addAfter(String baseName, String name, IoHandlerCommand command) { 119 Entry baseEntry = checkOldName(baseName); 120 checkAddable(name); 121 register(baseEntry, name, command); 122 } 123 124 public synchronized IoHandlerCommand remove(String name) { 125 Entry entry = checkOldName(name); 126 deregister(entry); 127 return entry.getCommand(); 128 } 129 130 public synchronized void clear() throws Exception { 131 Iterator<String> it = new ArrayList<String>(name2entry.keySet()).iterator(); 132 while (it.hasNext()) { 133 this.remove(it.next()); 134 } 135 } 136 137 private void register(Entry prevEntry, String name, IoHandlerCommand command) { 138 Entry newEntry = new Entry(prevEntry, prevEntry.nextEntry, name, command); 139 prevEntry.nextEntry.prevEntry = newEntry; 140 prevEntry.nextEntry = newEntry; 141 142 name2entry.put(name, newEntry); 143 } 144 145 private void deregister(Entry entry) { 146 Entry prevEntry = entry.prevEntry; 147 Entry nextEntry = entry.nextEntry; 148 prevEntry.nextEntry = nextEntry; 149 nextEntry.prevEntry = prevEntry; 150 151 name2entry.remove(entry.name); 152 } 153 154 /** 155 * Throws an exception when the specified filter name is not registered in this chain. 156 * 157 * @return An filter entry with the specified name. 158 */ 159 private Entry checkOldName(String baseName) { 160 Entry e = name2entry.get(baseName); 161 if (e == null) { 162 throw new IllegalArgumentException("Unknown filter name:" + baseName); 163 } 164 return e; 165 } 166 167 /** 168 * Checks the specified filter name is already taken and throws an exception if already taken. 169 */ 170 private void checkAddable(String name) { 171 if (name2entry.containsKey(name)) { 172 throw new IllegalArgumentException("Other filter is using the same name '" + name + "'"); 173 } 174 } 175 176 public void execute(NextCommand next, IoSession session, Object message) throws Exception { 177 if (next != null) { 178 session.setAttribute(NEXT_COMMAND, next); 179 } 180 181 try { 182 callNextCommand(head, session, message); 183 } finally { 184 session.removeAttribute(NEXT_COMMAND); 185 } 186 } 187 188 private void callNextCommand(Entry entry, IoSession session, Object message) throws Exception { 189 entry.getCommand().execute(entry.getNextCommand(), session, message); 190 } 191 192 public List<Entry> getAll() { 193 List<Entry> list = new ArrayList<Entry>(); 194 Entry e = head.nextEntry; 195 while (e != tail) { 196 list.add(e); 197 e = e.nextEntry; 198 } 199 200 return list; 201 } 202 203 public List<Entry> getAllReversed() { 204 List<Entry> list = new ArrayList<Entry>(); 205 Entry e = tail.prevEntry; 206 while (e != head) { 207 list.add(e); 208 e = e.prevEntry; 209 } 210 return list; 211 } 212 213 public boolean contains(String name) { 214 return getEntry(name) != null; 215 } 216 217 public boolean contains(IoHandlerCommand command) { 218 Entry e = head.nextEntry; 219 while (e != tail) { 220 if (e.getCommand() == command) { 221 return true; 222 } 223 e = e.nextEntry; 224 } 225 return false; 226 } 227 228 public boolean contains(Class<? extends IoHandlerCommand> commandType) { 229 Entry e = head.nextEntry; 230 while (e != tail) { 231 if (commandType.isAssignableFrom(e.getCommand().getClass())) { 232 return true; 233 } 234 e = e.nextEntry; 235 } 236 return false; 237 } 238 239 @Override 240 public String toString() { 241 StringBuilder buf = new StringBuilder(); 242 buf.append("{ "); 243 244 boolean empty = true; 245 246 Entry e = head.nextEntry; 247 while (e != tail) { 248 if (!empty) { 249 buf.append(", "); 250 } else { 251 empty = false; 252 } 253 254 buf.append('('); 255 buf.append(e.getName()); 256 buf.append(':'); 257 buf.append(e.getCommand()); 258 buf.append(')'); 259 260 e = e.nextEntry; 261 } 262 263 if (empty) { 264 buf.append("empty"); 265 } 266 267 buf.append(" }"); 268 269 return buf.toString(); 270 } 271 272 /** 273 * Represents a name-command pair that an {@link IoHandlerChain} contains. 274 * 275 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 276 */ 277 public class Entry { 278 private Entry prevEntry; 279 280 private Entry nextEntry; 281 282 private final String name; 283 284 private final IoHandlerCommand command; 285 286 private final NextCommand nextCommand; 287 288 private Entry(Entry prevEntry, Entry nextEntry, String name, IoHandlerCommand command) { 289 if (command == null) { 290 throw new IllegalArgumentException("command"); 291 } 292 if (name == null) { 293 throw new IllegalArgumentException("name"); 294 } 295 296 this.prevEntry = prevEntry; 297 this.nextEntry = nextEntry; 298 this.name = name; 299 this.command = command; 300 this.nextCommand = new NextCommand() { 301 public void execute(IoSession session, Object message) throws Exception { 302 Entry nextEntry = Entry.this.nextEntry; 303 callNextCommand(nextEntry, session, message); 304 } 305 }; 306 } 307 308 /** 309 * @return the name of the command. 310 */ 311 public String getName() { 312 return name; 313 } 314 315 /** 316 * @return the command. 317 */ 318 public IoHandlerCommand getCommand() { 319 return command; 320 } 321 322 /** 323 * @return the {@link IoHandlerCommand.NextCommand} of the command. 324 */ 325 public NextCommand getNextCommand() { 326 return nextCommand; 327 } 328 } 329}