27
27
import java .util .Set ;
28
28
import java .util .concurrent .ConcurrentHashMap ;
29
29
import java .util .concurrent .ScheduledFuture ;
30
- import java .util .concurrent .locks .Lock ;
31
- import java .util .concurrent .locks .ReentrantLock ;
32
30
33
31
import org .apache .commons .logging .Log ;
34
32
import org .apache .commons .logging .LogFactory ;
33
+
35
34
import org .springframework .core .NestedCheckedException ;
36
35
import org .springframework .util .Assert ;
37
36
import org .springframework .web .socket .CloseStatus ;
@@ -106,9 +105,11 @@ private enum State {NEW, OPEN, CLOSED}
106
105
107
106
private volatile long timeLastActive = this .timeCreated ;
108
107
109
- private volatile ScheduledFuture <?> heartbeatTask ;
108
+ private ScheduledFuture <?> heartbeatFuture ;
109
+
110
+ private HeartbeatTask heartbeatTask ;
110
111
111
- private final Lock heartbeatLock = new ReentrantLock ();
112
+ private final Object heartbeatLock = new Object ();
112
113
113
114
private volatile boolean heartbeatDisabled ;
114
115
@@ -248,16 +249,11 @@ public void disableHeartbeat() {
248
249
cancelHeartbeat ();
249
250
}
250
251
251
- public void sendHeartbeat () throws SockJsTransportFailureException {
252
- if (isActive ()) {
253
- if (heartbeatLock .tryLock ()) {
254
- try {
255
- writeFrame (SockJsFrame .heartbeatFrame ());
256
- scheduleHeartbeat ();
257
- }
258
- finally {
259
- heartbeatLock .unlock ();
260
- }
252
+ protected void sendHeartbeat () throws SockJsTransportFailureException {
253
+ synchronized (this .heartbeatLock ) {
254
+ if (isActive () && !this .heartbeatDisabled ) {
255
+ writeFrame (SockJsFrame .heartbeatFrame ());
256
+ scheduleHeartbeat ();
261
257
}
262
258
}
263
259
}
@@ -266,56 +262,33 @@ protected void scheduleHeartbeat() {
266
262
if (this .heartbeatDisabled ) {
267
263
return ;
268
264
}
269
-
270
- Assert .state (this .config .getTaskScheduler () != null , "Expected SockJS TaskScheduler" );
271
- cancelHeartbeat ();
272
- if (!isActive ()) {
273
- return ;
274
- }
275
-
276
- Date time = new Date (System .currentTimeMillis () + this .config .getHeartbeatTime ());
277
- this .heartbeatTask = this .config .getTaskScheduler ().schedule (new Runnable () {
278
- public void run () {
279
- try {
280
- sendHeartbeat ();
281
- }
282
- catch (Throwable ex ) {
283
- // ignore
284
- }
285
- }
286
- }, time );
287
- if (logger .isTraceEnabled ()) {
288
- logger .trace ("Scheduled heartbeat in session " + getId ());
289
- }
290
- }
291
-
292
- protected void cancelHeartbeat () {
293
- try {
294
- ScheduledFuture <?> task = this .heartbeatTask ;
295
- this .heartbeatTask = null ;
296
- if (task == null || task .isCancelled ()) {
265
+ synchronized (this .heartbeatLock ) {
266
+ cancelHeartbeat ();
267
+ if (!isActive ()) {
297
268
return ;
298
269
}
299
-
270
+ Date time = new Date (System .currentTimeMillis () + this .config .getHeartbeatTime ());
271
+ this .heartbeatTask = new HeartbeatTask ();
272
+ this .heartbeatFuture = this .config .getTaskScheduler ().schedule (this .heartbeatTask , time );
300
273
if (logger .isTraceEnabled ()) {
301
- logger .trace ("Cancelling heartbeat in session " + getId ());
302
- }
303
- if (task .cancel (false )) {
304
- return ;
274
+ logger .trace ("Scheduled heartbeat in session " + getId ());
305
275
}
276
+ }
277
+ }
306
278
307
- if (logger .isTraceEnabled ()) {
308
- logger .trace ("Failed to cancel heartbeat, acquiring heartbeat write lock." );
279
+ protected void cancelHeartbeat () {
280
+ synchronized (this .heartbeatLock ) {
281
+ if (this .heartbeatFuture != null ) {
282
+ if (logger .isTraceEnabled ()) {
283
+ logger .trace ("Cancelling heartbeat in session " + getId ());
284
+ }
285
+ this .heartbeatFuture .cancel (false );
286
+ this .heartbeatFuture = null ;
309
287
}
310
- this .heartbeatLock .lock ();
311
-
312
- if (logger .isTraceEnabled ()) {
313
- logger .trace ("Releasing heartbeat lock." );
288
+ if (this .heartbeatTask != null ) {
289
+ this .heartbeatTask .cancel ();
290
+ this .heartbeatTask = null ;
314
291
}
315
- this .heartbeatLock .unlock ();
316
- }
317
- catch (Throwable ex ) {
318
- logger .debug ("Failure while cancelling heartbeat in session " + getId (), ex );
319
292
}
320
293
}
321
294
@@ -465,4 +438,28 @@ public String toString() {
465
438
return getClass ().getSimpleName () + "[id=" + getId () + "]" ;
466
439
}
467
440
441
+
442
+ private class HeartbeatTask implements Runnable {
443
+
444
+ private boolean expired ;
445
+
446
+ @ Override
447
+ public void run () {
448
+ synchronized (heartbeatLock ) {
449
+ if (!this .expired ) {
450
+ try {
451
+ sendHeartbeat ();
452
+ }
453
+ finally {
454
+ this .expired = true ;
455
+ }
456
+ }
457
+ }
458
+ }
459
+
460
+ void cancel () {
461
+ this .expired = true ;
462
+ }
463
+ }
464
+
468
465
}
0 commit comments