19
19
import java .io .IOException ;
20
20
import java .util .ArrayList ;
21
21
import java .util .Collection ;
22
+ import java .util .Collections ;
22
23
import java .util .HashMap ;
24
+ import java .util .Iterator ;
25
+ import java .util .LinkedHashMap ;
23
26
import java .util .LinkedList ;
24
27
import java .util .List ;
25
28
import java .util .Map ;
29
+ import java .util .Map .Entry ;
26
30
import java .util .Properties ;
27
31
import java .util .concurrent .TimeoutException ;
28
32
import java .util .concurrent .atomic .AtomicBoolean ;
63
67
import org .springframework .util .StringUtils ;
64
68
65
69
import com .rabbitmq .client .AMQP .Queue .DeclareOk ;
70
+ import com .rabbitmq .client .AMQP .Queue .DeleteOk ;
66
71
import com .rabbitmq .client .AMQP .Queue .PurgeOk ;
67
72
import com .rabbitmq .client .Channel ;
68
73
@@ -124,6 +129,8 @@ public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, Applicat
124
129
125
130
private final ConnectionFactory connectionFactory ;
126
131
132
+ private final Map <String , Declarable > manualDeclarables = Collections .synchronizedMap (new LinkedHashMap <>());
133
+
127
134
private String beanName ;
128
135
129
136
private RetryTemplate retryTemplate ;
@@ -142,6 +149,8 @@ public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, Applicat
142
149
143
150
private boolean explicitDeclarationsOnly ;
144
151
152
+ private boolean redeclareManualDeclarations ;
153
+
145
154
private volatile boolean running = false ;
146
155
147
156
private volatile DeclarationExceptionEvent lastDeclarationExceptionEvent ;
@@ -220,6 +229,9 @@ public void declareExchange(final Exchange exchange) {
220
229
try {
221
230
this .rabbitTemplate .execute (channel -> {
222
231
declareExchanges (channel , exchange );
232
+ if (this .redeclareManualDeclarations ) {
233
+ this .manualDeclarables .put (exchange .getName (), exchange );
234
+ }
223
235
return null ;
224
236
});
225
237
}
@@ -238,6 +250,7 @@ public boolean deleteExchange(final String exchangeName) {
238
250
239
251
try {
240
252
channel .exchangeDelete (exchangeName );
253
+ removeExchangeBindings (exchangeName );
241
254
}
242
255
catch (@ SuppressWarnings (UNUSED ) IOException e ) {
243
256
return false ;
@@ -246,6 +259,24 @@ public boolean deleteExchange(final String exchangeName) {
246
259
});
247
260
}
248
261
262
+ private void removeExchangeBindings (final String exchangeName ) {
263
+ this .manualDeclarables .remove (exchangeName );
264
+ synchronized (this .manualDeclarables ) {
265
+ Iterator <Entry <String , Declarable >> iterator = this .manualDeclarables .entrySet ().iterator ();
266
+ while (iterator .hasNext ()) {
267
+ Entry <String , Declarable > next = iterator .next ();
268
+ if (next .getValue () instanceof Binding ) {
269
+ Binding binding = (Binding ) next .getValue ();
270
+ if ((!binding .isDestinationQueue () && binding .getDestination ().equals (exchangeName ))
271
+ || binding .getExchange ().equals (exchangeName )) {
272
+ iterator .remove ();
273
+ }
274
+ }
275
+ }
276
+ }
277
+ }
278
+
279
+
249
280
// Queue operations
250
281
251
282
/**
@@ -266,7 +297,11 @@ public String declareQueue(final Queue queue) {
266
297
try {
267
298
return this .rabbitTemplate .execute (channel -> {
268
299
DeclareOk [] declared = declareQueues (channel , queue );
269
- return declared .length > 0 ? declared [0 ].getQueue () : null ;
300
+ String result = declared .length > 0 ? declared [0 ].getQueue () : null ;
301
+ if (this .redeclareManualDeclarations ) {
302
+ this .manualDeclarables .put (result , queue );
303
+ }
304
+ return result ;
270
305
});
271
306
}
272
307
catch (AmqpException e ) {
@@ -303,6 +338,7 @@ public boolean deleteQueue(final String queueName) {
303
338
return this .rabbitTemplate .execute (channel -> { // NOSONAR never returns null
304
339
try {
305
340
channel .queueDelete (queueName );
341
+ removeQueueBindings (queueName );
306
342
}
307
343
catch (@ SuppressWarnings (UNUSED ) IOException e ) {
308
344
return false ;
@@ -316,11 +352,28 @@ public boolean deleteQueue(final String queueName) {
316
352
"Delete a queue from the broker if unused and empty (when corresponding arguments are true" )
317
353
public void deleteQueue (final String queueName , final boolean unused , final boolean empty ) {
318
354
this .rabbitTemplate .execute (channel -> {
319
- channel .queueDelete (queueName , unused , empty );
355
+ DeleteOk queueDelete = channel .queueDelete (queueName , unused , empty );
356
+ removeQueueBindings (queueName );
320
357
return null ;
321
358
});
322
359
}
323
360
361
+ private void removeQueueBindings (final String queueName ) {
362
+ this .manualDeclarables .remove (queueName );
363
+ synchronized (this .manualDeclarables ) {
364
+ Iterator <Entry <String , Declarable >> iterator = this .manualDeclarables .entrySet ().iterator ();
365
+ while (iterator .hasNext ()) {
366
+ Entry <String , Declarable > next = iterator .next ();
367
+ if (next .getValue () instanceof Binding ) {
368
+ Binding binding = (Binding ) next .getValue ();
369
+ if (binding .isDestinationQueue () && binding .getDestination ().equals (queueName )) {
370
+ iterator .remove ();
371
+ }
372
+ }
373
+ }
374
+ }
375
+ }
376
+
324
377
@ Override
325
378
@ ManagedOperation (description = "Purge a queue and optionally don't wait for the purge to occur" )
326
379
public void purgeQueue (final String queueName , final boolean noWait ) {
@@ -352,6 +405,9 @@ public void declareBinding(final Binding binding) {
352
405
try {
353
406
this .rabbitTemplate .execute (channel -> {
354
407
declareBindings (channel , binding );
408
+ if (this .redeclareManualDeclarations ) {
409
+ this .manualDeclarables .put (binding .toString (), binding );
410
+ }
355
411
return null ;
356
412
});
357
413
}
@@ -377,6 +433,7 @@ public void removeBinding(final Binding binding) {
377
433
channel .exchangeUnbind (binding .getDestination (), binding .getExchange (), binding .getRoutingKey (),
378
434
binding .getArguments ());
379
435
}
436
+ this .manualDeclarables .remove (binding .toString ());
380
437
return null ;
381
438
});
382
439
}
@@ -444,6 +501,37 @@ public void setExplicitDeclarationsOnly(boolean explicitDeclarationsOnly) {
444
501
this .explicitDeclarationsOnly = explicitDeclarationsOnly ;
445
502
}
446
503
504
+ /**
505
+ * Normally, when a connection is recovered, the admin only recovers auto-delete queues,
506
+ * etc, that are declared as beans in the application context. When this is true, it
507
+ * will also redeclare any manually declared {@link Declarable}s via admin methods.
508
+ * @return true to redeclare.
509
+ * @since 2.4
510
+ */
511
+ public boolean isRedeclareManualDeclarations () {
512
+ return this .redeclareManualDeclarations ;
513
+ }
514
+
515
+ /**
516
+ * Normally, when a connection is recovered, the admin only recovers auto-delete
517
+ * queues, etc, that are declared as beans in the application context. When this is
518
+ * true, it will also redeclare any manually declared {@link Declarable}s via admin
519
+ * methods. When a queue or exhange is deleted, it will not longer be recovered, nor
520
+ * will any corresponding bindings.
521
+ * @param redeclareManualDeclarations true to redeclare.
522
+ * @since 2.4
523
+ * @see #declareQueue(Queue)
524
+ * @see #declareExchange(Exchange)
525
+ * @see #declareBinding(Binding)
526
+ * @see #deleteQueue(String)
527
+ * @see #deleteExchange(String)
528
+ * @see #removeBinding(Binding)
529
+ * @see #resetAllManualDeclarations()
530
+ */
531
+ public void setRedeclareManualDeclarations (boolean redeclareManualDeclarations ) {
532
+ this .redeclareManualDeclarations = redeclareManualDeclarations ;
533
+ }
534
+
447
535
/**
448
536
* Set a retry template for auto declarations. There is a race condition with
449
537
* auto-delete, exclusive queues in that the queue might still exist for a short time,
@@ -597,7 +685,7 @@ public void initialize() {
597
685
}
598
686
}
599
687
600
- if (exchanges .size () == 0 && queues .size () == 0 && bindings .size () == 0 ) {
688
+ if (exchanges .size () == 0 && queues .size () == 0 && bindings .size () == 0 && this . manualDeclarables . size () == 0 ) {
601
689
this .logger .debug ("Nothing to declare" );
602
690
return ;
603
691
}
@@ -607,10 +695,36 @@ public void initialize() {
607
695
declareBindings (channel , bindings .toArray (new Binding [bindings .size ()]));
608
696
return null ;
609
697
});
698
+ if (this .manualDeclarables .size () > 0 ) {
699
+ synchronized (this .manualDeclarables ) {
700
+ this .logger .debug ("Redeclaring manually declared Declarables" );
701
+ for (Declarable dec : this .manualDeclarables .values ()) {
702
+ if (dec instanceof Queue ) {
703
+ declareQueue ((Queue ) dec );
704
+ }
705
+ else if (dec instanceof Exchange ) {
706
+ declareExchange ((Exchange ) dec );
707
+ }
708
+ else {
709
+ declareBinding ((Binding ) dec );
710
+ }
711
+ }
712
+ }
713
+ }
610
714
this .logger .debug ("Declarations finished" );
611
715
612
716
}
613
717
718
+ /**
719
+ * Invoke this method to prevent the admin from recovering any declarations made
720
+ * by calls to {@code declare*()} methods.
721
+ * @since 2.4
722
+ * @see #setRedeclareManualDeclarations(boolean)
723
+ */
724
+ public void resetAllManualDeclarations () {
725
+ this .manualDeclarables .clear ();
726
+ }
727
+
614
728
private void processDeclarables (Collection <Exchange > contextExchanges , Collection <Queue > contextQueues ,
615
729
Collection <Binding > contextBindings ) {
616
730
0 commit comments