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   * @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (jeu, 26 jun 2008) $
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     @Override
252     public String toString() {
253         StringBuffer buf = new StringBuffer();
254         buf.append("{ ");
255 
256         boolean empty = true;
257 
258         Entry e = head.nextEntry;
259         while (e != tail) {
260             if (!empty) {
261                 buf.append(", ");
262             } else {
263                 empty = false;
264             }
265 
266             buf.append('(');
267             buf.append(e.getName());
268             buf.append(':');
269             buf.append(e.getCommand());
270             buf.append(')');
271 
272             e = e.nextEntry;
273         }
274 
275         if (empty) {
276             buf.append("empty");
277         }
278 
279         buf.append(" }");
280 
281         return buf.toString();
282     }
283 
284     /**
285      * Represents a name-command pair that an {@link IoHandlerChain} contains.
286      *
287      * @author The Apache MINA Project (dev@mina.apache.org)
288      * @version $Rev: 671827 $, $Date: 2008-06-26 10:49:48 +0200 (jeu, 26 jun 2008) $
289      */
290     public class Entry {
291         private Entry prevEntry;
292 
293         private Entry nextEntry;
294 
295         private final String name;
296 
297         private final IoHandlerCommand command;
298 
299         private final NextCommand nextCommand;
300 
301         private Entry(Entry prevEntry, Entry nextEntry, String name,
302                 IoHandlerCommand command) {
303             if (command == null) {
304                 throw new NullPointerException("command");
305             }
306             if (name == null) {
307                 throw new NullPointerException("name");
308             }
309 
310             this.prevEntry = prevEntry;
311             this.nextEntry = nextEntry;
312             this.name = name;
313             this.command = command;
314             this.nextCommand = new NextCommand() {
315                 public void execute(IoSession session, Object message)
316                         throws Exception {
317                     Entry nextEntry = Entry.this.nextEntry;
318                     callNextCommand(nextEntry, session, message);
319                 }
320             };
321         }
322 
323         /**
324          * Returns the name of the command.
325          */
326         public String getName() {
327             return name;
328         }
329 
330         /**
331          * Returns the command.
332          */
333         public IoHandlerCommand getCommand() {
334             return command;
335         }
336 
337         /**
338          * Returns the {@link IoHandlerCommand.NextCommand} of the command.
339          */
340         public NextCommand getNextCommand() {
341             return nextCommand;
342         }
343     }
344 }