/* * Copyright 2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.amqp.rabbit.listener; import static org.hamcrest.Matchers.equalTo; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.springframework.amqp.core.Address; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.core.ChannelAwareMessageListener; import org.springframework.amqp.rabbit.junit.BrokerRunning; import org.springframework.amqp.rabbit.listener.DirectReplyToMessageListenerContainer.ChannelHolder; import org.springframework.amqp.utils.test.TestUtils; import org.springframework.beans.DirectFieldAccessor; import com.rabbitmq.client.AMQP.BasicProperties; import com.rabbitmq.client.Channel; import com.rabbitmq.client.GetResponse; /** * DirectReplyToMessageListenerContainer Tests. * * @author Gary Russell * @since 2.0 * */ public class DirectReplyToMessageListenerContainerTests { private static final String TEST_RELEASE_CONSUMER_Q = "test.release.consumer"; @Rule public BrokerRunning brokerRunning = BrokerRunning.isRunningWithEmptyQueues(TEST_RELEASE_CONSUMER_Q); @After public void tearDown() { this.brokerRunning.removeTestQueues(); } @Test public void testReleaseConsumerRace() throws Exception { CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost"); DirectReplyToMessageListenerContainer container = new DirectReplyToMessageListenerContainer(connectionFactory); final CountDownLatch latch = new CountDownLatch(1); // Populate void MessageListener for wrapping in the DirectReplyToMessageListenerContainer container.setMessageListener(m -> { }); // Extract actual ChannelAwareMessageListener from container // with the inUseConsumerChannels.remove(channel); operation final ChannelAwareMessageListener messageListener = TestUtils.getPropertyValue(container, "messageListener", ChannelAwareMessageListener.class); // Wrap actual listener for latch barrier exactly after inUseConsumerChannels.remove(channel); ChannelAwareMessageListener mockMessageListener = (message, channel) -> { try { messageListener.onMessage(message, channel); } finally { latch.countDown(); } }; // Populated mocked listener via reflection new DirectFieldAccessor(container) .setPropertyValue("messageListener", mockMessageListener); container.start(); ChannelHolder channel1 = container.getChannelHolder(); BasicProperties props = new BasicProperties().builder().replyTo(Address.AMQ_RABBITMQ_REPLY_TO).build(); channel1.getChannel().basicPublish("", TEST_RELEASE_CONSUMER_Q, props, "foo".getBytes()); Channel replyChannel = connectionFactory.createConnection().createChannel(false); GetResponse request = replyChannel.basicGet(TEST_RELEASE_CONSUMER_Q, true); int n = 0; while (n++ < 100 && request == null) { Thread.sleep(100); request = replyChannel.basicGet(TEST_RELEASE_CONSUMER_Q, true); } assertNotNull(request); replyChannel.basicPublish("", request.getProps().getReplyTo(), new BasicProperties(), "bar".getBytes()); replyChannel.close(); assertTrue(latch.await(10, TimeUnit.SECONDS)); ChannelHolder channel2 = container.getChannelHolder(); assertSame(channel1.getChannel(), channel2.getChannel()); container.releaseConsumerFor(channel1, false, null); // simulate race for future timeout/cancel and onMessage() Map<?, ?> inUse = TestUtils.getPropertyValue(container, "inUseConsumerChannels", Map.class); assertThat(inUse.size(), equalTo(1)); container.releaseConsumerFor(channel2, false, null); assertThat(inUse.size(), equalTo(0)); container.stop(); connectionFactory.destroy(); } }