001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.core.session; 021 022import java.util.Iterator; 023import java.util.Set; 024 025import org.apache.mina.core.future.CloseFuture; 026import org.apache.mina.core.future.IoFuture; 027import org.apache.mina.core.future.IoFutureListener; 028import org.apache.mina.core.service.IoService; 029import org.apache.mina.util.ConcurrentHashSet; 030 031/** 032 * Detects idle sessions and fires <tt>sessionIdle</tt> events to them. 033 * To be used for service unable to trigger idle events alone, like VmPipe 034 * or SerialTransport. Polling base transport are advised to trigger idle 035 * events alone, using the poll/select timeout. 036 * 037 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 038 */ 039public class IdleStatusChecker { 040 041 // the list of session to check 042 private final Set<AbstractIoSession> sessions = new ConcurrentHashSet<AbstractIoSession>(); 043 044 /* create a task you can execute in the transport code, 045 * if the transport is like NIO or APR you don't need to call it, 046 * you just need to call the needed static sessions on select()/poll() 047 * timeout. 048 */ 049 private final NotifyingTask notifyingTask = new NotifyingTask(); 050 051 private final IoFutureListener<IoFuture> sessionCloseListener = new SessionCloseListener(); 052 053 public IdleStatusChecker() { 054 // Do nothing 055 } 056 057 /** 058 * Add the session for being checked for idle. 059 * @param session the session to check 060 */ 061 public void addSession(AbstractIoSession session) { 062 sessions.add(session); 063 CloseFuture closeFuture = session.getCloseFuture(); 064 065 // isn't service reponsability to remove the session nicely ? 066 closeFuture.addListener(sessionCloseListener); 067 } 068 069 /** 070 * remove a session from the list of session being checked. 071 * @param session 072 */ 073 private void removeSession(AbstractIoSession session) { 074 sessions.remove(session); 075 } 076 077 /** 078 * get a runnable task able to be scheduled in the {@link IoService} executor. 079 * @return the associated runnable task 080 */ 081 public NotifyingTask getNotifyingTask() { 082 return notifyingTask; 083 } 084 085 /** 086 * The class to place in the transport executor for checking the sessions idle 087 */ 088 public class NotifyingTask implements Runnable { 089 private volatile boolean cancelled; 090 091 private volatile Thread thread; 092 093 // we forbid instantiation of this class outside 094 /** No qualifier */ 095 NotifyingTask() { 096 // Do nothing 097 } 098 099 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}