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.HashMap;
24  import java.util.Iterator;
25  import java.util.List;
26  import java.util.Map;
27  
28  import org.apache.mina.core.session.IoSession;
29  
30  /**
31   * A chain of {@link IoHandlerCommand}s.
32   *
33   * @author The Apache MINA Project (dev@mina.apache.org)
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() + '.'
41              + id + ".nextCommand";
42  
43      private final Map<String, Entry> name2entry = new HashMap<String, Entry>();
44  
45      private final Entry head;
46  
47      private final Entry tail;
48  
49      /**
50       * Creates a new, empty chain of {@link IoHandlerCommand}s.
51       */
52      public IoHandlerChain() {
53          head = new Entry(null, null, "head", createHeadCommand());
54          tail = new Entry(head, null, "tail", createTailCommand());
55          head.nextEntry = tail;
56      }
57  
58      private IoHandlerCommand createHeadCommand() {
59          return new IoHandlerCommand() {
60              public void execute(NextCommand next, IoSession session,
61                      Object message) throws Exception {
62                  next.execute(session, message);
63              }
64          };
65      }
66  
67      private IoHandlerCommand createTailCommand() {
68          return new IoHandlerCommand() {
69              public void execute(NextCommand next, IoSession session,
70                      Object message) throws Exception {
71                  next = (NextCommand) session.getAttribute(NEXT_COMMAND);
72                  if (next != null) {
73                      next.execute(session, message);
74                  }
75              }
76          };
77      }
78  
79      public Entry getEntry(String name) {
80          Entry e = name2entry.get(name);
81          if (e == null) {
82              return null;
83          }
84          return e;
85      }
86  
87      public IoHandlerCommand get(String name) {
88          Entry e = getEntry(name);
89          if (e == null) {
90              return null;
91          }
92  
93          return e.getCommand();
94      }
95  
96      public NextCommand getNextCommand(String name) {
97          Entry e = getEntry(name);
98          if (e == null) {
99              return null;
100         }
101 
102         return e.getNextCommand();
103     }
104 
105     public synchronized void addFirst(String name, IoHandlerCommand command) {
106         checkAddable(name);
107         register(head, name, command);
108     }
109 
110     public synchronized void addLast(String name, IoHandlerCommand command) {
111         checkAddable(name);
112         register(tail.prevEntry, name, command);
113     }
114 
115     public synchronized void addBefore(String baseName, String name,
116             IoHandlerCommand command) {
117         Entry baseEntry = checkOldName(baseName);
118         checkAddable(name);
119         register(baseEntry.prevEntry, name, command);
120     }
121 
122     public synchronized void addAfter(String baseName, String name,
123             IoHandlerCommand command) {
124         Entry baseEntry = checkOldName(baseName);
125         checkAddable(name);
126         register(baseEntry, name, command);
127     }
128 
129     public synchronized IoHandlerCommand remove(String name) {
130         Entry entry = checkOldName(name);
131         deregister(entry);
132         return entry.getCommand();
133     }
134 
135     public synchronized void clear() throws Exception {
136         Iterator<String> it = new ArrayList<String>(name2entry.keySet())
137                 .iterator();
138         while (it.hasNext()) {
139             this.remove(it.next());
140         }
141     }
142 
143     private void register(Entry prevEntry, String name, IoHandlerCommand command) {
144         Entry newEntry = new Entry(prevEntry, prevEntry.nextEntry, name,
145                 command);
146         prevEntry.nextEntry.prevEntry = newEntry;
147         prevEntry.nextEntry = newEntry;
148 
149         name2entry.put(name, newEntry);
150     }
151 
152     private void deregister(Entry entry) {
153         Entry prevEntry = entry.prevEntry;
154         Entry nextEntry = entry.nextEntry;
155         prevEntry.nextEntry = nextEntry;
156         nextEntry.prevEntry = prevEntry;
157 
158         name2entry.remove(entry.name);
159     }
160 
161     /**
162      * Throws an exception when the specified filter name is not registered in this chain.
163      *
164      * @return An filter entry with the specified name.
165      */
166     private Entry checkOldName(String baseName) {
167         Entry e = name2entry.get(baseName);
168         if (e == null) {
169             throw new IllegalArgumentException("Unknown filter name:"
170                     + baseName);
171         }
172         return e;
173     }
174 
175     /**
176      * Checks the specified filter name is already taken and throws an exception if already taken.
177      */
178     private void checkAddable(String name) {
179         if (name2entry.containsKey(name)) {
180             throw new IllegalArgumentException(
181                     "Other filter is using the same name '" + name + "'");
182         }
183     }
184 
185     public void execute(NextCommand next, IoSession session, Object message)
186             throws Exception {
187         if (next != null) {
188             session.setAttribute(NEXT_COMMAND, next);
189         }
190 
191         try {
192             callNextCommand(head, session, message);
193         } finally {
194             session.removeAttribute(NEXT_COMMAND);
195         }
196     }
197 
198     private void callNextCommand(Entry entry, IoSession session, Object message)
199             throws Exception {
200         entry.getCommand().execute(entry.getNextCommand(), session, message);
201     }
202 
203     public List<Entry> getAll() {
204         List<Entry> list = new ArrayList<Entry>();
205         Entry e = head.nextEntry;
206         while (e != tail) {
207             list.add(e);
208             e = e.nextEntry;
209         }
210 
211         return list;
212     }
213 
214     public List<Entry> getAllReversed() {
215         List<Entry> list = new ArrayList<Entry>();
216         Entry e = tail.prevEntry;
217         while (e != head) {
218             list.add(e);
219             e = e.prevEntry;
220         }
221         return list;
222     }
223 
224     public boolean contains(String name) {
225         return getEntry(name) != null;
226     }
227 
228     public boolean contains(IoHandlerCommand command) {
229         Entry e = head.nextEntry;
230         while (e != tail) {
231             if (e.getCommand() == command) {
232                 return true;
233             }
234             e = e.nextEntry;
235         }
236         return false;
237     }
238 
239     public boolean contains(Class<? extends IoHandlerCommand> commandType) {
240         Entry e = head.nextEntry;
241         while (e != tail) {
242             if (commandType.isAssignableFrom(e.getCommand().getClass())) {
243                 return true;
244             }
245             e = e.nextEntry;
246         }
247         return false;
248     }
249 
250     @Override
251     public String toString() {
252         StringBuilder buf = new StringBuilder();
253         buf.append("{ ");
254 
255         boolean empty = true;
256 
257         Entry e = head.nextEntry;
258         while (e != tail) {
259             if (!empty) {
260                 buf.append(", ");
261             } else {
262                 empty = false;
263             }
264 
265             buf.append('(');
266             buf.append(e.getName());
267             buf.append(':');
268             buf.append(e.getCommand());
269             buf.append(')');
270 
271             e = e.nextEntry;
272         }
273 
274         if (empty) {
275             buf.append("empty");
276         }
277 
278         buf.append(" }");
279 
280         return buf.toString();
281     }
282 
283     /**
284      * Represents a name-command pair that an {@link IoHandlerChain} contains.
285      *
286      * @author The Apache MINA Project (dev@mina.apache.org)
287      */
288     public class Entry {
289         private Entry prevEntry;
290 
291         private Entry nextEntry;
292 
293         private final String name;
294 
295         private final IoHandlerCommand command;
296 
297         private final NextCommand nextCommand;
298 
299         private Entry(Entry prevEntry, Entry nextEntry, String name,
300                 IoHandlerCommand command) {
301             if (command == null) {
302                 throw new NullPointerException("command");
303             }
304             if (name == null) {
305                 throw new NullPointerException("name");
306             }
307 
308             this.prevEntry = prevEntry;
309             this.nextEntry = nextEntry;
310             this.name = name;
311             this.command = command;
312             this.nextCommand = new NextCommand() {
313                 public void execute(IoSession session, Object message)
314                         throws Exception {
315                     Entry nextEntry = Entry.this.nextEntry;
316                     callNextCommand(nextEntry, session, message);
317                 }
318             };
319         }
320 
321         /**
322          * Returns the name of the command.
323          */
324         public String getName() {
325             return name;
326         }
327 
328         /**
329          * Returns the command.
330          */
331         public IoHandlerCommand getCommand() {
332             return command;
333         }
334 
335         /**
336          * Returns the {@link IoHandlerCommand.NextCommand} of the command.
337          */
338         public NextCommand getNextCommand() {
339             return nextCommand;
340         }
341     }
342 }