@@ -67,7 +67,14 @@ public class AutorecoveringConnection implements Connection, Recoverable, Networ
67
67
private final Map <String , RecordedConsumer > consumers = new ConcurrentHashMap <String , RecordedConsumer >();
68
68
private final List <ConsumerRecoveryListener > consumerRecoveryListeners = new ArrayList <ConsumerRecoveryListener >();
69
69
private final List <QueueRecoveryListener > queueRecoveryListeners = new ArrayList <QueueRecoveryListener >();
70
-
70
+
71
+ // Used to block connection recovery attempts after close() is invoked.
72
+ private volatile boolean manuallyClosed = false ;
73
+
74
+ // This lock guards the manuallyClosed flag and the delegate connection. Guarding these two ensures that a new connection can never
75
+ // be created after application code has initiated shutdown.
76
+ private Object recoveryLock = new Object ();
77
+
71
78
public AutorecoveringConnection (ConnectionParams params , FrameHandlerFactory f , Address [] addrs ) {
72
79
this .cf = new RecoveryAwareAMQConnectionFactory (params , f , addrs );
73
80
this .params = params ;
@@ -181,48 +188,69 @@ public boolean isOpen() {
181
188
* @see com.rabbitmq.client.Connection#close()
182
189
*/
183
190
public void close () throws IOException {
191
+ synchronized (recoveryLock ) {
192
+ this .manuallyClosed = true ;
193
+ }
184
194
delegate .close ();
185
195
}
186
196
187
197
/**
188
198
* @see Connection#close(int)
189
199
*/
190
200
public void close (int timeout ) throws IOException {
201
+ synchronized (recoveryLock ) {
202
+ this .manuallyClosed = true ;
203
+ }
191
204
delegate .close (timeout );
192
205
}
193
206
194
207
/**
195
208
* @see Connection#close(int, String, int)
196
209
*/
197
210
public void close (int closeCode , String closeMessage , int timeout ) throws IOException {
211
+ synchronized (recoveryLock ) {
212
+ this .manuallyClosed = true ;
213
+ }
198
214
delegate .close (closeCode , closeMessage , timeout );
199
215
}
200
216
201
217
/**
202
218
* @see com.rabbitmq.client.Connection#abort()
203
219
*/
204
220
public void abort () {
221
+ synchronized (recoveryLock ) {
222
+ this .manuallyClosed = true ;
223
+ }
205
224
delegate .abort ();
206
225
}
207
226
208
227
/**
209
228
* @see Connection#abort(int, String, int)
210
229
*/
211
230
public void abort (int closeCode , String closeMessage , int timeout ) {
231
+ synchronized (recoveryLock ) {
232
+ this .manuallyClosed = true ;
233
+ }
212
234
delegate .abort (closeCode , closeMessage , timeout );
213
235
}
214
236
215
237
/**
216
238
* @see Connection#abort(int, String)
217
239
*/
218
240
public void abort (int closeCode , String closeMessage ) {
241
+ synchronized (recoveryLock ) {
242
+ this .manuallyClosed = true ;
243
+ }
219
244
delegate .abort (closeCode , closeMessage );
220
245
}
221
246
222
247
/**
223
248
* @see Connection#abort(int)
224
249
*/
225
250
public void abort (int timeout ) {
251
+ synchronized (recoveryLock ) {
252
+ this .manuallyClosed = true ;
253
+ }
226
254
delegate .abort (timeout );
227
255
}
228
256
@@ -261,7 +289,10 @@ public void clearBlockedListeners() {
261
289
* @see com.rabbitmq.client.Connection#close(int, String)
262
290
*/
263
291
public void close (int closeCode , String closeMessage ) throws IOException {
264
- delegate .close (closeCode , closeMessage );
292
+ synchronized (recoveryLock ) {
293
+ this .manuallyClosed = true ;
294
+ }
295
+ delegate .close (closeCode , closeMessage );
265
296
}
266
297
267
298
/**
@@ -404,16 +435,18 @@ public void removeConsumerRecoveryListener(ConsumerRecoveryListener listener) {
404
435
405
436
synchronized private void beginAutomaticRecovery () throws InterruptedException , IOException , TopologyRecoveryException {
406
437
Thread .sleep (this .params .getNetworkRecoveryInterval ());
407
- this .recoverConnection ();
408
- this .recoverShutdownListeners ();
409
- this .recoverBlockedListeners ();
410
- this .recoverChannels ();
411
- if (this .params .isTopologyRecoveryEnabled ()) {
412
- this .recoverEntities ();
413
- this .recoverConsumers ();
414
- }
438
+ if (!this .recoverConnection ())
439
+ return ;
440
+
441
+ this .recoverShutdownListeners ();
442
+ this .recoverBlockedListeners ();
443
+ this .recoverChannels ();
444
+ if (this .params .isTopologyRecoveryEnabled ()) {
445
+ this .recoverEntities ();
446
+ this .recoverConsumers ();
447
+ }
415
448
416
- this .notifyRecoveryListeners ();
449
+ this .notifyRecoveryListeners ();
417
450
}
418
451
419
452
private void recoverShutdownListeners () {
@@ -428,18 +461,33 @@ private void recoverBlockedListeners() {
428
461
}
429
462
}
430
463
431
- private void recoverConnection () throws IOException , InterruptedException {
432
- boolean recovering = true ;
433
- while (recovering ) {
464
+ // Returns true if the connection was recovered,
465
+ // false if application initiated shutdown while attempting recovery.
466
+ private boolean recoverConnection () throws IOException , InterruptedException {
467
+ while (!manuallyClosed )
468
+ {
434
469
try {
435
- this .delegate = this .cf .newConnection ();
436
- recovering = false ;
470
+ RecoveryAwareAMQConnection newConn = this .cf .newConnection ();
471
+ synchronized (recoveryLock ) {
472
+ if (!manuallyClosed ) {
473
+ // This is the standard case.
474
+ this .delegate = newConn ;
475
+ return true ;
476
+ }
477
+ }
478
+ // This is the once in a blue moon case.
479
+ // Application code just called close as the connection
480
+ // was being re-established. So we attempt to close the newly created connection.
481
+ newConn .abort ();
482
+ return false ;
437
483
} catch (Exception e ) {
438
484
// TODO: exponential back-off
439
485
Thread .sleep (this .params .getNetworkRecoveryInterval ());
440
486
this .getExceptionHandler ().handleConnectionRecoveryException (this , e );
441
487
}
442
488
}
489
+
490
+ return false ;
443
491
}
444
492
445
493
private void recoverChannels () {
0 commit comments