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<>();
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      /**
54       * Creates a new instance of IdleStatusChecker 
55       */
56      public IdleStatusChecker() {
57          // Do nothing
58      }
59  
60      /**
61       * Add the session for being checked for idle. 
62       * @param session the session to check
63       */
64      public void addSession(AbstractIoSession session) {
65          sessions.add(session);
66          CloseFuture closeFuture = session.getCloseFuture();
67  
68          // isn't service reponsability to remove the session nicely ?
69          closeFuture.addListener(sessionCloseListener);
70      }
71  
72      /**
73       * get a runnable task able to be scheduled in the {@link IoService} executor.
74       * @return the associated runnable task
75       */
76      public NotifyingTask getNotifyingTask() {
77          return notifyingTask;
78      }
79  
80      /**
81       * The class to place in the transport executor for checking the sessions idle 
82       */
83      public class NotifyingTask implements Runnable {
84          private volatile boolean cancelled;
85  
86          private volatile Thread thread;
87  
88          // we forbid instantiation of this class outside
89          /** No qualifier */
90          NotifyingTask() {
91              // Do nothing
92          }
93  
94          /**
95           * {@inheritDoc}
96           */
97          @Override
98          public void run() {
99              thread = Thread.currentThread();
100             try {
101                 while (!cancelled) {
102                     // Check idleness with fixed delay (1 second).
103                     long currentTime = System.currentTimeMillis();
104 
105                     notifySessions(currentTime);
106 
107                     try {
108                         Thread.sleep(1000);
109                     } catch (InterruptedException e) {
110                         // will exit the loop if interrupted from interrupt()
111                     }
112                 }
113             } finally {
114                 thread = null;
115             }
116         }
117 
118         /**
119          * stop execution of the task
120          */
121         public void cancel() {
122             cancelled = true;
123             
124             if (thread != null) {
125                 thread.interrupt();
126             }
127         }
128 
129         private void notifySessions(long currentTime) {
130             Iterator<AbstractIoSession> it = sessions.iterator();
131             while (it.hasNext()) {
132                 AbstractIoSession session = it.next();
133                 if (session.isConnected()) {
134                     AbstractIoSession.notifyIdleSession(session, currentTime);
135                 }
136             }
137         }
138     }
139 
140     private class SessionCloseListener implements IoFutureListener<IoFuture> {
141         /**
142          * Default constructor
143          */
144         public SessionCloseListener() {
145             super();
146         }
147 
148         /**
149          * {@inheritDoc}
150          */
151         @Override
152         public void operationComplete(IoFuture future) {
153             removeSession((AbstractIoSession) future.getSession());
154         }
155         
156         /**
157          * remove a session from the list of session being checked.
158          * @param session The session to remove
159          */
160         private void removeSession(AbstractIoSession session) {
161             sessions.remove(session);
162         }
163     }
164 }