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