/* * Copyright 2014-2017 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.integration.amqp.channel; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.instanceOf; import static org.junit.Assert.assertEquals; 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 static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.willThrow; import static org.mockito.Mockito.mock; import java.util.Collection; import java.util.Map; import java.util.Set; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import org.junit.After; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.springframework.amqp.core.AmqpTemplate; import org.springframework.amqp.core.MessageListener; import org.springframework.amqp.core.Queue; import org.springframework.amqp.rabbit.connection.CachingConnectionFactory; import org.springframework.amqp.rabbit.core.RabbitAdmin; import org.springframework.amqp.rabbit.core.RabbitTemplate; import org.springframework.amqp.rabbit.junit.BrokerRunning; import org.springframework.amqp.rabbit.listener.BlockingQueueConsumer; import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer; import org.springframework.amqp.support.converter.MessageConversionException; import org.springframework.amqp.support.converter.MessageConverter; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.integration.amqp.config.AmqpChannelFactoryBean; import org.springframework.integration.amqp.support.AmqpHeaderMapper; import org.springframework.integration.support.MessageBuilder; import org.springframework.integration.test.util.TestUtils; import org.springframework.messaging.Message; import org.springframework.messaging.PollableChannel; import org.springframework.messaging.support.GenericMessage; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @author Gary Russell * @author Artem Bilan * * @since 4.0 * */ @ContextConfiguration @RunWith(SpringJUnit4ClassRunner.class) @DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD) public class ChannelTests { @ClassRule public static final BrokerRunning brokerIsRunning = BrokerRunning.isRunningWithEmptyQueues("pollableWithEP", "withEP", "testConvertFail"); @Rule public ExpectedException exception = ExpectedException.none(); @Autowired private PublishSubscribeAmqpChannel channel; @Autowired private PollableAmqpChannel pollableWithEP; @Autowired private PointToPointSubscribableAmqpChannel withEP; @Autowired private PublishSubscribeAmqpChannel pubSubWithEP; @Autowired private PollableChannel out; @Autowired private CachingConnectionFactory factory; @Autowired private AmqpHeaderMapper mapperIn; @Autowired private AmqpHeaderMapper mapperOut; @After public void tearDown() { brokerIsRunning.deleteExchanges("si.fanout.foo", "si.fanout.channel", "si.fanout.pubSubWithEP"); brokerIsRunning.removeTestQueues(); } @Test @DirtiesContext public void pubSubLostConnectionTest() throws Exception { final CyclicBarrier latch = new CyclicBarrier(2); channel.subscribe(message -> { try { latch.await(10, TimeUnit.SECONDS); } catch (Exception e) { } }); this.channel.send(new GenericMessage<String>("foo")); latch.await(10, TimeUnit.SECONDS); latch.reset(); BlockingQueueConsumer consumer = (BlockingQueueConsumer) TestUtils.getPropertyValue(this.channel, "container.consumers", Set.class).iterator().next(); factory.destroy(); waitForNewConsumer(this.channel, consumer); this.channel.send(new GenericMessage<String>("bar")); latch.await(10, TimeUnit.SECONDS); this.channel.destroy(); this.pubSubWithEP.destroy(); assertEquals(0, TestUtils.getPropertyValue(factory, "connectionListener.delegates", Collection.class).size()); } @SuppressWarnings("unchecked") private void waitForNewConsumer(PublishSubscribeAmqpChannel channel, BlockingQueueConsumer consumer) throws Exception { final Object consumersMonitor = TestUtils.getPropertyValue(channel, "container.consumersMonitor"); int n = 0; while (n++ < 100) { Set<BlockingQueueConsumer> consumers = TestUtils.getPropertyValue(channel, "container.consumers", Set.class); synchronized (consumersMonitor) { if (!consumers.isEmpty()) { BlockingQueueConsumer newConsumer = consumers.iterator().next(); if (newConsumer != consumer && TestUtils.getPropertyValue(newConsumer, "consumerTags", Map.class).size() > 0) { break; } } } Thread.sleep(100); } assertTrue("Failed to restart consumer", n < 100); } /* * Verify queue is declared if not present and not declared if it is already present. */ @Test public void channelDeclarationTests() { RabbitAdmin admin = new RabbitAdmin(this.factory); admin.deleteQueue("implicit"); SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(this.factory); AmqpTemplate amqpTemplate = mock(AmqpTemplate.class); PointToPointSubscribableAmqpChannel channel = new PointToPointSubscribableAmqpChannel("implicit", container, amqpTemplate); channel.setBeanFactory(mock(BeanFactory.class)); channel.afterPropertiesSet(); assertNotNull(admin.getQueueProperties("implicit")); admin.deleteQueue("implicit"); admin.deleteQueue("explicit"); channel.setQueueName("explicit"); channel.afterPropertiesSet(); assertNotNull(admin.getQueueProperties("explicit")); admin.deleteQueue("explicit"); admin.declareQueue(new Queue("explicit", false)); // verify no declaration if exists with non-standard props channel.afterPropertiesSet(); assertNotNull(admin.getQueueProperties("explicit")); admin.deleteQueue("explicit"); } @Test public void testAmqpChannelFactoryBean() throws Exception { AmqpChannelFactoryBean channelFactoryBean = new AmqpChannelFactoryBean(); channelFactoryBean.setBeanFactory(mock(BeanFactory.class)); channelFactoryBean.setConnectionFactory(this.factory); channelFactoryBean.setBeanName("testChannel"); channelFactoryBean.afterPropertiesSet(); AbstractAmqpChannel channel = channelFactoryBean.getObject(); assertThat(channel, instanceOf(PointToPointSubscribableAmqpChannel.class)); channelFactoryBean = new AmqpChannelFactoryBean(); channelFactoryBean.setBeanFactory(mock(BeanFactory.class)); channelFactoryBean.setConnectionFactory(this.factory); channelFactoryBean.setBeanName("testChannel"); channelFactoryBean.setPubSub(true); channelFactoryBean.afterPropertiesSet(); channel = channelFactoryBean.getObject(); assertThat(channel, instanceOf(PublishSubscribeAmqpChannel.class)); RabbitAdmin rabbitAdmin = new RabbitAdmin(this.factory); rabbitAdmin.deleteQueue("testChannel"); rabbitAdmin.deleteExchange("si.fanout.testChannel"); } @Test public void extractPayloadTests() throws Exception { Foo foo = new Foo("bar"); Message<?> message = MessageBuilder.withPayload(foo).setHeader("baz", "qux").build(); this.pollableWithEP.send(message); Message<?> received = this.pollableWithEP.receive(10000); assertNotNull(received); assertThat(received.getPayload(), equalTo(foo)); assertThat(received.getHeaders().get("baz"), equalTo("qux")); this.withEP.send(message); received = this.out.receive(10000); assertNotNull(received); assertThat(received.getPayload(), equalTo(foo)); assertThat(received.getHeaders().get("baz"), equalTo("qux")); this.pubSubWithEP.send(message); received = this.out.receive(10000); assertNotNull(received); assertThat(received.getPayload(), equalTo(foo)); assertThat(received.getHeaders().get("baz"), equalTo("qux")); assertSame(this.mapperIn, TestUtils.getPropertyValue(this.pollableWithEP, "inboundHeaderMapper")); assertSame(this.mapperOut, TestUtils.getPropertyValue(this.pollableWithEP, "outboundHeaderMapper")); } @Test public void messageConversionTests() throws Exception { RabbitTemplate amqpTemplate = new RabbitTemplate(this.factory); MessageConverter messageConverter = mock(MessageConverter.class); amqpTemplate.setMessageConverter(messageConverter); PointToPointSubscribableAmqpChannel channel = new PointToPointSubscribableAmqpChannel("testConvertFail", new SimpleMessageListenerContainer(this.factory), amqpTemplate); channel.afterPropertiesSet(); MessageListener listener = TestUtils.getPropertyValue(channel, "container.messageListener", MessageListener.class); willThrow(new MessageConversionException("foo", new IllegalStateException("bar"))) .given(messageConverter).fromMessage(any(org.springframework.amqp.core.Message.class)); this.exception.expect(MessageConversionException.class); this.exception.expectCause(instanceOf(IllegalStateException.class)); listener.onMessage(mock(org.springframework.amqp.core.Message.class)); } public static class Foo { private String bar; public Foo() { } public Foo(String bar) { this.bar = bar; } public String getBar() { return this.bar; } public void setBar(String bar) { this.bar = bar; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((this.bar == null) ? 0 : this.bar.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Foo other = (Foo) obj; if (this.bar == null) { if (other.bar != null) { return false; } } else if (!this.bar.equals(other.bar)) { return false; } return true; } } }