查看本类的 API文档回源码主页即时通讯网 - 即时通讯开发者社区!
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.common.support;
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.ConnectFuture;
29  import org.apache.mina.common.IdleStatus;
30  import org.apache.mina.common.IoFilter;
31  import org.apache.mina.common.IoFilterAdapter;
32  import org.apache.mina.common.IoFilterChain;
33  import org.apache.mina.common.IoFilterLifeCycleException;
34  import org.apache.mina.common.IoSession;
35  import org.apache.mina.common.IoFilter.NextFilter;
36  import org.apache.mina.common.IoFilter.WriteRequest;
37  import org.apache.mina.util.ByteBufferUtil;
38  import org.apache.mina.util.SessionLog;
39  
40  /**
41   * An abstract implementation of {@link IoFilterChain} that provides
42   * common operations for developers to implement their own transport layer.
43   * <p>
44   * The only method a developer should implement is
45   * {@link #doWrite(IoSession, IoFilter.WriteRequest)}.  This method is invoked
46   * when filter chain is evaluated for
47   * {@link IoFilter#filterWrite(NextFilter, IoSession, IoFilter.WriteRequest)} and
48   * finally to be written out.
49   *
50   * @author The Apache Directory Project (mina-dev@directory.apache.org)
51   * @version $Rev: 561238 $, $Date: 2007-07-31 15:12:43 +0900 (Tue, 31 Jul 2007) $
52   */
53  public abstract class AbstractIoFilterChain implements IoFilterChain {
54      /**
55       * A session attribute that stores a {@link ConnectFuture} related with
56       * the {@link IoSession}.  {@link AbstractIoFilterChain} clears this
57       * attribute and notifies the future when {@link #fireSessionOpened(IoSession)}
58       * or {@link #fireExceptionCaught(IoSession, Throwable)} is invoked
59       */
60      public static final String CONNECT_FUTURE = AbstractIoFilterChain.class
61              .getName()
62              + ".connectFuture";
63  
64      private final IoSession session;
65  
66      private final Map<String, Entry> name2entry = new HashMap<String, Entry>();
67  
68      private final EntryImpl head;
69  
70      private final EntryImpl tail;
71  
72      protected AbstractIoFilterChain(IoSession session) {
73          if (session == null) {
74              throw new NullPointerException("session");
75          }
76  
77          this.session = session;
78          head = new EntryImpl(null, null, "head", new HeadFilter());
79          tail = new EntryImpl(head, null, "tail", new TailFilter());
80          head.nextEntry = tail;
81      }
82  
83      public IoSession getSession() {
84          return session;
85      }
86  
87      public Entry getEntry(String name) {
88          Entry e = name2entry.get(name);
89          if (e == null) {
90              return null;
91          }
92          return e;
93      }
94  
95      public IoFilter get(String name) {
96          Entry e = getEntry(name);
97          if (e == null) {
98              return null;
99          }
100 
101         return e.getFilter();
102     }
103 
104     public NextFilter getNextFilter(String name) {
105         Entry e = getEntry(name);
106         if (e == null) {
107             return null;
108         }
109 
110         return e.getNextFilter();
111     }
112 
113     public synchronized void addFirst(String name, IoFilter filter) {
114         checkAddable(name);
115         register(head, name, filter);
116     }
117 
118     public synchronized void addLast(String name, IoFilter filter) {
119         checkAddable(name);
120         register(tail.prevEntry, name, filter);
121     }
122 
123     public synchronized void addBefore(String baseName, String name,
124             IoFilter filter) {
125         EntryImpl baseEntry = checkOldName(baseName);
126         checkAddable(name);
127         register(baseEntry.prevEntry, name, filter);
128     }
129 
130     public synchronized void addAfter(String baseName, String name,
131             IoFilter filter) {
132         EntryImpl baseEntry = checkOldName(baseName);
133         checkAddable(name);
134         register(baseEntry, name, filter);
135     }
136 
137     public synchronized IoFilter remove(String name) {
138         EntryImpl entry = checkOldName(name);
139         deregister(entry);
140         return entry.getFilter();
141     }
142 
143     public synchronized void clear() throws Exception {
144         Iterator<String> it = new ArrayList<String>(name2entry.keySet())
145                 .iterator();
146         while (it.hasNext()) {
147             this.remove(it.next());
148         }
149     }
150 
151     private void register(EntryImpl prevEntry, String name, IoFilter filter) {
152         EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry,
153                 name, filter);
154 
155         try {
156             filter.onPreAdd(this, name, newEntry.getNextFilter());
157         } catch (Exception e) {
158             throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':'
159                     + filter + " in " + getSession(), e);
160         }
161 
162         prevEntry.nextEntry.prevEntry = newEntry;
163         prevEntry.nextEntry = newEntry;
164         name2entry.put(name, newEntry);
165 
166         try {
167             filter.onPostAdd(this, name, newEntry.getNextFilter());
168         } catch (Exception e) {
169             deregister0(newEntry);
170             throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':'
171                     + filter + " in " + getSession(), e);
172         }
173     }
174 
175     private void deregister(EntryImpl entry) {
176         IoFilter filter = entry.getFilter();
177 
178         try {
179             filter.onPreRemove(this, entry.getName(), entry.getNextFilter());
180         } catch (Exception e) {
181             throw new IoFilterLifeCycleException("onPreRemove(): "
182                     + entry.getName() + ':' + filter + " in " + getSession(), e);
183         }
184 
185         deregister0(entry);
186 
187         try {
188             filter.onPostRemove(this, entry.getName(), entry.getNextFilter());
189         } catch (Exception e) {
190             throw new IoFilterLifeCycleException("onPostRemove(): "
191                     + entry.getName() + ':' + filter + " in " + getSession(), e);
192         }
193     }
194 
195     private void deregister0(EntryImpl entry) {
196         EntryImpl prevEntry = entry.prevEntry;
197         EntryImpl nextEntry = entry.nextEntry;
198         prevEntry.nextEntry = nextEntry;
199         nextEntry.prevEntry = prevEntry;
200 
201         name2entry.remove(entry.name);
202     }
203 
204     /**
205      * Throws an exception when the specified filter name is not registered in this chain.
206      *
207      * @return An filter entry with the specified name.
208      */
209     private EntryImpl checkOldName(String baseName) {
210         EntryImpl e = (EntryImpl) name2entry.get(baseName);
211         if (e == null) {
212             throw new IllegalArgumentException("Unknown filter name:"
213                     + baseName);
214         }
215         return e;
216     }
217 
218     /**
219      * Checks the specified filter name is already taken and throws an exception if already taken.
220      */
221     private void checkAddable(String name) {
222         if (name2entry.containsKey(name)) {
223             throw new IllegalArgumentException(
224                     "Other filter is using the same name '" + name + "'");
225         }
226     }
227 
228     public void fireSessionCreated(IoSession session) {
229         Entry head = this.head;
230         callNextSessionCreated(head, session);
231     }
232 
233     private void callNextSessionCreated(Entry entry, IoSession session) {
234         try {
235             entry.getFilter().sessionCreated(entry.getNextFilter(), session);
236         } catch (Throwable e) {
237             fireExceptionCaught(session, e);
238         }
239     }
240 
241     public void fireSessionOpened(IoSession session) {
242         Entry head = this.head;
243         callNextSessionOpened(head, session);
244     }
245 
246     private void callNextSessionOpened(Entry entry, IoSession session) {
247         try {
248             entry.getFilter().sessionOpened(entry.getNextFilter(), session);
249         } catch (Throwable e) {
250             fireExceptionCaught(session, e);
251         }
252     }
253 
254     public void fireSessionClosed(IoSession session) {
255         // Update future.
256         try {
257             session.getCloseFuture().setClosed();
258         } catch (Throwable t) {
259             fireExceptionCaught(session, t);
260         }
261 
262         // And start the chain.
263         Entry head = this.head;
264         callNextSessionClosed(head, session);
265     }
266 
267     private void callNextSessionClosed(Entry entry, IoSession session) {
268         try {
269             entry.getFilter().sessionClosed(entry.getNextFilter(), session);
270 
271         } catch (Throwable e) {
272             fireExceptionCaught(session, e);
273         }
274     }
275 
276     public void fireSessionIdle(IoSession session, IdleStatus status) {
277         Entry head = this.head;
278         callNextSessionIdle(head, session, status);
279     }
280 
281     private void callNextSessionIdle(Entry entry, IoSession session,
282             IdleStatus status) {
283         try {
284             entry.getFilter().sessionIdle(entry.getNextFilter(), session,
285                     status);
286         } catch (Throwable e) {
287             fireExceptionCaught(session, e);
288         }
289     }
290 
291     public void fireMessageReceived(IoSession session, Object message) {
292         Entry head = this.head;
293         callNextMessageReceived(head, session, message);
294     }
295 
296     private void callNextMessageReceived(Entry entry, IoSession session,
297             Object message) {
298         try {
299             entry.getFilter().messageReceived(entry.getNextFilter(), session,
300                     message);
301         } catch (Throwable e) {
302             fireExceptionCaught(session, e);
303         }
304     }
305 
306     public void fireMessageSent(IoSession session, WriteRequest request) {
307         try {
308             request.getFuture().setWritten(true);
309         } catch (Throwable t) {
310             fireExceptionCaught(session, t);
311         }
312 
313         Entry head = this.head;
314         callNextMessageSent(head, session, request.getMessage());
315     }
316 
317     private void callNextMessageSent(Entry entry, IoSession session,
318             Object message) {
319         try {
320             entry.getFilter().messageSent(entry.getNextFilter(), session,
321                     message);
322         } catch (Throwable e) {
323             fireExceptionCaught(session, e);
324         }
325     }
326 
327     public void fireExceptionCaught(IoSession session, Throwable cause) {
328         // Notify the related ConnectFuture
329         // if the session is created from SocketConnector.
330         ConnectFuture future = (ConnectFuture) session
331                 .removeAttribute(CONNECT_FUTURE);
332         if (future == null) {
333             Entry head = this.head;
334             callNextExceptionCaught(head, session, cause);
335         } else {
336             // Please note that this place is not the only place that
337             // calls ConnectFuture.setException().
338             future.setException(cause);
339         }
340     }
341 
342     private void callNextExceptionCaught(Entry entry, IoSession session,
343             Throwable cause) {
344         try {
345             entry.getFilter().exceptionCaught(entry.getNextFilter(), session,
346                     cause);
347         } catch (Throwable e) {
348             SessionLog.warn(session,
349                     "Unexpected exception from exceptionCaught handler.", e);
350         }
351     }
352 
353     public void fireFilterWrite(IoSession session, WriteRequest writeRequest) {
354         Entry tail = this.tail;
355         callPreviousFilterWrite(tail, session, writeRequest);
356     }
357 
358     private void callPreviousFilterWrite(Entry entry, IoSession session,
359             WriteRequest writeRequest) {
360         try {
361             entry.getFilter().filterWrite(entry.getNextFilter(), session,
362                     writeRequest);
363         } catch (Throwable e) {
364             writeRequest.getFuture().setWritten(false);
365             fireExceptionCaught(session, e);
366         }
367     }
368 
369     public void fireFilterClose(IoSession session) {
370         Entry tail = this.tail;
371         callPreviousFilterClose(tail, session);
372     }
373 
374     private void callPreviousFilterClose(Entry entry, IoSession session) {
375         try {
376             entry.getFilter().filterClose(entry.getNextFilter(), session);
377         } catch (Throwable e) {
378             fireExceptionCaught(session, e);
379         }
380     }
381 
382     public List<Entry> getAll() {
383         List<Entry> list = new ArrayList<Entry>();
384         EntryImpl e = head.nextEntry;
385         while (e != tail) {
386             list.add(e);
387             e = e.nextEntry;
388         }
389 
390         return list;
391     }
392 
393     public List<Entry> getAllReversed() {
394         List<Entry> list = new ArrayList<Entry>();
395         EntryImpl e = tail.prevEntry;
396         while (e != head) {
397             list.add(e);
398             e = e.prevEntry;
399         }
400         return list;
401     }
402 
403     public boolean contains(String name) {
404         return getEntry(name) != null;
405     }
406 
407     public boolean contains(IoFilter filter) {
408         EntryImpl e = head.nextEntry;
409         while (e != tail) {
410             if (e.getFilter() == filter) {
411                 return true;
412             }
413             e = e.nextEntry;
414         }
415         return false;
416     }
417 
418     public boolean contains(Class<? extends IoFilter> filterType) {
419         EntryImpl e = head.nextEntry;
420         while (e != tail) {
421             if (filterType.isAssignableFrom(e.getFilter().getClass())) {
422                 return true;
423             }
424             e = e.nextEntry;
425         }
426         return false;
427     }
428 
429     public String toString() {
430         StringBuffer buf = new StringBuffer();
431         buf.append("{ ");
432 
433         boolean empty = true;
434 
435         EntryImpl e = head.nextEntry;
436         while (e != tail) {
437             if (!empty) {
438                 buf.append(", ");
439             } else {
440                 empty = false;
441             }
442 
443             buf.append('(');
444             buf.append(e.getName());
445             buf.append(':');
446             buf.append(e.getFilter());
447             buf.append(')');
448 
449             e = e.nextEntry;
450         }
451 
452         if (empty) {
453             buf.append("empty");
454         }
455 
456         buf.append(" }");
457 
458         return buf.toString();
459     }
460 
461     protected void finalize() throws Throwable {
462         try {
463             this.clear();
464         } finally {
465             super.finalize();
466         }
467     }
468 
469     protected abstract void doWrite(IoSession session, WriteRequest writeRequest)
470             throws Exception;
471 
472     protected abstract void doClose(IoSession session) throws Exception;
473 
474     private class HeadFilter extends IoFilterAdapter {
475         public void sessionCreated(NextFilter nextFilter, IoSession session) {
476             nextFilter.sessionCreated(session);
477         }
478 
479         public void sessionOpened(NextFilter nextFilter, IoSession session) {
480             nextFilter.sessionOpened(session);
481         }
482 
483         public void sessionClosed(NextFilter nextFilter, IoSession session) {
484             nextFilter.sessionClosed(session);
485         }
486 
487         public void sessionIdle(NextFilter nextFilter, IoSession session,
488                 IdleStatus status) {
489             nextFilter.sessionIdle(session, status);
490         }
491 
492         public void exceptionCaught(NextFilter nextFilter, IoSession session,
493                 Throwable cause) {
494             nextFilter.exceptionCaught(session, cause);
495         }
496 
497         public void messageReceived(NextFilter nextFilter, IoSession session,
498                 Object message) {
499             nextFilter.messageReceived(session, message);
500         }
501 
502         public void messageSent(NextFilter nextFilter, IoSession session,
503                 Object message) {
504             nextFilter.messageSent(session, message);
505         }
506 
507         public void filterWrite(NextFilter nextFilter, IoSession session,
508                 WriteRequest writeRequest) throws Exception {
509             if (session.getTransportType().getEnvelopeType().isAssignableFrom(
510                     writeRequest.getMessage().getClass())) {
511                 doWrite(session, writeRequest);
512             } else {
513                 throw new IllegalStateException(
514                         "Write requests must be transformed to "
515                                 + session.getTransportType().getEnvelopeType()
516                                 + ": " + writeRequest);
517             }
518         }
519 
520         public void filterClose(NextFilter nextFilter, IoSession session)
521                 throws Exception {
522             doClose(session);
523         }
524     }
525 
526     private static class TailFilter extends IoFilterAdapter {
527         public void sessionCreated(NextFilter nextFilter, IoSession session)
528                 throws Exception {
529             session.getHandler().sessionCreated(session);
530         }
531 
532         public void sessionOpened(NextFilter nextFilter, IoSession session)
533                 throws Exception {
534             try {
535                 session.getHandler().sessionOpened(session);
536             } finally {
537                 // Notify the related ConnectFuture
538                 // if the session is created from SocketConnector.
539                 ConnectFuture future = (ConnectFuture) session
540                         .removeAttribute(CONNECT_FUTURE);
541                 if (future != null) {
542                     future.setSession(session);
543                 }
544             }
545         }
546 
547         public void sessionClosed(NextFilter nextFilter, IoSession session)
548                 throws Exception {
549             try {
550                 session.getHandler().sessionClosed(session);
551             } finally {
552                 // Remove all filters.
553                 session.getFilterChain().clear();
554             }
555         }
556 
557         public void sessionIdle(NextFilter nextFilter, IoSession session,
558                 IdleStatus status) throws Exception {
559             session.getHandler().sessionIdle(session, status);
560         }
561 
562         public void exceptionCaught(NextFilter nextFilter, IoSession session,
563                 Throwable cause) throws Exception {
564             session.getHandler().exceptionCaught(session, cause);
565         }
566 
567         public void messageReceived(NextFilter nextFilter, IoSession session,
568                 Object message) throws Exception {
569             try {
570                 session.getHandler().messageReceived(session, message);
571             } finally {
572                 ByteBufferUtil.releaseIfPossible(message);
573             }
574         }
575 
576         public void messageSent(NextFilter nextFilter, IoSession session,
577                 Object message) throws Exception {
578             try {
579                 session.getHandler().messageSent(session, message);
580             } finally {
581                 ByteBufferUtil.releaseIfPossible(message);
582             }
583         }
584 
585         public void filterWrite(NextFilter nextFilter, IoSession session,
586                 WriteRequest writeRequest) throws Exception {
587             nextFilter.filterWrite(session, writeRequest);
588         }
589 
590         public void filterClose(NextFilter nextFilter, IoSession session)
591                 throws Exception {
592             nextFilter.filterClose(session);
593         }
594     }
595 
596     private class EntryImpl implements Entry {
597         private EntryImpl prevEntry;
598 
599         private EntryImpl nextEntry;
600 
601         private final String name;
602 
603         private final IoFilter filter;
604 
605         private final NextFilter nextFilter;
606 
607         private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry,
608                 String name, IoFilter filter) {
609             if (filter == null) {
610                 throw new NullPointerException("filter");
611             }
612             if (name == null) {
613                 throw new NullPointerException("name");
614             }
615 
616             this.prevEntry = prevEntry;
617             this.nextEntry = nextEntry;
618             this.name = name;
619             this.filter = filter;
620             this.nextFilter = new NextFilter() {
621                 public void sessionCreated(IoSession session) {
622                     Entry nextEntry = EntryImpl.this.nextEntry;
623                     callNextSessionCreated(nextEntry, session);
624                 }
625 
626                 public void sessionOpened(IoSession session) {
627                     Entry nextEntry = EntryImpl.this.nextEntry;
628                     callNextSessionOpened(nextEntry, session);
629                 }
630 
631                 public void sessionClosed(IoSession session) {
632                     Entry nextEntry = EntryImpl.this.nextEntry;
633                     callNextSessionClosed(nextEntry, session);
634                 }
635 
636                 public void sessionIdle(IoSession session, IdleStatus status) {
637                     Entry nextEntry = EntryImpl.this.nextEntry;
638                     callNextSessionIdle(nextEntry, session, status);
639                 }
640 
641                 public void exceptionCaught(IoSession session, Throwable cause) {
642                     Entry nextEntry = EntryImpl.this.nextEntry;
643                     callNextExceptionCaught(nextEntry, session, cause);
644                 }
645 
646                 public void messageReceived(IoSession session, Object message) {
647                     Entry nextEntry = EntryImpl.this.nextEntry;
648                     callNextMessageReceived(nextEntry, session, message);
649                 }
650 
651                 public void messageSent(IoSession session, Object message) {
652                     Entry nextEntry = EntryImpl.this.nextEntry;
653                     callNextMessageSent(nextEntry, session, message);
654                 }
655 
656                 public void filterWrite(IoSession session,
657                         WriteRequest writeRequest) {
658                     Entry nextEntry = EntryImpl.this.prevEntry;
659                     callPreviousFilterWrite(nextEntry, session, writeRequest);
660                 }
661 
662                 public void filterClose(IoSession session) {
663                     Entry nextEntry = EntryImpl.this.prevEntry;
664                     callPreviousFilterClose(nextEntry, session);
665                 }
666             };
667         }
668 
669         public String getName() {
670             return name;
671         }
672 
673         public IoFilter getFilter() {
674             return filter;
675         }
676 
677         public NextFilter getNextFilter() {
678             return nextFilter;
679         }
680 
681         public String toString() {
682             return "(" + getName() + ':' + filter + ')';
683         }
684     }
685 }