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 }