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<>();
43  
44      /** The head of the IoHandlerCommand chain */
45      private final Entry head;
46  
47      /** THe tail of the IoHandlerCommand chain */
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              /**
62               * {@inheritDoc}
63               */
64              @Override
65              public void execute(NextCommand next, IoSession session, Object message) throws Exception {
66                  next.execute(session, message);
67              }
68          };
69      }
70  
71      private IoHandlerCommand createTailCommand() {
72          return new IoHandlerCommand() {
73              /**
74               * {@inheritDoc}
75               */
76              @Override
77              public void execute(NextCommand next, IoSession session, Object message) throws Exception {
78                  next = (NextCommand) session.getAttribute(NEXT_COMMAND);
79                  
80                  if (next != null) {
81                      next.execute(session, message);
82                  }
83              }
84          };
85      }
86  
87      /**
88       * Retrieve a name-command pair by its name
89       * @param name The name of the {@link IoHandlerCommand} we are looking for
90       * @return The associated name-command pair, if any, null otherwise
91       */
92      public Entry getEntry(String name) {
93          Entry e = name2entry.get(name);
94          
95          if (e == null) {
96              return null;
97          }
98          
99          return e;
100     }
101 
102     /**
103      * Retrieve a {@link IoHandlerCommand} by its name
104      * 
105      * @param name The name of the {@link IoHandlerCommand} we are looking for
106      * @return The associated {@link IoHandlerCommand}, if any, null otherwise
107      */
108     public IoHandlerCommand get(String name) {
109         Entry e = getEntry(name);
110         
111         if (e == null) {
112             return null;
113         }
114 
115         return e.getCommand();
116     }
117 
118     /**
119      * Retrieve the {@link IoHandlerCommand} following the {@link IoHandlerCommand} we
120      * fetched by its name
121      * 
122      * @param name The name of the {@link IoHandlerCommand}
123      * @return The {@link IoHandlerCommand} which is next to teh ngiven name, if any, null otherwise
124      */
125     public NextCommand getNextCommand(String name) {
126         Entry e = getEntry(name);
127         
128         if (e == null) {
129             return null;
130         }
131 
132         return e.getNextCommand();
133     }
134 
135     /**
136      * Adds a name-command pair into the chain
137      * 
138      * @param name The name
139      * @param command The command
140      */
141     public synchronized void addFirst(String name, IoHandlerCommand command) {
142         checkAddable(name);
143         register(head, name, command);
144     }
145 
146     /**
147      * Adds a name-command at the end of the chain
148      * 
149      * @param name The name
150      * @param command The command
151      */
152     public synchronized void addLast(String name, IoHandlerCommand command) {
153         checkAddable(name);
154         register(tail.prevEntry, name, command);
155     }
156 
157     /**
158      * Adds a name-command before a given name-command in the chain
159      * 
160      * @param baseName The {@linkplain IoHandlerCommand} name before which we will inject a new name-command
161      * @param name The name The name
162      * @param command The command The command
163      */
164     public synchronized void addBefore(String baseName, String name, IoHandlerCommand command) {
165         Entry baseEntry = checkOldName(baseName);
166         checkAddable(name);
167         register(baseEntry.prevEntry, name, command);
168     }
169 
170     /**
171      * Adds a name-command after a given name-command in the chain
172      * 
173      * @param baseName The {@link IoHandlerCommand} name after which we will inject a new name-command
174      * @param name The name The name
175      * @param command The command The command
176      */
177     public synchronized void addAfter(String baseName, String name, IoHandlerCommand command) {
178         Entry baseEntry = checkOldName(baseName);
179         checkAddable(name);
180         register(baseEntry, name, command);
181     }
182 
183     /**
184      * Removes a {@link IoHandlerCommand} by its name
185      * 
186      * @param name The name
187      * @return The removed {@link IoHandlerCommand}
188      */
189     public synchronized IoHandlerCommand remove(String name) {
190         Entry entry = checkOldName(name);
191         deregister(entry);
192         
193         return entry.getCommand();
194     }
195 
196     /**
197      * Remove all the {@link IoHandlerCommand} from the chain
198      * @throws Exception If we faced some exception during the cleanup 
199      */
200     public synchronized void clear() throws Exception {
201         Iterator<String> it = new ArrayList<String>(name2entry.keySet()).iterator();
202        
203         while (it.hasNext()) {
204             remove(it.next());
205         }
206     }
207 
208     private void register(Entry prevEntry, String name, IoHandlerCommand command) {
209         Entry newEntry = new Entry(prevEntry, prevEntry.nextEntry, name, command);
210         prevEntry.nextEntry.prevEntry = newEntry;
211         prevEntry.nextEntry = newEntry;
212 
213         name2entry.put(name, newEntry);
214     }
215 
216     private void deregister(Entry entry) {
217         Entry prevEntry = entry.prevEntry;
218         Entry nextEntry = entry.nextEntry;
219         prevEntry.nextEntry = nextEntry;
220         nextEntry.prevEntry = prevEntry;
221 
222         name2entry.remove(entry.name);
223     }
224 
225     /**
226      * Throws an exception when the specified filter name is not registered in this chain.
227      *
228      * @return An filter entry with the specified name.
229      */
230     private Entry checkOldName(String baseName) {
231         Entry e = name2entry.get(baseName);
232         
233         if (e == null) {
234             throw new IllegalArgumentException("Unknown filter name:" + baseName);
235         }
236         
237         return e;
238     }
239 
240     /**
241      * Checks the specified filter name is already taken and throws an exception if already taken.
242      */
243     private void checkAddable(String name) {
244         if (name2entry.containsKey(name)) {
245             throw new IllegalArgumentException("Other filter is using the same name '" + name + "'");
246         }
247     }
248 
249     /**
250      * {@inheritDoc}
251      */
252     @Override
253     public void execute(NextCommand next, IoSession session, Object message) throws Exception {
254         if (next != null) {
255             session.setAttribute(NEXT_COMMAND, next);
256         }
257 
258         try {
259             callNextCommand(head, session, message);
260         } finally {
261             session.removeAttribute(NEXT_COMMAND);
262         }
263     }
264 
265     private void callNextCommand(Entry entry, IoSession session, Object message) throws Exception {
266         entry.getCommand().execute(entry.getNextCommand(), session, message);
267     }
268 
269     /**
270      * @return The list of name-commands registered into the chain
271      */
272     public List<Entry> getAll() {
273         List<Entry> list = new ArrayList<>();
274         Entry e = head.nextEntry;
275         
276         while (e != tail) {
277             list.add(e);
278             e = e.nextEntry;
279         }
280 
281         return list;
282     }
283 
284     /**
285      * @return A reverted list of the registered name-commands
286      */
287     public List<Entry> getAllReversed() {
288         List<Entry> list = new ArrayList<>();
289         Entry e = tail.prevEntry;
290         
291         while (e != head) {
292             list.add(e);
293             e = e.prevEntry;
294         }
295         
296         return list;
297     }
298 
299     /**
300      * Checks if the chain of {@link IoHandlerCommand} contains a {@link IoHandlerCommand} by its name
301      * 
302      * @param name The {@link IoHandlerCommand} name
303      * @return <tt>TRUE</tt> if the {@link IoHandlerCommand} is found in the chain
304      */
305     public boolean contains(String name) {
306         return getEntry(name) != null;
307     }
308 
309     /**
310      * Checks if the chain of {@link IoHandlerCommand} contains a specific {@link IoHandlerCommand}
311      * 
312      * @param command The {@link IoHandlerCommand} we are looking for
313      * @return <tt>TRUE</tt> if the {@link IoHandlerCommand} is found in the chain
314      */
315     public boolean contains(IoHandlerCommand command) {
316         Entry e = head.nextEntry;
317         while (e != tail) {
318             if (e.getCommand() == command) {
319                 return true;
320             }
321             e = e.nextEntry;
322         }
323         return false;
324     }
325 
326     /**
327      * Checks if the chain of {@link IoHandlerCommand} contains a specific {@link IoHandlerCommand}
328      * 
329      * @param commandType The type of {@link IoHandlerCommand} we are looking for
330      * @return <tt>TRUE</tt> if the {@link IoHandlerCommand} is found in the chain
331      */
332     public boolean contains(Class<? extends IoHandlerCommand> commandType) {
333         Entry e = head.nextEntry;
334         
335         while (e != tail) {
336             if (commandType.isAssignableFrom(e.getCommand().getClass())) {
337                 return true;
338             }
339             
340             e = e.nextEntry;
341         }
342         
343         return false;
344     }
345 
346     /**
347      * {@inheritDoc}
348      */
349     @Override
350     public String toString() {
351         StringBuilder buf = new StringBuilder();
352         buf.append("{ ");
353 
354         boolean empty = true;
355 
356         Entry e = head.nextEntry;
357         
358         while (e != tail) {
359             if (!empty) {
360                 buf.append(", ");
361             } else {
362                 empty = false;
363             }
364 
365             buf.append('(');
366             buf.append(e.getName());
367             buf.append(':');
368             buf.append(e.getCommand());
369             buf.append(')');
370 
371             e = e.nextEntry;
372         }
373 
374         if (empty) {
375             buf.append("empty");
376         }
377 
378         buf.append(" }");
379 
380         return buf.toString();
381     }
382 
383     /**
384      * Represents a name-command pair that an {@link IoHandlerChain} contains.
385      *
386      * @author <a href="http://mina.apache.org">Apache MINA Project</a>
387      */
388     public class Entry {
389         private Entry prevEntry;
390 
391         private Entry nextEntry;
392 
393         private final String name;
394 
395         private final IoHandlerCommand command;
396 
397         private final NextCommand nextCommand;
398 
399         private Entry(Entry prevEntry, Entry nextEntry, String name, IoHandlerCommand command) {
400             if (command == null) {
401                 throw new IllegalArgumentException("command");
402             }
403             
404             if (name == null) {
405                 throw new IllegalArgumentException("name");
406             }
407 
408             this.prevEntry = prevEntry;
409             this.nextEntry = nextEntry;
410             this.name = name;
411             this.command = command;
412             this.nextCommand = new NextCommand() {
413                 /**
414                  * {@inheritDoc}
415                  */
416                 @Override
417                 public void execute(IoSession session, Object message) throws Exception {
418                     callNextCommand(Entry.this.nextEntry, session, message);
419                 }
420             };
421         }
422 
423         /**
424          * @return the name of the command.
425          */
426         public String getName() {
427             return name;
428         }
429 
430         /**
431          * @return the command.
432          */
433         public IoHandlerCommand getCommand() {
434             return command;
435         }
436 
437         /**
438          * @return the {@link IoHandlerCommand.NextCommand} of the command.
439          */
440         public NextCommand getNextCommand() {
441             return nextCommand;
442         }
443     }
444 }