1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.filter.keepalive;
21
22 import org.apache.mina.core.filterchain.IoFilter;
23 import org.apache.mina.core.filterchain.IoFilterAdapter;
24 import org.apache.mina.core.filterchain.IoFilterChain;
25 import org.apache.mina.core.service.IoHandler;
26 import org.apache.mina.core.session.AttributeKey;
27 import org.apache.mina.core.session.IdleStatus;
28 import org.apache.mina.core.session.IoEventType;
29 import org.apache.mina.core.session.IoSession;
30 import org.apache.mina.core.session.IoSessionConfig;
31 import org.apache.mina.core.write.DefaultWriteRequest;
32 import org.apache.mina.core.write.WriteRequest;
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
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141 public class KeepAliveFilter extends IoFilterAdapter {
142
143 private final AttributeKey WAITING_FOR_RESPONSE = new AttributeKey(
144 getClass(), "waitingForResponse");
145 private final AttributeKey IGNORE_READER_IDLE_ONCE = new AttributeKey(
146 getClass(), "ignoreReaderIdleOnce");
147
148 private final KeepAliveMessageFactory messageFactory;
149 private final IdleStatus interestedIdleStatus;
150 private volatile KeepAliveRequestTimeoutHandler requestTimeoutHandler;
151 private volatile int requestInterval;
152 private volatile int requestTimeout;
153 private volatile boolean forwardEvent;
154
155
156
157
158
159
160
161
162
163
164
165 public KeepAliveFilter(KeepAliveMessageFactory messageFactory) {
166 this(messageFactory, IdleStatus.READER_IDLE, KeepAliveRequestTimeoutHandler.CLOSE);
167 }
168
169
170
171
172
173
174
175
176
177
178 public KeepAliveFilter(
179 KeepAliveMessageFactory messageFactory,
180 IdleStatus interestedIdleStatus) {
181 this(messageFactory, interestedIdleStatus, KeepAliveRequestTimeoutHandler.CLOSE, 60, 30);
182 }
183
184
185
186
187
188
189
190
191
192
193 public KeepAliveFilter(
194 KeepAliveMessageFactory messageFactory, KeepAliveRequestTimeoutHandler policy) {
195 this(messageFactory, IdleStatus.READER_IDLE, policy, 60, 30);
196 }
197
198
199
200
201
202
203
204
205
206 public KeepAliveFilter(
207 KeepAliveMessageFactory messageFactory,
208 IdleStatus interestedIdleStatus, KeepAliveRequestTimeoutHandler policy) {
209 this(messageFactory, interestedIdleStatus, policy, 60, 30);
210 }
211
212
213
214
215 public KeepAliveFilter(
216 KeepAliveMessageFactory messageFactory,
217 IdleStatus interestedIdleStatus, KeepAliveRequestTimeoutHandler policy,
218 int keepAliveRequestInterval, int keepAliveRequestTimeout) {
219 if (messageFactory == null) {
220 throw new NullPointerException("messageFactory");
221 }
222 if (interestedIdleStatus == null) {
223 throw new NullPointerException("interestedIdleStatus");
224 }
225 if (policy == null) {
226 throw new NullPointerException("policy");
227 }
228
229 this.messageFactory = messageFactory;
230 this.interestedIdleStatus = interestedIdleStatus;
231 requestTimeoutHandler = policy;
232
233 setRequestInterval(keepAliveRequestInterval);
234 setRequestTimeout(keepAliveRequestTimeout);
235 }
236
237 public IdleStatus getInterestedIdleStatus() {
238 return interestedIdleStatus;
239 }
240
241 public KeepAliveRequestTimeoutHandler getRequestTimeoutHandler() {
242 return requestTimeoutHandler;
243 }
244
245 public void setRequestTimeoutHandler(KeepAliveRequestTimeoutHandler timeoutHandler) {
246 if (timeoutHandler == null) {
247 throw new NullPointerException("timeoutHandler");
248 }
249 requestTimeoutHandler = timeoutHandler;
250 }
251
252 public int getRequestInterval() {
253 return requestInterval;
254 }
255
256 public void setRequestInterval(int keepAliveRequestInterval) {
257 if (keepAliveRequestInterval <= 0) {
258 throw new IllegalArgumentException(
259 "keepAliveRequestInterval must be a positive integer: " +
260 keepAliveRequestInterval);
261 }
262 requestInterval = keepAliveRequestInterval;
263 }
264
265 public int getRequestTimeout() {
266 return requestTimeout;
267 }
268
269 public void setRequestTimeout(int keepAliveRequestTimeout) {
270 if (keepAliveRequestTimeout <= 0) {
271 throw new IllegalArgumentException(
272 "keepAliveRequestTimeout must be a positive integer: " +
273 keepAliveRequestTimeout);
274 }
275 requestTimeout = keepAliveRequestTimeout;
276 }
277
278 public KeepAliveMessageFactory getMessageFactory() {
279 return messageFactory;
280 }
281
282
283
284
285
286
287 public boolean isForwardEvent() {
288 return forwardEvent;
289 }
290
291
292
293
294
295
296 public void setForwardEvent(boolean forwardEvent) {
297 this.forwardEvent = forwardEvent;
298 }
299
300 @Override
301 public void onPreAdd(IoFilterChain parent, String name,
302 NextFilter nextFilter) throws Exception {
303 if (parent.contains(this)) {
304 throw new IllegalArgumentException(
305 "You can't add the same filter instance more than once. " +
306 "Create another instance and add it.");
307 }
308 }
309
310 @Override
311 public void onPostAdd(
312 IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
313 resetStatus(parent.getSession());
314 }
315
316 @Override
317 public void onPostRemove(
318 IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
319 resetStatus(parent.getSession());
320 }
321
322 @Override
323 public void messageReceived(
324 NextFilter nextFilter, IoSession session, Object message) throws Exception {
325 try {
326 if (messageFactory.isRequest(session, message)) {
327 Object pongMessage =
328 messageFactory.getResponse(session, message);
329
330 if (pongMessage != null) {
331 nextFilter.filterWrite(
332 session, new DefaultWriteRequest(pongMessage));
333 }
334 }
335
336 if (messageFactory.isResponse(session, message)) {
337 resetStatus(session);
338 }
339 } finally {
340 if (!isKeepAliveMessage(session, message)) {
341 nextFilter.messageReceived(session, message);
342 }
343 }
344 }
345
346 @Override
347 public void messageSent(
348 NextFilter nextFilter, IoSession session, WriteRequest writeRequest) throws Exception {
349 Object message = writeRequest.getMessage();
350 if (!isKeepAliveMessage(session, message)) {
351 nextFilter.messageSent(session, writeRequest);
352 }
353 }
354
355 @Override
356 public void sessionIdle(
357 NextFilter nextFilter, IoSession session, IdleStatus status) throws Exception {
358 if (status == interestedIdleStatus) {
359 if (!session.containsAttribute(WAITING_FOR_RESPONSE)) {
360 Object pingMessage = messageFactory.getRequest(session);
361 if (pingMessage != null) {
362 nextFilter.filterWrite(
363 session,
364 new DefaultWriteRequest(pingMessage));
365
366
367
368 if (getRequestTimeoutHandler() != KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) {
369 markStatus(session);
370 if (interestedIdleStatus == IdleStatus.BOTH_IDLE) {
371 session.setAttribute(IGNORE_READER_IDLE_ONCE);
372 }
373 } else {
374 resetStatus(session);
375 }
376 }
377 } else {
378 handlePingTimeout(session);
379 }
380 } else if (status == IdleStatus.READER_IDLE) {
381 if (session.removeAttribute(IGNORE_READER_IDLE_ONCE) == null) {
382 if (session.containsAttribute(WAITING_FOR_RESPONSE)) {
383 handlePingTimeout(session);
384 }
385 }
386 }
387
388 if (forwardEvent) {
389 nextFilter.sessionIdle(session, status);
390 }
391 }
392
393 private void handlePingTimeout(IoSession session) throws Exception {
394 resetStatus(session);
395 KeepAliveRequestTimeoutHandler handler = getRequestTimeoutHandler();
396 if (handler == KeepAliveRequestTimeoutHandler.DEAF_SPEAKER) {
397 return;
398 }
399
400 handler.keepAliveRequestTimedOut(this, session);
401 }
402
403 private void markStatus(IoSession session) {
404 session.getConfig().setIdleTime(interestedIdleStatus, 0);
405 session.getConfig().setReaderIdleTime(getRequestTimeout());
406 session.setAttribute(WAITING_FOR_RESPONSE);
407 }
408
409 private void resetStatus(IoSession session) {
410 session.getConfig().setReaderIdleTime(0);
411 session.getConfig().setWriterIdleTime(0);
412 session.getConfig().setIdleTime(
413 interestedIdleStatus, getRequestInterval());
414 session.removeAttribute(WAITING_FOR_RESPONSE);
415 }
416
417 private boolean isKeepAliveMessage(IoSession session, Object message) {
418 return messageFactory.isRequest(session, message) ||
419 messageFactory.isResponse(session, message);
420 }
421 }