From 4e0361de2cb09ce27912d69d58df8b54fa05e9eb Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Mon, 28 Mar 2011 15:19:51 +0100 Subject: [PATCH] INT-136: Weaken checks for concurrent initialization --- .../amqp/rabbit/core/RabbitAdmin.java | 21 +++++---- .../core/RabbitAdminIntegrationTests.java | 44 +++++++++++++++++++ 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitAdmin.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitAdmin.java index 9eefac05..e151ec62 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitAdmin.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/core/RabbitAdmin.java @@ -207,24 +207,27 @@ public class RabbitAdmin implements AmqpAdmin, ApplicationContextAware, Initiali return; } - // Prevent stack overflow... - final AtomicBoolean initializing = new AtomicBoolean(false); - connectionFactory.addConnectionListener(new ConnectionListener() { - private volatile boolean initialized = false; + // Prevent stack overflow... + private AtomicBoolean initializing = new AtomicBoolean(false); public void onCreate(Connection connection) { - if (!initializing.compareAndSet(false, true) || initialized) { + if (!initializing.compareAndSet(false, true)) { + // If we are already initializing, we don't need to do it again... return; } - initialize(); - initializing.compareAndSet(true, false); - initialized = true; + try { + // ...but it is possible for this to happen twice in the same ConnectionFactory (if more than + // one concurrent Connection is allowed). It's idempotent, so no big deal (a bit of network + // chatter). If anyone has a problem with it: use auto-startup="false". + initialize(); + } finally { + initializing.compareAndSet(true, false); + } } public void onClose(Connection connection) { - initialized = false; } }); diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitAdminIntegrationTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitAdminIntegrationTests.java index b2e46ad7..b91eb4b4 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitAdminIntegrationTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/core/RabbitAdminIntegrationTests.java @@ -101,6 +101,50 @@ public class RabbitAdminIntegrationTests { } + @Test + public void testStartupWithNonDurable() throws Exception { + + final Queue queue = new Queue("test.queue", false, false, false); + context.getBeanFactory().registerSingleton("foo", queue); + rabbitAdmin.deleteQueue(queue.getName()); + rabbitAdmin.afterPropertiesSet(); + + final AtomicReference connectionHolder = new AtomicReference(); + + RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); + // Force RabbitAdmin to initialize the queue + boolean exists = rabbitTemplate.execute(new ChannelCallback() { + public Boolean doInRabbit(Channel channel) throws Exception { + DeclareOk result = channel.queueDeclarePassive(queue.getName()); + connectionHolder.set(channel.getConnection()); + return result != null; + } + }); + assertTrue("Expected Queue to exist", exists); + + assertTrue(queueExists(connectionHolder.get(), queue)); + + // simulate broker going down and coming back up... + rabbitAdmin.deleteQueue(queue.getName()); + connectionFactory.destroy(); + assertFalse(queueExists(null, queue)); + + // Broker auto-deleted queue, but it is re-created by the connection listener + exists = rabbitTemplate.execute(new ChannelCallback() { + public Boolean doInRabbit(Channel channel) throws Exception { + DeclareOk result = channel.queueDeclarePassive(queue.getName()); + connectionHolder.set(channel.getConnection()); + return result != null; + } + }); + assertTrue("Expected Queue to exist", exists); + + assertTrue(queueExists(connectionHolder.get(), queue)); + assertTrue(rabbitAdmin.deleteQueue(queue.getName())); + assertFalse(queueExists(null, queue)); + + } + /** * Use native Rabbit API to test queue, bypassing all the connection and channel caching and callbacks in Spring * AMQP.