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.core.session;
21  
22  import java.util.Iterator;
23  import java.util.Set;
24  
25  import org.apache.mina.core.future.CloseFuture;
26  import org.apache.mina.core.future.IoFuture;
27  import org.apache.mina.core.future.IoFutureListener;
28  import org.apache.mina.core.service.IoService;
29  import org.apache.mina.util.ConcurrentHashSet;
30  
31  /**
32   * Detects idle sessions and fires <tt>sessionIdle</tt> events to them.
33   * To be used for service unable to trigger idle events alone, like VmPipe
34   * or SerialTransport. Polling base transport are advised to trigger idle 
35   * events alone, using the poll/select timeout. 
36   *
37   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
38   */
39  public class IdleStatusChecker {
40  
41      // the list of session to check
42      private final Set<AbstractIoSession> sessions = new ConcurrentHashSet<AbstractIoSession>();
43  
44      /* create a task you can execute in the transport code,
45       * if the transport is like NIO or APR you don't need to call it,
46       * you just need to call the needed static sessions on select()/poll() 
47       * timeout.
48       */
49      private final NotifyingTask notifyingTask = new NotifyingTask();
50  
51      private final IoFutureListener<IoFuture> sessionCloseListener = new SessionCloseListener();
52  
53      public IdleStatusChecker() {
54          // Do nothing
55      }
56  
57      /**
58       * Add the session for being checked for idle. 
59       * @param session the session to check
60       */
61      public void addSession(AbstractIoSession session) {
62          sessions.add(session);
63          CloseFuture closeFuture = session.getCloseFuture();
64  
65          // isn't service reponsability to remove the session nicely ?
66          closeFuture.addListener(sessionCloseListener);
67      }
68  
69      /**
70       * remove a session from the list of session being checked.
71       * @param session
72       */
73      private void removeSession(AbstractIoSession session) {
74          sessions.remove(session);
75      }
76  
77      /**
78       * get a runnable task able to be scheduled in the {@link IoService} executor.
79       * @return the associated runnable task
80       */
81      public NotifyingTask getNotifyingTask() {
82          return notifyingTask;
83      }
84  
85      /**
86       * The class to place in the transport executor for checking the sessions idle 
87       */
88      public class NotifyingTask implements Runnable {
89          private volatile boolean cancelled;
90  
91          private volatile Thread thread;
92  
93          // we forbid instantiation of this class outside
94          /** No qualifier */
95          NotifyingTask() {
96              // Do nothing
97          }
98  
99          public void run() {
100             thread = Thread.currentThread();
101             try {
102                 while (!cancelled) {
103                     // Check idleness with fixed delay (1 second).
104                     long currentTime = System.currentTimeMillis();
105 
106                     notifySessions(currentTime);
107 
108                     try {
109                         Thread.sleep(1000);
110                     } catch (InterruptedException e) {
111                         // will exit the loop if interrupted from interrupt()
112                     }
113                 }
114             } finally {
115                 thread = null;
116             }
117         }
118 
119         /**
120          * stop execution of the task
121          */
122         public void cancel() {
123             cancelled = true;
124             Thread thread = this.thread;
125             if (thread != null) {
126                 thread.interrupt();
127             }
128         }
129 
130         private void notifySessions(long currentTime) {
131             Iterator<AbstractIoSession> it = sessions.iterator();
132             while (it.hasNext()) {
133                 AbstractIoSession session = it.next();
134                 if (session.isConnected()) {
135                     AbstractIoSession.notifyIdleSession(session, currentTime);
136                 }
137             }
138         }
139     }
140 
141     private class SessionCloseListener implements IoFutureListener<IoFuture> {
142         /**
143          * Default constructor
144          */
145         public SessionCloseListener() {
146             super();
147         }
148 
149         public void operationComplete(IoFuture future) {
150             removeSession((AbstractIoSession) future.getSession());
151         }
152     }
153 }