1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.timeout;
17
18 import static org.jboss.netty.channel.Channels.*;
19
20 import java.util.concurrent.TimeUnit;
21
22 import org.jboss.netty.bootstrap.ServerBootstrap;
23 import org.jboss.netty.channel.Channel;
24 import org.jboss.netty.channel.ChannelHandlerContext;
25 import org.jboss.netty.channel.ChannelPipeline;
26 import org.jboss.netty.channel.ChannelPipelineFactory;
27 import org.jboss.netty.channel.ChannelStateEvent;
28 import org.jboss.netty.channel.Channels;
29 import org.jboss.netty.channel.LifeCycleAwareChannelHandler;
30 import org.jboss.netty.channel.MessageEvent;
31 import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
32 import org.jboss.netty.channel.WriteCompletionEvent;
33 import org.jboss.netty.util.ExternalResourceReleasable;
34 import org.jboss.netty.util.HashedWheelTimer;
35 import org.jboss.netty.util.Timeout;
36 import org.jboss.netty.util.Timer;
37 import org.jboss.netty.util.TimerTask;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123 public class IdleStateHandler extends SimpleChannelUpstreamHandler
124 implements LifeCycleAwareChannelHandler,
125 ExternalResourceReleasable {
126
127 final Timer timer;
128
129 final long readerIdleTimeMillis;
130 volatile Timeout readerIdleTimeout;
131 volatile long lastReadTime;
132
133 final long writerIdleTimeMillis;
134 volatile Timeout writerIdleTimeout;
135 volatile long lastWriteTime;
136
137 final long allIdleTimeMillis;
138 volatile Timeout allIdleTimeout;
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159 public IdleStateHandler(
160 Timer timer,
161 int readerIdleTimeSeconds,
162 int writerIdleTimeSeconds,
163 int allIdleTimeSeconds) {
164
165 this(timer,
166 readerIdleTimeSeconds, writerIdleTimeSeconds, allIdleTimeSeconds,
167 TimeUnit.SECONDS);
168 }
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192 public IdleStateHandler(
193 Timer timer,
194 long readerIdleTime, long writerIdleTime, long allIdleTime,
195 TimeUnit unit) {
196
197 if (timer == null) {
198 throw new NullPointerException("timer");
199 }
200 if (unit == null) {
201 throw new NullPointerException("unit");
202 }
203
204 this.timer = timer;
205 if (readerIdleTime <= 0) {
206 readerIdleTimeMillis = 0;
207 } else {
208 readerIdleTimeMillis = Math.max(unit.toMillis(readerIdleTime), 1);
209 }
210 if (writerIdleTime <= 0) {
211 writerIdleTimeMillis = 0;
212 } else {
213 writerIdleTimeMillis = Math.max(unit.toMillis(writerIdleTime), 1);
214 }
215 if (allIdleTime <= 0) {
216 allIdleTimeMillis = 0;
217 } else {
218 allIdleTimeMillis = Math.max(unit.toMillis(allIdleTime), 1);
219 }
220 }
221
222
223
224
225
226
227 public void releaseExternalResources() {
228 timer.stop();
229 }
230
231 public void beforeAdd(ChannelHandlerContext ctx) throws Exception {
232 if (ctx.getPipeline().isAttached()) {
233
234
235
236 initialize(ctx);
237 } else {
238
239
240 }
241 }
242
243 public void afterAdd(ChannelHandlerContext ctx) throws Exception {
244
245 }
246
247 public void beforeRemove(ChannelHandlerContext ctx) throws Exception {
248 destroy();
249 }
250
251 public void afterRemove(ChannelHandlerContext ctx) throws Exception {
252
253 }
254
255 @Override
256 public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
257 throws Exception {
258
259
260
261 initialize(ctx);
262 ctx.sendUpstream(e);
263 }
264
265 @Override
266 public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
267 throws Exception {
268 destroy();
269 ctx.sendUpstream(e);
270 }
271
272 @Override
273 public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
274 throws Exception {
275 lastReadTime = System.currentTimeMillis();
276 ctx.sendUpstream(e);
277 }
278
279 @Override
280 public void writeComplete(ChannelHandlerContext ctx, WriteCompletionEvent e)
281 throws Exception {
282 if (e.getWrittenAmount() > 0) {
283 lastWriteTime = System.currentTimeMillis();
284 }
285 ctx.sendUpstream(e);
286 }
287
288 private void initialize(ChannelHandlerContext ctx) {
289 lastReadTime = lastWriteTime = System.currentTimeMillis();
290 if (readerIdleTimeMillis > 0) {
291 readerIdleTimeout = timer.newTimeout(
292 new ReaderIdleTimeoutTask(ctx),
293 readerIdleTimeMillis, TimeUnit.MILLISECONDS);
294 }
295 if (writerIdleTimeMillis > 0) {
296 writerIdleTimeout = timer.newTimeout(
297 new WriterIdleTimeoutTask(ctx),
298 writerIdleTimeMillis, TimeUnit.MILLISECONDS);
299 }
300 if (allIdleTimeMillis > 0) {
301 allIdleTimeout = timer.newTimeout(
302 new AllIdleTimeoutTask(ctx),
303 allIdleTimeMillis, TimeUnit.MILLISECONDS);
304 }
305 }
306
307 private void destroy() {
308 if (readerIdleTimeout != null) {
309 readerIdleTimeout.cancel();
310 readerIdleTimeout = null;
311 }
312 if (writerIdleTimeout != null) {
313 writerIdleTimeout.cancel();
314 writerIdleTimeout = null;
315 }
316 if (allIdleTimeout != null) {
317 allIdleTimeout.cancel();
318 allIdleTimeout = null;
319 }
320 }
321
322 protected void channelIdle(
323 ChannelHandlerContext ctx, IdleState state, long lastActivityTimeMillis) throws Exception {
324 ctx.sendUpstream(new DefaultIdleStateEvent(ctx.getChannel(), state, lastActivityTimeMillis));
325 }
326
327 private final class ReaderIdleTimeoutTask implements TimerTask {
328
329 private final ChannelHandlerContext ctx;
330
331 ReaderIdleTimeoutTask(ChannelHandlerContext ctx) {
332 this.ctx = ctx;
333 }
334
335 public void run(Timeout timeout) throws Exception {
336 if (timeout.isCancelled() || !ctx.getChannel().isOpen()) {
337 return;
338 }
339
340 long currentTime = System.currentTimeMillis();
341 long lastReadTime = IdleStateHandler.this.lastReadTime;
342 long nextDelay = readerIdleTimeMillis - (currentTime - lastReadTime);
343 if (nextDelay <= 0) {
344
345 readerIdleTimeout =
346 timer.newTimeout(this, readerIdleTimeMillis, TimeUnit.MILLISECONDS);
347 try {
348 channelIdle(ctx, IdleState.READER_IDLE, lastReadTime);
349 } catch (Throwable t) {
350 fireExceptionCaught(ctx, t);
351 }
352 } else {
353
354 readerIdleTimeout =
355 timer.newTimeout(this, nextDelay, TimeUnit.MILLISECONDS);
356 }
357 }
358
359 }
360
361 private final class WriterIdleTimeoutTask implements TimerTask {
362
363 private final ChannelHandlerContext ctx;
364
365 WriterIdleTimeoutTask(ChannelHandlerContext ctx) {
366 this.ctx = ctx;
367 }
368
369 public void run(Timeout timeout) throws Exception {
370 if (timeout.isCancelled() || !ctx.getChannel().isOpen()) {
371 return;
372 }
373
374 long currentTime = System.currentTimeMillis();
375 long lastWriteTime = IdleStateHandler.this.lastWriteTime;
376 long nextDelay = writerIdleTimeMillis - (currentTime - lastWriteTime);
377 if (nextDelay <= 0) {
378
379 writerIdleTimeout =
380 timer.newTimeout(this, writerIdleTimeMillis, TimeUnit.MILLISECONDS);
381 try {
382 channelIdle(ctx, IdleState.WRITER_IDLE, lastWriteTime);
383 } catch (Throwable t) {
384 fireExceptionCaught(ctx, t);
385 }
386 } else {
387
388 writerIdleTimeout =
389 timer.newTimeout(this, nextDelay, TimeUnit.MILLISECONDS);
390 }
391 }
392 }
393
394 private final class AllIdleTimeoutTask implements TimerTask {
395
396 private final ChannelHandlerContext ctx;
397
398 AllIdleTimeoutTask(ChannelHandlerContext ctx) {
399 this.ctx = ctx;
400 }
401
402 public void run(Timeout timeout) throws Exception {
403 if (timeout.isCancelled() || !ctx.getChannel().isOpen()) {
404 return;
405 }
406
407 long currentTime = System.currentTimeMillis();
408 long lastIoTime = Math.max(lastReadTime, lastWriteTime);
409 long nextDelay = allIdleTimeMillis - (currentTime - lastIoTime);
410 if (nextDelay <= 0) {
411
412
413 allIdleTimeout =
414 timer.newTimeout(this, allIdleTimeMillis, TimeUnit.MILLISECONDS);
415 try {
416 channelIdle(ctx, IdleState.ALL_IDLE, lastIoTime);
417 } catch (Throwable t) {
418 fireExceptionCaught(ctx, t);
419 }
420 } else {
421
422
423 allIdleTimeout =
424 timer.newTimeout(this, nextDelay, TimeUnit.MILLISECONDS);
425 }
426 }
427 }
428 }