1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.mina.service.idlechecker;
20
21 import java.util.Collections;
22 import java.util.Set;
23 import java.util.concurrent.ConcurrentHashMap;
24
25 import org.apache.mina.api.IdleStatus;
26 import org.apache.mina.session.AbstractIoSession;
27 import org.apache.mina.session.AttributeKey;
28 import org.slf4j.Logger;
29 import org.slf4j.LoggerFactory;
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class IndexedIdleChecker implements IdleChecker {
59
60 private static final int MAX_IDLE_TIME_IN_SEC = 60 * 60;
61
62
63 private static final long MAX_IDLE_TIME_IN_MS = MAX_IDLE_TIME_IN_SEC * 1000L;
64
65
66 private static final Logger LOG = LoggerFactory.getLogger(IndexedIdleChecker.class);
67
68
69 private static final boolean IS_DEBUG = LOG.isDebugEnabled();
70
71 private static final AttributeKey<Integer> READ_IDLE_INDEX = AttributeKey.createKey(Integer.class,
72 "idle.read.index");
73
74 private static final AttributeKey<Integer> WRITE_IDLE_INDEX = AttributeKey.createKey(Integer.class,
75 "idle.write.index");
76
77 private long lastCheckTimeMs = System.currentTimeMillis();
78
79 @SuppressWarnings("unchecked")
80 private final Set<AbstractIoSession>[] readIdleSessionIndex = new Set[MAX_IDLE_TIME_IN_SEC];
81
82 @SuppressWarnings("unchecked")
83 private final Set<AbstractIoSession>[] writeIdleSessionIndex = new Set[MAX_IDLE_TIME_IN_SEC];
84
85
86 private static final int GRANULARITY_IN_MS = 1000;
87
88 private final Worker worker = new Worker();
89
90 private volatile boolean running = true;
91
92
93
94
95 @Override
96 public void start() {
97 worker.start();
98 }
99
100
101
102
103 @Override
104 public void destroy() {
105 running = false;
106 try {
107
108 worker.interrupt();
109
110 worker.join();
111 } catch (InterruptedException e) {
112
113 }
114 }
115
116
117
118
119 @Override
120 public void sessionRead(AbstractIoSession session, long timeInMs) {
121 if (IS_DEBUG) {
122 LOG.debug("session read event, compute idle index of session {}", session);
123 }
124
125
126 Integer oldIndex = session.getAttribute(READ_IDLE_INDEX);
127
128 if (oldIndex != null && readIdleSessionIndex[oldIndex] != null) {
129 if (IS_DEBUG) {
130 LOG.debug("remove for old index {}", oldIndex);
131 }
132
133 readIdleSessionIndex[oldIndex].remove(session);
134 }
135
136 long idleTimeInMs = session.getConfig().getIdleTimeInMillis(IdleStatus.READ_IDLE);
137
138
139 if (idleTimeInMs <= 0L) {
140 if (IS_DEBUG) {
141 LOG.debug("no read idle configuration");
142 }
143 } else {
144 int nextIdleTimeInSeconds = (int) ((timeInMs + idleTimeInMs) / 1000L);
145 int index = nextIdleTimeInSeconds % MAX_IDLE_TIME_IN_SEC;
146
147 if (IS_DEBUG) {
148 LOG.debug("computed index : {}", index);
149 }
150
151 if (readIdleSessionIndex[index] == null) {
152 readIdleSessionIndex[index] = Collections
153 .newSetFromMap(new ConcurrentHashMap<AbstractIoSession, Boolean>());
154 }
155
156 if (IS_DEBUG) {
157 LOG.debug("marking session {} idle for index {}", session, index);
158 }
159
160 readIdleSessionIndex[index].add(session);
161 session.setAttribute(READ_IDLE_INDEX, index);
162 }
163 }
164
165
166
167
168 @Override
169 public void sessionWritten(AbstractIoSession session, long timeInMs) {
170 if (IS_DEBUG) {
171 LOG.debug("session write event, compute idle index of session {}", session);
172 }
173
174
175 Integer oldIndex = session.getAttribute(WRITE_IDLE_INDEX);
176
177 if (oldIndex != null && writeIdleSessionIndex[oldIndex] != null) {
178 if (IS_DEBUG) {
179 LOG.debug("remove for old index {}", oldIndex);
180 }
181
182 writeIdleSessionIndex[oldIndex].remove(session);
183 }
184
185 long idleTimeInMs = session.getConfig().getIdleTimeInMillis(IdleStatus.WRITE_IDLE);
186
187
188 if (idleTimeInMs <= 0L) {
189 if (IS_DEBUG) {
190 LOG.debug("no write idle configuration");
191 }
192 } else {
193 int nextIdleTimeInSeconds = (int) ((timeInMs + idleTimeInMs) / 1000L);
194 int index = nextIdleTimeInSeconds % MAX_IDLE_TIME_IN_SEC;
195
196 if (writeIdleSessionIndex[index] == null) {
197 writeIdleSessionIndex[index] = Collections
198 .newSetFromMap(new ConcurrentHashMap<AbstractIoSession, Boolean>());
199 }
200
201 writeIdleSessionIndex[index].add(session);
202 session.setAttribute(WRITE_IDLE_INDEX, index);
203 }
204 }
205
206
207
208
209 @Override
210 public int processIdleSession(long timeMs) {
211 int counter = 0;
212 long delta = timeMs - lastCheckTimeMs;
213
214 if (LOG.isDebugEnabled()) {
215 LOG.debug("checking idle time, last = {}, now = {}, delta = {}", new Object[] { lastCheckTimeMs, timeMs,
216 delta });
217 }
218
219 if (delta < 1000) {
220 LOG.debug("not a second between the last checks, abort");
221 return 0;
222 }
223
224
225
226
227
228 int startIdx = ((int) (Math.max(lastCheckTimeMs, timeMs - MAX_IDLE_TIME_IN_MS + 1) / 1000L))
229 % MAX_IDLE_TIME_IN_SEC;
230 int endIdx = ((int) (timeMs / 1000L)) % MAX_IDLE_TIME_IN_SEC;
231
232 LOG.debug("scaning from index {} to index {}", startIdx, endIdx);
233
234 int index = startIdx;
235 do {
236
237 LOG.trace("scanning index {}", index);
238
239 counter += processIndex(readIdleSessionIndex, index, IdleStatus.READ_IDLE);
240 counter += processIndex(writeIdleSessionIndex, index, IdleStatus.WRITE_IDLE);
241
242 index = (index + 1) % MAX_IDLE_TIME_IN_SEC;
243 } while (index != endIdx);
244
245
246 lastCheckTimeMs = timeMs;
247 LOG.debug("detected {} idleing sessions", counter);
248 return counter;
249 }
250
251 private int processIndex(Set<AbstractIoSession>[] indexByTime, int position, IdleStatus status) {
252 Set<AbstractIoSession> sessions = indexByTime[position];
253
254 if (sessions == null) {
255 return 0;
256 }
257
258 int counter = 0;
259
260 for (AbstractIoSession idleSession : sessions) {
261 idleSession.setAttribute(status == IdleStatus.READ_IDLE ? READ_IDLE_INDEX : WRITE_IDLE_INDEX, null);
262
263 if (idleSession.getConfig().getIdleTimeInMillis(status) > 0) {
264 idleSession.processSessionIdle(status);
265 }
266 counter++;
267 }
268
269 indexByTime[position] = null;
270 return counter;
271 }
272
273
274
275
276 private class Worker extends Thread {
277
278 public Worker() {
279 super("IdleChecker");
280 setDaemon(true);
281 }
282
283 @Override
284 public void run() {
285 while (running) {
286 try {
287 sleep(GRANULARITY_IN_MS);
288 processIdleSession(System.currentTimeMillis());
289 } catch (InterruptedException e) {
290 break;
291 }
292 }
293 }
294 }
295 }