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