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;
21  
22  import java.util.Iterator;
23  import java.util.Set;
24  import java.util.concurrent.Executors;
25  import java.util.concurrent.ScheduledExecutorService;
26  import java.util.concurrent.TimeUnit;
27  
28  import org.apache.mina.util.ConcurrentHashSet;
29  import org.apache.mina.util.NamePreservingRunnable;
30  
31  /**
32   * Detects idle sessions and fires <tt>sessionIdle</tt> events to them.
33   *
34   * @author The Apache MINA Project (dev@mina.apache.org)
35   * @version $Rev: 525369 $, $Date: 2007-04-04 05:05:11 +0200 (mer., 04 avr. 2007) $
36   */
37  public class IdleStatusChecker {
38      private static final IdleStatusChecker INSTANCE = new IdleStatusChecker();
39  
40      public static IdleStatusChecker getInstance() {
41          return INSTANCE;
42      }
43  
44      private final Set<AbstractIoSession> sessions =
45          new ConcurrentHashSet<AbstractIoSession>();
46      private final Set<AbstractIoService> services =
47          new ConcurrentHashSet<AbstractIoService>();
48  
49      private final Object lock = new Object();
50      private final Runnable notifyingTask = new NamePreservingRunnable(
51              new NotifyingTask(), "IdleStatusChecker");
52      private final IoFutureListener<IoFuture> sessionCloseListener =
53          new SessionCloseListener();
54      private volatile ScheduledExecutorService executor;
55  
56      private IdleStatusChecker() {}
57  
58      public void addSession(AbstractIoSession session) {
59          synchronized (lock) {
60              boolean start = false;
61              if (sessions.isEmpty() && services.isEmpty()) {
62                  start = true;
63              }
64              if (!sessions.add(session)) {
65                  return;
66              }
67              if (start) {
68                  start();
69              }
70          }
71          
72          session.getCloseFuture().addListener(sessionCloseListener);
73      }
74      
75      public void addService(AbstractIoService service) {
76          synchronized (lock) {
77              boolean start = false;
78              if (sessions.isEmpty() && services.isEmpty()) {
79                  start = true;
80              }
81              if (!services.add(service)) {
82                  return;
83              }
84              if (start) {
85                  start();
86              }
87          }
88      }
89  
90      public void removeSession(AbstractIoSession session) {
91          synchronized (lock) {
92              sessions.remove(session);
93              if (sessions.isEmpty() && services.isEmpty()) {
94                  stop();
95              }
96          }
97      }
98      
99      public void removeService(AbstractIoService service) {
100         synchronized (lock) {
101             services.remove(service);
102             if (sessions.isEmpty() && services.isEmpty()) {
103                 stop();
104             }
105         }
106     }
107 
108     private void start() {
109         ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
110         this.executor = executor;
111         executor.scheduleWithFixedDelay(
112                 notifyingTask, 1000, 1000, TimeUnit.MILLISECONDS);
113     }
114     
115     private void stop() {
116         ScheduledExecutorService executor = this.executor;
117         if (executor == null) {
118             return;
119         }
120         executor.shutdownNow();
121         this.executor = null;
122     }
123 
124     private class NotifyingTask implements Runnable {
125         public void run() {
126             long currentTime = System.currentTimeMillis();
127             notifyServices(currentTime);
128             notifySessions(currentTime);
129         }
130 
131         private void notifyServices(long currentTime) {
132             Iterator<AbstractIoService> it = services.iterator();
133             while (it.hasNext()) {
134                 AbstractIoService service = it.next();
135                 if (service.isActive()) {
136                     notifyIdleness(service, currentTime, false);
137                 }
138             }
139         }
140 
141         private void notifySessions(long currentTime) {
142             Iterator<AbstractIoSession> it = sessions.iterator();
143             while (it.hasNext()) {
144                 AbstractIoSession session = it.next();
145                 if (session.isConnected()) {
146                     notifyIdleSession(session, currentTime);
147                 }
148             }
149         }
150     }
151     
152     private class SessionCloseListener implements IoFutureListener<IoFuture> {
153         public void operationComplete(IoFuture future) {
154             removeSession((AbstractIoSession) future.getSession());
155         }
156     }
157 
158     /**
159      * Fires a {@link IoEventType#SESSION_IDLE} event to any applicable
160      * sessions in the specified collection.
161      *
162      * @param currentTime the current time (i.e. {@link System#currentTimeMillis()})
163      */
164     public static void notifyIdleness(Iterator<? extends IoSession> sessions, long currentTime) {
165         IoSession s = null;
166         while (sessions.hasNext()) {
167             s = sessions.next();
168             notifyIdleSession(s, currentTime);
169         }
170     }
171 
172     public static void notifyIdleness(IoService service, long currentTime) {
173         notifyIdleness(service, currentTime, true);
174     }
175     
176     private static void notifyIdleness(IoService service, long currentTime, boolean includeSessions) {
177         if (!(service instanceof AbstractIoService)) {
178             return;
179         }
180         
181         ((AbstractIoService) service).notifyIdleness(currentTime);
182         
183         if (includeSessions) {
184             notifyIdleness(service.getManagedSessions().iterator(), currentTime);
185         }
186     }
187 
188     /**
189      * Fires a {@link IoEventType#SESSION_IDLE} event if applicable for the
190      * specified {@code session}.
191      *
192      * @param currentTime the current time (i.e. {@link System#currentTimeMillis()})
193      */
194     public static void notifyIdleSession(IoSession session, long currentTime) {
195         if (session instanceof AbstractIoSession) {
196             AbstractIoSession s = (AbstractIoSession) session;
197             notifyIdleSession1(
198                     s, currentTime,
199                     s.getConfig().getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
200                     IdleStatus.BOTH_IDLE, Math.max(
201                             s.getLastIoTime(),
202                             s.getLastIdleTime(IdleStatus.BOTH_IDLE)));
203             
204             notifyIdleSession1(
205                     s, currentTime,
206                     s.getConfig().getIdleTimeInMillis(IdleStatus.READER_IDLE),
207                     IdleStatus.READER_IDLE, Math.max(
208                             s.getLastReadTime(),
209                             s.getLastIdleTime(IdleStatus.READER_IDLE)));
210             
211             notifyIdleSession1(
212                     s, currentTime,
213                     s.getConfig().getIdleTimeInMillis(IdleStatus.WRITER_IDLE),
214                     IdleStatus.WRITER_IDLE, Math.max(
215                             s.getLastWriteTime(),
216                             s.getLastIdleTime(IdleStatus.WRITER_IDLE)));
217     
218             notifyWriteTimeout(s, currentTime);
219             updateThroughput(s, currentTime);
220         } else {
221             notifyIdleSession0(
222                     session, currentTime,
223                     session.getConfig().getIdleTimeInMillis(IdleStatus.BOTH_IDLE),
224                     IdleStatus.BOTH_IDLE, Math.max(
225                             session.getLastIoTime(),
226                             session.getLastIdleTime(IdleStatus.BOTH_IDLE)));
227             
228             notifyIdleSession0(
229                     session, currentTime,
230                     session.getConfig().getIdleTimeInMillis(IdleStatus.READER_IDLE),
231                     IdleStatus.READER_IDLE, Math.max(
232                             session.getLastReadTime(),
233                             session.getLastIdleTime(IdleStatus.READER_IDLE)));
234             
235             notifyIdleSession0(
236                     session, currentTime,
237                     session.getConfig().getIdleTimeInMillis(IdleStatus.WRITER_IDLE),
238                     IdleStatus.WRITER_IDLE, Math.max(
239                             session.getLastWriteTime(),
240                             session.getLastIdleTime(IdleStatus.WRITER_IDLE)));
241         }
242     }
243 
244     private static void notifyIdleSession0(
245             IoSession session, long currentTime,
246             long idleTime, IdleStatus status, long lastIoTime) {
247         if (idleTime > 0 && lastIoTime != 0
248                 && currentTime - lastIoTime >= idleTime) {
249             session.getFilterChain().fireSessionIdle(status);
250         }
251     }
252 
253     private static void notifyIdleSession1(
254             AbstractIoSession session, long currentTime,
255             long idleTime, IdleStatus status, long lastIoTime) {
256         if (idleTime > 0 && lastIoTime != 0
257                 && currentTime - lastIoTime >= idleTime) {
258             session.getFilterChain().fireSessionIdle(status);
259         }
260     }
261 
262     private static void notifyWriteTimeout(
263             AbstractIoSession session, long currentTime) {
264 
265         long writeTimeout = session.getConfig().getWriteTimeoutInMillis();
266         if (writeTimeout > 0 &&
267                 currentTime - session.getLastWriteTime() >= writeTimeout &&
268                 !session.getWriteRequestQueue().isEmpty(session)) {
269             WriteRequest request = session.getCurrentWriteRequest();
270             if (request != null) {
271                 session.setCurrentWriteRequest(null);
272                 WriteTimeoutException cause = new WriteTimeoutException(request);
273                 request.getFuture().setException(cause);
274                 session.getFilterChain().fireExceptionCaught(cause);
275                 // WriteException is an IOException, so we close the session.
276                 session.close();
277             }
278         }
279     }
280 
281     private static void updateThroughput(
282             AbstractIoSession session, long currentTime) {
283         session.updateThroughput(currentTime, false);
284     }
285 }