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.chain;
21  
22  import java.util.ArrayList;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.concurrent.ConcurrentHashMap;
27  
28  import org.apache.mina.core.session.IoSession;
29  
30  /**
31   * A chain of {@link IoHandlerCommand}s.
32   *
33   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
34   */
35  public class IoHandlerChain implements IoHandlerCommand {
36      private static volatile int nextId = 0;
37  
38      private final int id = nextId++;
39  
40      private final String NEXT_COMMAND = IoHandlerChain.class.getName() + '.' + id + ".nextCommand";
41  
42      private final Map<String, Entry> name2entry = new ConcurrentHashMap<String, Entry>();
43  
44      private final Entry head;
45  
46      private final Entry tail;
47  
48      /**
49       * Creates a new, empty chain of {@link IoHandlerCommand}s.
50       */
51      public IoHandlerChain() {
52          head = new Entry(null, null, "head", createHeadCommand());
53          tail = new Entry(head, null, "tail", createTailCommand());
54          head.nextEntry = tail;
55      }
56  
57      private IoHandlerCommand createHeadCommand() {
58          return new IoHandlerCommand() {
59              public void execute(NextCommand next, IoSession session, Object message) throws Exception {
60                  next.execute(session, message);
61              }
62          };
63      }
64  
65      private IoHandlerCommand createTailCommand() {
66          return new IoHandlerCommand() {
67              public void execute(NextCommand next, IoSession session, Object message) throws Exception {
68                  next = (NextCommand) session.getAttribute(NEXT_COMMAND);
69                  if (next != null) {
70                      next.execute(session, message);
71                  }
72              }
73          };
74      }
75  
76      public Entry getEntry(String name) {
77          Entry e = name2entry.get(name);
78          if (e == null) {
79              return null;
80          }
81          return e;
82      }
83  
84      public IoHandlerCommand get(String name) {
85          Entry e = getEntry(name);
86          if (e == null) {
87              return null;
88          }
89  
90          return e.getCommand();
91      }
92  
93      public NextCommand getNextCommand(String name) {
94          Entry e = getEntry(name);
95          if (e == null) {
96              return null;
97          }
98  
99          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          * Returns the name of the command.
310          */
311         public String getName() {
312             return name;
313         }
314 
315         /**
316          * Returns the command.
317          */
318         public IoHandlerCommand getCommand() {
319             return command;
320         }
321 
322         /**
323          * Returns the {@link IoHandlerCommand.NextCommand} of the command.
324          */
325         public NextCommand getNextCommand() {
326             return nextCommand;
327         }
328     }
329 }