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.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: 555855 $, $Date: 2007-07-13 12:19:00 +0900 (금, 13  7월 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 name2entry = new HashMap();
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 = (Entry) 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 it = new ArrayList(name2entry.keySet()).iterator();
145         while (it.hasNext()) {
146             this.remove((String) it.next());
147         }
148     }
149 
150     private void register(EntryImpl prevEntry, String name, IoFilter filter) {
151         EntryImpl newEntry = new EntryImpl(prevEntry, prevEntry.nextEntry,
152                 name, filter);
153 
154         try {
155             filter.onPreAdd(this, name, newEntry.getNextFilter());
156         } catch (Exception e) {
157             throw new IoFilterLifeCycleException("onPreAdd(): " + name + ':'
158                     + filter + " in " + getSession(), e);
159         }
160 
161         prevEntry.nextEntry.prevEntry = newEntry;
162         prevEntry.nextEntry = newEntry;
163         name2entry.put(name, newEntry);
164 
165         try {
166             filter.onPostAdd(this, name, newEntry.getNextFilter());
167         } catch (Exception e) {
168             deregister0(newEntry);
169             throw new IoFilterLifeCycleException("onPostAdd(): " + name + ':'
170                     + filter + " in " + getSession(), e);
171         }
172     }
173 
174     private void deregister(EntryImpl entry) {
175         IoFilter filter = entry.getFilter();
176 
177         try {
178             filter.onPreRemove(this, entry.getName(), entry.getNextFilter());
179         } catch (Exception e) {
180             throw new IoFilterLifeCycleException("onPreRemove(): "
181                     + entry.getName() + ':' + filter + " in " + getSession(), e);
182         }
183 
184         deregister0(entry);
185 
186         try {
187             filter.onPostRemove(this, entry.getName(), entry.getNextFilter());
188         } catch (Exception e) {
189             throw new IoFilterLifeCycleException("onPostRemove(): "
190                     + entry.getName() + ':' + filter + " in " + getSession(), e);
191         }
192     }
193 
194     private void deregister0(EntryImpl entry) {
195         EntryImpl prevEntry = entry.prevEntry;
196         EntryImpl nextEntry = entry.nextEntry;
197         prevEntry.nextEntry = nextEntry;
198         nextEntry.prevEntry = prevEntry;
199 
200         name2entry.remove(entry.name);
201     }
202 
203     /**
204      * Throws an exception when the specified filter name is not registered in this chain.
205      *
206      * @return An filter entry with the specified name.
207      */
208     private EntryImpl checkOldName(String baseName) {
209         EntryImpl e = (EntryImpl) name2entry.get(baseName);
210         if (e == null) {
211             throw new IllegalArgumentException("Unknown filter name:"
212                     + baseName);
213         }
214         return e;
215     }
216 
217     /**
218      * Checks the specified filter name is already taken and throws an exception if already taken.
219      */
220     private void checkAddable(String name) {
221         if (name2entry.containsKey(name)) {
222             throw new IllegalArgumentException(
223                     "Other filter is using the same name '" + name + "'");
224         }
225     }
226 
227     public void fireSessionCreated(IoSession session) {
228         Entry head = this.head;
229         callNextSessionCreated(head, session);
230     }
231 
232     private void callNextSessionCreated(Entry entry, IoSession session) {
233         try {
234             entry.getFilter().sessionCreated(entry.getNextFilter(), session);
235         } catch (Throwable e) {
236             fireExceptionCaught(session, e);
237         }
238     }
239 
240     public void fireSessionOpened(IoSession session) {
241         Entry head = this.head;
242         callNextSessionOpened(head, session);
243     }
244 
245     private void callNextSessionOpened(Entry entry, IoSession session) {
246         try {
247             entry.getFilter().sessionOpened(entry.getNextFilter(), session);
248         } catch (Throwable e) {
249             fireExceptionCaught(session, e);
250         }
251     }
252 
253     public void fireSessionClosed(IoSession session) {
254         // Update future.
255         try {
256             session.getCloseFuture().setClosed();
257         } catch (Throwable t) {
258             fireExceptionCaught(session, t);
259         }
260 
261         // And start the chain.
262         Entry head = this.head;
263         callNextSessionClosed(head, session);
264     }
265 
266     private void callNextSessionClosed(Entry entry, IoSession session) {
267         try {
268             entry.getFilter().sessionClosed(entry.getNextFilter(), session);
269 
270         } catch (Throwable e) {
271             fireExceptionCaught(session, e);
272         }
273     }
274 
275     public void fireSessionIdle(IoSession session, IdleStatus status) {
276         Entry head = this.head;
277         callNextSessionIdle(head, session, status);
278     }
279 
280     private void callNextSessionIdle(Entry entry, IoSession session,
281             IdleStatus status) {
282         try {
283             entry.getFilter().sessionIdle(entry.getNextFilter(), session,
284                     status);
285         } catch (Throwable e) {
286             fireExceptionCaught(session, e);
287         }
288     }
289 
290     public void fireMessageReceived(IoSession session, Object message) {
291         Entry head = this.head;
292         callNextMessageReceived(head, session, message);
293     }
294 
295     private void callNextMessageReceived(Entry entry, IoSession session,
296             Object message) {
297         try {
298             entry.getFilter().messageReceived(entry.getNextFilter(), session,
299                     message);
300         } catch (Throwable e) {
301             fireExceptionCaught(session, e);
302         }
303     }
304 
305     public void fireMessageSent(IoSession session, WriteRequest request) {
306         try {
307             request.getFuture().setWritten(true);
308         } catch (Throwable t) {
309             fireExceptionCaught(session, t);
310         }
311 
312         Entry head = this.head;
313         callNextMessageSent(head, session, request.getMessage());
314     }
315 
316     private void callNextMessageSent(Entry entry, IoSession session,
317             Object message) {
318         try {
319             entry.getFilter().messageSent(entry.getNextFilter(), session,
320                     message);
321         } catch (Throwable e) {
322             fireExceptionCaught(session, e);
323         }
324     }
325 
326     public void fireExceptionCaught(IoSession session, Throwable cause) {
327         // Notify the related ConnectFuture
328         // if the session is created from SocketConnector.
329         ConnectFuture future = (ConnectFuture) session
330                 .removeAttribute(CONNECT_FUTURE);
331         if (future == null) {
332             Entry head = this.head;
333             callNextExceptionCaught(head, session, cause);
334         } else {
335             // Please note that this place is not the only place that
336             // calls ConnectFuture.setException().
337             future.setException(cause);
338         }
339     }
340 
341     private void callNextExceptionCaught(Entry entry, IoSession session,
342             Throwable cause) {
343         try {
344             entry.getFilter().exceptionCaught(entry.getNextFilter(), session,
345                     cause);
346         } catch (Throwable e) {
347             SessionLog.warn(session,
348                     "Unexpected exception from exceptionCaught handler.", e);
349         }
350     }
351 
352     public void fireFilterWrite(IoSession session, WriteRequest writeRequest) {
353         Entry tail = this.tail;
354         callPreviousFilterWrite(tail, session, writeRequest);
355     }
356 
357     private void callPreviousFilterWrite(Entry entry, IoSession session,
358             WriteRequest writeRequest) {
359         try {
360             entry.getFilter().filterWrite(entry.getNextFilter(), session,
361                     writeRequest);
362         } catch (Throwable e) {
363             fireExceptionCaught(session, e);
364         }
365     }
366 
367     public void fireFilterClose(IoSession session) {
368         Entry tail = this.tail;
369         callPreviousFilterClose(tail, session);
370     }
371 
372     private void callPreviousFilterClose(Entry entry, IoSession session) {
373         try {
374             entry.getFilter().filterClose(entry.getNextFilter(), session);
375         } catch (Throwable e) {
376             fireExceptionCaught(session, e);
377         }
378     }
379 
380     public List getAll() {
381         List list = new ArrayList();
382         EntryImpl e = head.nextEntry;
383         while (e != tail) {
384             list.add(e);
385             e = e.nextEntry;
386         }
387 
388         return list;
389     }
390 
391     public List getAllReversed() {
392         List list = new ArrayList();
393         EntryImpl e = tail.prevEntry;
394         while (e != head) {
395             list.add(e);
396             e = e.prevEntry;
397         }
398         return list;
399     }
400 
401     public boolean contains(String name) {
402         return getEntry(name) != null;
403     }
404 
405     public boolean contains(IoFilter filter) {
406         EntryImpl e = head.nextEntry;
407         while (e != tail) {
408             if (e.getFilter() == filter) {
409                 return true;
410             }
411             e = e.nextEntry;
412         }
413         return false;
414     }
415 
416     public boolean contains(Class filterType) {
417         EntryImpl e = head.nextEntry;
418         while (e != tail) {
419             if (filterType.isAssignableFrom(e.getFilter().getClass())) {
420                 return true;
421             }
422             e = e.nextEntry;
423         }
424         return false;
425     }
426 
427     public String toString() {
428         StringBuffer buf = new StringBuffer();
429         buf.append("{ ");
430 
431         boolean empty = true;
432 
433         EntryImpl e = head.nextEntry;
434         while (e != tail) {
435             if (!empty) {
436                 buf.append(", ");
437             } else {
438                 empty = false;
439             }
440 
441             buf.append('(');
442             buf.append(e.getName());
443             buf.append(':');
444             buf.append(e.getFilter());
445             buf.append(')');
446 
447             e = e.nextEntry;
448         }
449 
450         if (empty) {
451             buf.append("empty");
452         }
453 
454         buf.append(" }");
455 
456         return buf.toString();
457     }
458 
459     protected void finalize() throws Throwable {
460         try {
461             this.clear();
462         } finally {
463             super.finalize();
464         }
465     }
466 
467     protected abstract void doWrite(IoSession session, WriteRequest writeRequest)
468             throws Exception;
469 
470     protected abstract void doClose(IoSession session) throws Exception;
471 
472     private class HeadFilter extends IoFilterAdapter {
473         public void sessionCreated(NextFilter nextFilter, IoSession session) {
474             nextFilter.sessionCreated(session);
475         }
476 
477         public void sessionOpened(NextFilter nextFilter, IoSession session) {
478             nextFilter.sessionOpened(session);
479         }
480 
481         public void sessionClosed(NextFilter nextFilter, IoSession session) {
482             nextFilter.sessionClosed(session);
483         }
484 
485         public void sessionIdle(NextFilter nextFilter, IoSession session,
486                 IdleStatus status) {
487             nextFilter.sessionIdle(session, status);
488         }
489 
490         public void exceptionCaught(NextFilter nextFilter, IoSession session,
491                 Throwable cause) {
492             nextFilter.exceptionCaught(session, cause);
493         }
494 
495         public void messageReceived(NextFilter nextFilter, IoSession session,
496                 Object message) {
497             nextFilter.messageReceived(session, message);
498         }
499 
500         public void messageSent(NextFilter nextFilter, IoSession session,
501                 Object message) {
502             nextFilter.messageSent(session, message);
503         }
504 
505         public void filterWrite(NextFilter nextFilter, IoSession session,
506                 WriteRequest writeRequest) throws Exception {
507             if (session.getTransportType().getEnvelopeType().isAssignableFrom(
508                     writeRequest.getMessage().getClass())) {
509                 doWrite(session, writeRequest);
510             } else {
511                 throw new IllegalStateException(
512                         "Write requests must be transformed to "
513                                 + session.getTransportType().getEnvelopeType()
514                                 + ": " + writeRequest);
515             }
516         }
517 
518         public void filterClose(NextFilter nextFilter, IoSession session)
519                 throws Exception {
520             doClose(session);
521         }
522     }
523 
524     private static class TailFilter extends IoFilterAdapter {
525         public void sessionCreated(NextFilter nextFilter, IoSession session)
526                 throws Exception {
527             session.getHandler().sessionCreated(session);
528         }
529 
530         public void sessionOpened(NextFilter nextFilter, IoSession session)
531                 throws Exception {
532             try {
533                 session.getHandler().sessionOpened(session);
534             } finally {
535                 // Notify the related ConnectFuture
536                 // if the session is created from SocketConnector.
537                 ConnectFuture future = (ConnectFuture) session
538                         .removeAttribute(CONNECT_FUTURE);
539                 if (future != null) {
540                     future.setSession(session);
541                 }
542             }
543         }
544 
545         public void sessionClosed(NextFilter nextFilter, IoSession session)
546                 throws Exception {
547             try {
548                 session.getHandler().sessionClosed(session);
549             } finally {
550                 // Remove all filters.
551                 session.getFilterChain().clear();
552             }
553         }
554 
555         public void sessionIdle(NextFilter nextFilter, IoSession session,
556                 IdleStatus status) throws Exception {
557             session.getHandler().sessionIdle(session, status);
558         }
559 
560         public void exceptionCaught(NextFilter nextFilter, IoSession session,
561                 Throwable cause) throws Exception {
562             session.getHandler().exceptionCaught(session, cause);
563         }
564 
565         public void messageReceived(NextFilter nextFilter, IoSession session,
566                 Object message) throws Exception {
567             try {
568                 session.getHandler().messageReceived(session, message);
569             } finally {
570                 ByteBufferUtil.releaseIfPossible(message);
571             }
572         }
573 
574         public void messageSent(NextFilter nextFilter, IoSession session,
575                 Object message) throws Exception {
576             try {
577                 session.getHandler().messageSent(session, message);
578             } finally {
579                 ByteBufferUtil.releaseIfPossible(message);
580             }
581         }
582 
583         public void filterWrite(NextFilter nextFilter, IoSession session,
584                 WriteRequest writeRequest) throws Exception {
585             nextFilter.filterWrite(session, writeRequest);
586         }
587 
588         public void filterClose(NextFilter nextFilter, IoSession session)
589                 throws Exception {
590             nextFilter.filterClose(session);
591         }
592     }
593 
594     private class EntryImpl implements Entry {
595         private EntryImpl prevEntry;
596 
597         private EntryImpl nextEntry;
598 
599         private final String name;
600 
601         private final IoFilter filter;
602 
603         private final NextFilter nextFilter;
604 
605         private EntryImpl(EntryImpl prevEntry, EntryImpl nextEntry,
606                 String name, IoFilter filter) {
607             if (filter == null) {
608                 throw new NullPointerException("filter");
609             }
610             if (name == null) {
611                 throw new NullPointerException("name");
612             }
613 
614             this.prevEntry = prevEntry;
615             this.nextEntry = nextEntry;
616             this.name = name;
617             this.filter = filter;
618             this.nextFilter = new NextFilter() {
619                 public void sessionCreated(IoSession session) {
620                     Entry nextEntry = EntryImpl.this.nextEntry;
621                     callNextSessionCreated(nextEntry, session);
622                 }
623 
624                 public void sessionOpened(IoSession session) {
625                     Entry nextEntry = EntryImpl.this.nextEntry;
626                     callNextSessionOpened(nextEntry, session);
627                 }
628 
629                 public void sessionClosed(IoSession session) {
630                     Entry nextEntry = EntryImpl.this.nextEntry;
631                     callNextSessionClosed(nextEntry, session);
632                 }
633 
634                 public void sessionIdle(IoSession session, IdleStatus status) {
635                     Entry nextEntry = EntryImpl.this.nextEntry;
636                     callNextSessionIdle(nextEntry, session, status);
637                 }
638 
639                 public void exceptionCaught(IoSession session, Throwable cause) {
640                     Entry nextEntry = EntryImpl.this.nextEntry;
641                     callNextExceptionCaught(nextEntry, session, cause);
642                 }
643 
644                 public void messageReceived(IoSession session, Object message) {
645                     Entry nextEntry = EntryImpl.this.nextEntry;
646                     callNextMessageReceived(nextEntry, session, message);
647                 }
648 
649                 public void messageSent(IoSession session, Object message) {
650                     Entry nextEntry = EntryImpl.this.nextEntry;
651                     callNextMessageSent(nextEntry, session, message);
652                 }
653 
654                 public void filterWrite(IoSession session,
655                         WriteRequest writeRequest) {
656                     Entry nextEntry = EntryImpl.this.prevEntry;
657                     callPreviousFilterWrite(nextEntry, session, writeRequest);
658                 }
659 
660                 public void filterClose(IoSession session) {
661                     Entry nextEntry = EntryImpl.this.prevEntry;
662                     callPreviousFilterClose(nextEntry, session);
663                 }
664             };
665         }
666 
667         public String getName() {
668             return name;
669         }
670 
671         public IoFilter getFilter() {
672             return filter;
673         }
674 
675         public NextFilter getNextFilter() {
676             return nextFilter;
677         }
678 
679         public String toString() {
680             return "(" + getName() + ':' + filter + ')';
681         }
682     }
683 }