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.ArrayList;
23  import java.util.List;
24  
25  import org.apache.mina.common.IdleStatus;
26  import org.apache.mina.common.IoFilterAdapter;
27  import org.apache.mina.common.IoFilterChain;
28  import org.apache.mina.common.IoSession;
29  import org.apache.mina.common.ThreadModel;
30  import org.apache.mina.util.ByteBufferUtil;
31  import org.slf4j.Logger;
32  import org.slf4j.LoggerFactory;
33  
34  import edu.emory.mathcs.backport.java.util.concurrent.Executor;
35  import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue;
36  import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor;
37  import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit;
38  
39  /**
40   * A filter that forward events to {@link Executor} in
41   * <a href="http://dcl.mathcs.emory.edu/util/backport-util-concurrent/">backport-util-concurrent</a>.
42   * You can apply various thread model by inserting this filter to the {@link IoFilterChain}.
43   * This filter is usually inserted by {@link ThreadModel} automatically, so you don't need
44   * to add this filter in most cases.
45   * <p>
46   * Please note that this filter doesn't manage the life cycle of the underlying
47   * {@link Executor}.  You have to destroy or stop it by yourself.
48   *
49   * <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
50   * @version $Rev: 350169 $, $Date: 2005-12-01 00:17:41 -0500 (Thu, 01 Dec 2005) $
51   */
52  public class ExecutorFilter extends IoFilterAdapter {
53      private final Logger logger = LoggerFactory.getLogger(getClass());
54  
55      private final Executor executor;
56  
57      /**
58       * Creates a new instace with the default thread pool implementation
59       * (<tt>new ThreadPoolExecutor(16, 16, 60, TimeUnit.SECONDS, new LinkedBlockingQueue() )</tt>).
60       */
61      public ExecutorFilter() {
62          this(new ThreadPoolExecutor(16, 16, 60, TimeUnit.SECONDS,
63                  new LinkedBlockingQueue()));
64      }
65  
66      /**
67       * Creates a new instance with the specified <tt>executor</tt>.
68       */
69      public ExecutorFilter(Executor executor) {
70          if (executor == null) {
71              throw new NullPointerException("executor");
72          }
73  
74          this.executor = executor;
75      }
76  
77      /**
78       * Returns the underlying {@link Executor} instance this filter uses.
79       */
80      public Executor getExecutor() {
81          return executor;
82      }
83  
84      private void fireEvent(NextFilter nextFilter, IoSession session,
85              EventType type, Object data) {
86          Event event = new Event(type, nextFilter, data);
87          SessionBuffer buf = SessionBuffer.getSessionBuffer(session);
88  
89          synchronized (buf.eventQueue) {
90              buf.eventQueue.add(event);
91              if (buf.processingCompleted) {
92                  buf.processingCompleted = false;
93                  if (logger.isDebugEnabled()) {
94                      logger.debug("Launching thread for "
95                              + session.getRemoteAddress());
96                  }
97  
98                  executor.execute(new ProcessEventsRunnable(buf));
99              }
100         }
101     }
102 
103     private static class SessionBuffer {
104         private static final String KEY = SessionBuffer.class.getName()
105                 + ".KEY";
106 
107         private static SessionBuffer getSessionBuffer(IoSession session) {
108             synchronized (session) {
109                 SessionBuffer buf = (SessionBuffer) session.getAttribute(KEY);
110                 if (buf == null) {
111                     buf = new SessionBuffer(session);
112                     session.setAttribute(KEY, buf);
113                 }
114                 return buf;
115             }
116         }
117 
118         private final IoSession session;
119 
120         private final List eventQueue = new ArrayList();
121 
122         private boolean processingCompleted = true;
123 
124         private SessionBuffer(IoSession session) {
125             this.session = session;
126         }
127     }
128 
129     protected static class EventType {
130         public static final EventType OPENED = new EventType("OPENED");
131 
132         public static final EventType CLOSED = new EventType("CLOSED");
133 
134         public static final EventType READ = new EventType("READ");
135 
136         public static final EventType WRITTEN = new EventType("WRITTEN");
137 
138         public static final EventType RECEIVED = new EventType("RECEIVED");
139 
140         public static final EventType SENT = new EventType("SENT");
141 
142         public static final EventType IDLE = new EventType("IDLE");
143 
144         public static final EventType EXCEPTION = new EventType("EXCEPTION");
145 
146         private final String value;
147 
148         private EventType(String value) {
149             this.value = value;
150         }
151 
152         public String toString() {
153             return value;
154         }
155     }
156 
157     protected static class Event {
158         private final EventType type;
159 
160         private final NextFilter nextFilter;
161 
162         private final Object data;
163 
164         Event(EventType type, NextFilter nextFilter, Object data) {
165             this.type = type;
166             this.nextFilter = nextFilter;
167             this.data = data;
168         }
169 
170         public Object getData() {
171             return data;
172         }
173 
174         public NextFilter getNextFilter() {
175             return nextFilter;
176         }
177 
178         public EventType getType() {
179             return type;
180         }
181     }
182 
183     public void sessionCreated(NextFilter nextFilter, IoSession session) {
184         nextFilter.sessionCreated(session);
185     }
186 
187     public void sessionOpened(NextFilter nextFilter, IoSession session) {
188         fireEvent(nextFilter, session, EventType.OPENED, null);
189     }
190 
191     public void sessionClosed(NextFilter nextFilter, IoSession session) {
192         fireEvent(nextFilter, session, EventType.CLOSED, null);
193     }
194 
195     public void sessionIdle(NextFilter nextFilter, IoSession session,
196             IdleStatus status) {
197         fireEvent(nextFilter, session, EventType.IDLE, status);
198     }
199 
200     public void exceptionCaught(NextFilter nextFilter, IoSession session,
201             Throwable cause) {
202         fireEvent(nextFilter, session, EventType.EXCEPTION, cause);
203     }
204 
205     public void messageReceived(NextFilter nextFilter, IoSession session,
206             Object message) {
207         ByteBufferUtil.acquireIfPossible(message);
208         fireEvent(nextFilter, session, EventType.RECEIVED, message);
209     }
210 
211     public void messageSent(NextFilter nextFilter, IoSession session,
212             Object message) {
213         ByteBufferUtil.acquireIfPossible(message);
214         fireEvent(nextFilter, session, EventType.SENT, message);
215     }
216 
217     protected void processEvent(NextFilter nextFilter, IoSession session,
218             EventType type, Object data) {
219         if (type == EventType.RECEIVED) {
220             nextFilter.messageReceived(session, data);
221             ByteBufferUtil.releaseIfPossible(data);
222         } else if (type == EventType.SENT) {
223             nextFilter.messageSent(session, data);
224             ByteBufferUtil.releaseIfPossible(data);
225         } else if (type == EventType.EXCEPTION) {
226             nextFilter.exceptionCaught(session, (Throwable) data);
227         } else if (type == EventType.IDLE) {
228             nextFilter.sessionIdle(session, (IdleStatus) data);
229         } else if (type == EventType.OPENED) {
230             nextFilter.sessionOpened(session);
231         } else if (type == EventType.CLOSED) {
232             nextFilter.sessionClosed(session);
233         }
234     }
235 
236     public void filterWrite(NextFilter nextFilter, IoSession session,
237             WriteRequest writeRequest) {
238         nextFilter.filterWrite(session, writeRequest);
239     }
240 
241     public void filterClose(NextFilter nextFilter, IoSession session)
242             throws Exception {
243         nextFilter.filterClose(session);
244     }
245 
246     private class ProcessEventsRunnable implements Runnable {
247         private final SessionBuffer buffer;
248 
249         ProcessEventsRunnable(SessionBuffer buffer) {
250             this.buffer = buffer;
251         }
252 
253         public void run() {
254             while (true) {
255                 Event event;
256 
257                 synchronized (buffer.eventQueue) {
258                     if (buffer.eventQueue.isEmpty()) {
259                         buffer.processingCompleted = true;
260                         break;
261                     }
262 
263                     event = (Event) buffer.eventQueue.remove(0);
264                 }
265 
266                 processEvent(event.getNextFilter(), buffer.session, event
267                         .getType(), event.getData());
268             }
269 
270             if (logger.isDebugEnabled()) {
271                 logger.debug("Exiting since queue is empty for "
272                         + buffer.session.getRemoteAddress());
273             }
274         }
275     }
276 }