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.filter.executor;
21  
22  import java.util.EnumSet;
23  import java.util.concurrent.Executor;
24  import java.util.concurrent.ExecutorService;
25  import java.util.concurrent.Executors;
26  import java.util.concurrent.ThreadFactory;
27  import java.util.concurrent.TimeUnit;
28  
29  import org.apache.mina.core.filterchain.IoFilterAdapter;
30  import org.apache.mina.core.filterchain.IoFilterChain;
31  import org.apache.mina.core.filterchain.IoFilterEvent;
32  import org.apache.mina.core.session.IdleStatus;
33  import org.apache.mina.core.session.IoEventType;
34  import org.apache.mina.core.session.IoSession;
35  import org.apache.mina.core.write.WriteRequest;
36  
37  /**
38   * A filter that forwards I/O events to {@link Executor} to enforce a certain
39   * thread model while allowing the events per session to be processed
40   * simultaneously. You can apply various thread model by inserting this filter
41   * to a {@link IoFilterChain}.
42   * 
43   * <h2>Life Cycle Management</h2>
44   * 
45   * Please note that this filter doesn't manage the life cycle of the {@link Executor}.
46   * If you created this filter using {@link #ExecutorFilter(Executor)} or similar
47   * constructor that accepts an {@link Executor} that you've instantiated, you have
48   * full control and responsibility of managing its life cycle (e.g. calling
49   * {@link ExecutorService#shutdown()}.
50   * <p> 
51   * If you created this filter using convenience constructors like
52   * {@link #ExecutorFilter(int)}, then you can shut down the executor by calling
53   * {@link #destroy()} explicitly.
54   * 
55   * <h2>Event Ordering</h2>
56   * 
57   * All convenience constructors of this filter creates a new
58   * {@link OrderedThreadPoolExecutor} instance.  Therefore, the order of event is
59   * maintained like the following:
60   * <ul>
61   * <li>All event handler methods are called exclusively.
62   *     (e.g. messageReceived and messageSent can't be invoked at the same time.)</li>
63   * <li>The event order is never mixed up.
64   *     (e.g. messageReceived is always invoked before sessionClosed or messageSent.)</li>
65   * </ul>
66   * However, if you specified other {@link Executor} instance in the constructor,
67   * the order of events are not maintained at all.  This means more than one event
68   * handler methods can be invoked at the same time with mixed order.  For example,
69   * let's assume that messageReceived, messageSent, and sessionClosed events are
70   * fired.
71   * <ul>
72   * <li>All event handler methods can be called simultaneously.
73   *     (e.g. messageReceived and messageSent can be invoked at the same time.)</li>
74   * <li>The event order can be mixed up.
75   *     (e.g. sessionClosed or messageSent can be invoked before messageReceived
76   *           is invoked.)</li>
77   * </ul>
78   * If you need to maintain the order of events per session, please specify an
79   * {@link OrderedThreadPoolExecutor} instance or use the convenience constructors.
80   * 
81   * <h2>Selective Filtering</h2>
82   * 
83   * By default, all event types but <tt>sessionCreated</tt>, <tt>filterWrite</tt>,
84   * <tt>filterClose</tt> and <tt>filterSetTrafficMask</tt> are submitted to the
85   * underlying executor, which is most common setting.
86   * <p>
87   * If you want to submit only a certain set of event types, you can specify them
88   * in the constructor.  For example, you could configure a thread pool for
89   * write operation for the maximum performance:
90   * <pre><code>
91   * IoService service = ...;
92   * DefaultIoFilterChainBuilder chain = service.getFilterChain();
93   * 
94   * chain.addLast("codec", new ProtocolCodecFilter(...));
95   * // Use one thread pool for most events.
96   * chain.addLast("executor1", new ExecutorFilter());
97   * // and another dedicated thread pool for 'filterWrite' events.
98   * chain.addLast("executor2", new ExecutorFilter(IoEventType.WRITE));
99   * </code></pre>
100  * 
101  * <h2>Preventing {@link OutOfMemoryError}</h2>
102  * 
103  * Please refer to {@link IoEventQueueThrottle}, which is specified as
104  * a parameter of the convenience constructors.
105  * 
106  * @author The Apache MINA Project (dev@mina.apache.org)
107  * @version $Rev: 706043 $, $Date: 2008-10-19 18:14:11 +0200 (Sun, 19 Oct 2008) $
108  * 
109  * @see OrderedThreadPoolExecutor
110  * @see UnorderedThreadPoolExecutor
111  * @org.apache.xbean.XBean
112  */
113 public class ExecutorFilter extends IoFilterAdapter {
114     /** The list of handled events */
115     private EnumSet<IoEventType> eventTypes;
116     
117     /** The associated executor */
118     private Executor executor;
119     
120     /** A flag set if the executor can be managed */ 
121     private boolean manageableExecutor;
122     
123     /** The default pool size */
124     private static final int DEFAULT_MAX_POOL_SIZE = 16;
125     
126     /** The number of thread to create at startup */
127     private static final int BASE_THREAD_NUMBER = 0;
128     
129     /** The default KeepAlive time, in seconds */
130     private static final long DEFAULT_KEEPALIVE_TIME = 30;
131     
132     /** 
133      * A set of flags used to tell if the Executor has been created 
134      * in the constructor or passed as an argument. In the second case, 
135      * the executor state can be managed.
136      **/
137     private static final boolean MANAGEABLE_EXECUTOR = true;
138     private static final boolean NOT_MANAGEABLE_EXECUTOR = false;
139     
140     /** A list of default EventTypes to be handled by the executor */
141     private static IoEventType[] DEFAULT_EVENT_SET = new IoEventType[] {
142         IoEventType.EXCEPTION_CAUGHT,
143         IoEventType.MESSAGE_RECEIVED, 
144         IoEventType.MESSAGE_SENT,
145         IoEventType.SESSION_CLOSED, 
146         IoEventType.SESSION_IDLE,
147         IoEventType.SESSION_OPENED
148     };
149     
150 
151     /**
152      * (Convenience constructor) Creates a new instance with a new
153      * {@link OrderedThreadPoolExecutor}, no thread in the pool, and a 
154      * maximum of 16 threads in the pool. All the event will be handled 
155      * by this default executor.
156      */
157     public ExecutorFilter() {
158         // Create a new default Executor
159         Executor executor = createDefaultExecutor(
160             BASE_THREAD_NUMBER,
161             DEFAULT_MAX_POOL_SIZE,
162             DEFAULT_KEEPALIVE_TIME,
163             TimeUnit.SECONDS,
164             Executors.defaultThreadFactory(),
165             null);
166         
167         // Initialize the filter
168         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
169     }
170     
171     /**
172      * (Convenience constructor) Creates a new instance with a new
173      * {@link OrderedThreadPoolExecutor}, no thread in the pool, but 
174      * a maximum of threads in the pool is given. All the event will be handled 
175      * by this default executor.
176      * 
177      * @param maximumPoolSize The maximum number of thread the default executor can 
178      * use
179      */
180     public ExecutorFilter(int maximumPoolSize) {
181         // Create a new default Executor
182         Executor executor = createDefaultExecutor(
183             BASE_THREAD_NUMBER,
184             maximumPoolSize,
185             DEFAULT_KEEPALIVE_TIME,
186             TimeUnit.SECONDS,
187             Executors.defaultThreadFactory(),
188             null);
189         
190         // Initialize the filter
191         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
192     }
193     
194     /**
195      * (Convenience constructor) Creates a new instance with a new
196      * {@link OrderedThreadPoolExecutor}, a number of thread to start with, a  
197      * maximum of threads the pool can contain. All the event will be handled 
198      * by this default executor.
199      *
200      * @param corePoolSize the base number of threads the pool will contain at startup
201      * @param maximumPoolSize The maximum number of thread the default executor can 
202      * use
203      */
204     public ExecutorFilter(int corePoolSize, int maximumPoolSize) {
205         // Create a new default Executor
206         Executor executor = createDefaultExecutor(
207             corePoolSize,
208             maximumPoolSize,
209             DEFAULT_KEEPALIVE_TIME,
210             TimeUnit.SECONDS,
211             Executors.defaultThreadFactory(),
212             null);
213         
214         // Initialize the filter
215         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
216     }
217     
218     /**
219      * (Convenience constructor) Creates a new instance with a new
220      * {@link OrderedThreadPoolExecutor}.
221      */
222     public ExecutorFilter(int corePoolSize, int maximumPoolSize, long keepAliveTime, 
223             TimeUnit unit) {
224         // Create a new default Executor
225         Executor executor = createDefaultExecutor(
226             corePoolSize,
227             maximumPoolSize,
228             keepAliveTime,
229             unit,
230             Executors.defaultThreadFactory(),
231             null);
232         
233         // Initialize the filter
234         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
235     }
236 
237     /**
238      * (Convenience constructor) Creates a new instance with a new
239      * {@link OrderedThreadPoolExecutor}.
240      */
241     public ExecutorFilter(
242             int corePoolSize, int maximumPoolSize, 
243             long keepAliveTime, TimeUnit unit,
244             IoEventQueueHandler queueHandler) {
245         // Create a new default Executor
246         Executor executor = createDefaultExecutor(
247             corePoolSize,
248             maximumPoolSize,
249             keepAliveTime,
250             unit,
251             Executors.defaultThreadFactory(),
252             queueHandler);
253         
254         // Initialize the filter
255         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
256     }
257 
258     /**
259      * (Convenience constructor) Creates a new instance with a new
260      * {@link OrderedThreadPoolExecutor}.
261      */
262     public ExecutorFilter(
263             int corePoolSize, int maximumPoolSize, 
264             long keepAliveTime, TimeUnit unit,
265             ThreadFactory threadFactory) {
266         // Create a new default Executor
267         Executor executor = createDefaultExecutor(
268             corePoolSize,
269             maximumPoolSize,
270             keepAliveTime,
271             unit,
272             threadFactory,
273             null);
274         
275         // Initialize the filter
276         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
277     }
278 
279     /**
280      * (Convenience constructor) Creates a new instance with a new
281      * {@link OrderedThreadPoolExecutor}.
282      */
283     public ExecutorFilter(
284             int corePoolSize, int maximumPoolSize, 
285             long keepAliveTime, TimeUnit unit,
286             ThreadFactory threadFactory, IoEventQueueHandler queueHandler) {
287         // Create a new default Executor
288         Executor executor = new OrderedThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, threadFactory, queueHandler);
289         
290         // Initialize the filter
291         init(executor, MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
292     }
293 
294     /**
295      * (Convenience constructor) Creates a new instance with a new
296      * {@link OrderedThreadPoolExecutor}.
297      */
298     public ExecutorFilter(IoEventType... eventTypes) {
299         // Create a new default Executor
300         Executor executor = createDefaultExecutor(
301             BASE_THREAD_NUMBER,
302             DEFAULT_MAX_POOL_SIZE,
303             DEFAULT_KEEPALIVE_TIME,
304             TimeUnit.SECONDS,
305             Executors.defaultThreadFactory(),
306             null);
307         
308         // Initialize the filter
309         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
310     }
311     
312     /**
313      * (Convenience constructor) Creates a new instance with a new
314      * {@link OrderedThreadPoolExecutor}.
315      */
316     public ExecutorFilter(int maximumPoolSize, IoEventType... eventTypes) {
317         // Create a new default Executor
318         Executor executor = createDefaultExecutor(
319             BASE_THREAD_NUMBER,
320             maximumPoolSize,
321             DEFAULT_KEEPALIVE_TIME,
322             TimeUnit.SECONDS,
323             Executors.defaultThreadFactory(),
324             null);
325         
326         // Initialize the filter
327         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
328     }
329     
330     /**
331      * (Convenience constructor) Creates a new instance with a new
332      * {@link OrderedThreadPoolExecutor}.
333      */
334     public ExecutorFilter(int corePoolSize, int maximumPoolSize, IoEventType... eventTypes) {
335         // Create a new default Executor
336         Executor executor = createDefaultExecutor(
337             corePoolSize,
338             maximumPoolSize,
339             DEFAULT_KEEPALIVE_TIME,
340             TimeUnit.SECONDS,
341             Executors.defaultThreadFactory(),
342             null);
343         
344         // Initialize the filter
345         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
346     }
347     
348     /**
349      * (Convenience constructor) Creates a new instance with a new
350      * {@link OrderedThreadPoolExecutor}.
351      */
352     public ExecutorFilter(
353             int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, IoEventType... eventTypes) {
354         // Create a new default Executor
355         Executor executor = createDefaultExecutor(
356             corePoolSize,
357             maximumPoolSize,
358             keepAliveTime,
359             unit,
360             Executors.defaultThreadFactory(),
361             null);
362         
363         // Initialize the filter
364         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
365     }
366     
367     /**
368      * (Convenience constructor) Creates a new instance with a new
369      * {@link OrderedThreadPoolExecutor}.
370      */
371     public ExecutorFilter(
372             int corePoolSize, int maximumPoolSize, 
373             long keepAliveTime, TimeUnit unit,
374             IoEventQueueHandler queueHandler, IoEventType... eventTypes) {
375         // Create a new default Executor
376         Executor executor = createDefaultExecutor(
377             corePoolSize,
378             maximumPoolSize,
379             keepAliveTime,
380             unit,
381             Executors.defaultThreadFactory(),
382             queueHandler);
383         
384         // Initialize the filter
385         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
386     }
387 
388     /**
389      * (Convenience constructor) Creates a new instance with a new
390      * {@link OrderedThreadPoolExecutor}.
391      */
392     public ExecutorFilter(
393             int corePoolSize, int maximumPoolSize, 
394             long keepAliveTime, TimeUnit unit,
395             ThreadFactory threadFactory, IoEventType... eventTypes) {
396         // Create a new default Executor
397         Executor executor = createDefaultExecutor(
398             corePoolSize,
399             maximumPoolSize,
400             keepAliveTime,
401             unit,
402             threadFactory,
403             null);
404         
405         // Initialize the filter
406         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
407     }
408 
409     /**
410      * (Convenience constructor) Creates a new instance with a new
411      * {@link OrderedThreadPoolExecutor}.
412      * 
413      * @param corePoolSize The base number of thread in the pool
414      * @param maximumPoolSize The macimum thread contained in the executor
415      * @param keepAliveTime The KeepAlive timeout, expressed using the time unit
416      * @param unit The time unit
417      * @param threadFactory
418      * @param queueHandler
419      * @param eventTypes The list of events handled by the created executor
420      */
421     public ExecutorFilter(
422             int corePoolSize, int maximumPoolSize, 
423             long keepAliveTime, TimeUnit unit,
424             ThreadFactory threadFactory, IoEventQueueHandler queueHandler, 
425             IoEventType... eventTypes) {
426         // Create a new default Executor
427         Executor executor = new OrderedThreadPoolExecutor(corePoolSize, maximumPoolSize, 
428             keepAliveTime, unit, threadFactory, queueHandler);
429         
430         // Initialize the filter
431         init(executor, MANAGEABLE_EXECUTOR, eventTypes);
432     }
433     
434     /**
435      * Creates a new instance with the specified {@link Executor}.
436      */
437     public ExecutorFilter(Executor executor) {
438         // Initialize the filter
439         init(executor, NOT_MANAGEABLE_EXECUTOR, DEFAULT_EVENT_SET);
440     }
441 
442     /**
443      * Creates a new instance with the specified {@link Executor}.
444      */
445     public ExecutorFilter(Executor executor, IoEventType... eventTypes) {
446         // Initialize the filter
447         init(executor, NOT_MANAGEABLE_EXECUTOR, eventTypes);
448     }
449     
450     /**
451      * Create an OrderedThreadPool executor.
452      *
453      * @param corePoolSize
454      * @param maximumPoolSize
455      * @param keepAliveTime
456      * @param unit
457      * @param threadFactory
458      * @param queueHandler
459      * @return
460      */
461     private Executor createDefaultExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime,
462         TimeUnit unit, ThreadFactory threadFactory, IoEventQueueHandler queueHandler) {
463         // Create a new Executor
464         Executor executor = new OrderedThreadPoolExecutor(corePoolSize, maximumPoolSize, 
465             keepAliveTime, unit, threadFactory, queueHandler);
466         
467         return executor;
468     }
469     
470     /**
471      * Create an EnumSet from an array of EventTypes, and set the associated
472      * eventTypes field.
473      *
474      * @param eventTypes The array of handled events
475      */
476     private void initEventTypes(IoEventType... eventTypes) {
477         if (eventTypes == null || eventTypes.length == 0) {
478             eventTypes = DEFAULT_EVENT_SET;
479         }
480 
481         // Copy the list of handled events in the event set
482         this.eventTypes = EnumSet.of(eventTypes[0], eventTypes);
483         
484         // Check that we don't have the SESSION_CREATED event in the set
485         if (this.eventTypes.contains( IoEventType.SESSION_CREATED )) {
486             this.eventTypes = null;
487             throw new IllegalArgumentException(IoEventType.SESSION_CREATED
488                 + " is not allowed.");
489         }
490     }
491 
492     /**
493      * Creates a new instance of ExecutorFilter. This private constructor is called by all
494      * the public constructor.
495      *
496      * @param executor The underlying {@link Executor} in charge of managing the Thread pool.
497      * @param manageableExecutor Tells if the Executor's Life Cycle can be managed or not
498      * @param eventTypes The lit of event which are handled by the executor
499      * @param
500      */
501     private void init(Executor executor, boolean manageableExecutor, IoEventType... eventTypes) {
502         if (executor == null) {
503             throw new NullPointerException("executor");
504         }
505 
506         initEventTypes(eventTypes);
507         this.executor = executor;
508         this.manageableExecutor = manageableExecutor;
509     }
510     
511     /**
512      * Shuts down the underlying executor if this filter hase been created via
513      * a convenience constructor.
514      */
515     @Override
516     public void destroy() {
517         if (manageableExecutor) {
518             ((ExecutorService) executor).shutdown();
519         }
520     }
521 
522     /**
523      * Returns the underlying {@link Executor} instance this filter uses.
524      * 
525      * @return The underlying {@link Executor}
526      */
527     public final Executor getExecutor() {
528         return executor;
529     }
530 
531     /**
532      * Fires the specified event through the underlying executor.
533      */
534     protected void fireEvent(IoFilterEvent event) {
535         getExecutor().execute(event);
536     }
537 
538     /**
539      * A trigger fired when adding this filter in a chain. As this filter can be
540      * added only once in a chain, if the chain already contains the same filter,
541      * and exception will be thrown.
542      * 
543      * @param parent The chain in which we want to inject this filter
544      * @param name The Fitler's name
545      * @param nextFilter The next filter in the chain
546      * 
547      * @throws IllegalArgumentException If the filter is already present in the chain
548      */
549     @Override
550     public void onPreAdd(IoFilterChain parent, String name,
551             NextFilter nextFilter) throws Exception {
552         if (parent.contains(this)) {
553             throw new IllegalArgumentException(
554                     "You can't add the same filter instance more than once.  Create another instance and add it.");
555         }
556     }
557 
558     @Override
559     public final void sessionOpened(NextFilter nextFilter, IoSession session) {
560         if (eventTypes.contains(IoEventType.SESSION_OPENED)) {
561             fireEvent(new IoFilterEvent(nextFilter, IoEventType.SESSION_OPENED,
562                     session, null));
563         } else {
564             nextFilter.sessionOpened(session);
565         }
566     }
567 
568     @Override
569     public final void sessionClosed(NextFilter nextFilter, IoSession session) {
570         if (eventTypes.contains(IoEventType.SESSION_CLOSED)) {
571             fireEvent(new IoFilterEvent(nextFilter, IoEventType.SESSION_CLOSED,
572                     session, null));
573         } else {
574             nextFilter.sessionClosed(session);
575         }
576     }
577 
578     @Override
579     public final void sessionIdle(NextFilter nextFilter, IoSession session,
580             IdleStatus status) {
581         if (eventTypes.contains(IoEventType.SESSION_IDLE)) {
582             fireEvent(new IoFilterEvent(nextFilter, IoEventType.SESSION_IDLE,
583                     session, status));
584         } else {
585             nextFilter.sessionIdle(session, status);
586         }
587     }
588 
589     @Override
590     public final void exceptionCaught(NextFilter nextFilter, IoSession session,
591             Throwable cause) {
592         if (eventTypes.contains(IoEventType.EXCEPTION_CAUGHT)) {
593             fireEvent(new IoFilterEvent(nextFilter,
594                     IoEventType.EXCEPTION_CAUGHT, session, cause));
595         } else {
596             nextFilter.exceptionCaught(session, cause);
597         }
598     }
599 
600     @Override
601     public final void messageReceived(NextFilter nextFilter, IoSession session,
602             Object message) {
603         if (eventTypes.contains(IoEventType.MESSAGE_RECEIVED)) {
604             fireEvent(new IoFilterEvent(nextFilter,
605                     IoEventType.MESSAGE_RECEIVED, session, message));
606         } else {
607             nextFilter.messageReceived(session, message);
608         }
609     }
610 
611     @Override
612     public final void messageSent(NextFilter nextFilter, IoSession session,
613             WriteRequest writeRequest) {
614         if (eventTypes.contains(IoEventType.MESSAGE_SENT)) {
615             fireEvent(new IoFilterEvent(nextFilter, IoEventType.MESSAGE_SENT,
616                     session, writeRequest));
617         } else {
618             nextFilter.messageSent(session, writeRequest);
619         }
620     }
621 
622     @Override
623     public final void filterWrite(NextFilter nextFilter, IoSession session,
624             WriteRequest writeRequest) {
625         if (eventTypes.contains(IoEventType.WRITE)) {
626             fireEvent(new IoFilterEvent(nextFilter, IoEventType.WRITE, session,
627                     writeRequest));
628         } else {
629             nextFilter.filterWrite(session, writeRequest);
630         }
631     }
632 
633     @Override
634     public final void filterClose(NextFilter nextFilter, IoSession session)
635             throws Exception {
636         if (eventTypes.contains(IoEventType.CLOSE)) {
637             fireEvent(new IoFilterEvent(nextFilter, IoEventType.CLOSE, session,
638                     null));
639         } else {
640             nextFilter.filterClose(session);
641         }
642     }
643 }