org.springframework.integration.amqp.config.AmqpOutboundChannelAdapterParserTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.integration.amqp.config.AmqpOutboundChannelAdapterParserTests.java

Source

/*
 * Copyright 2002-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.config;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.logging.Log;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.mockito.internal.stubbing.answers.DoesNothing;

import org.springframework.amqp.core.MessageDeliveryMode;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.support.CorrelationData;
import org.springframework.amqp.rabbit.support.PublisherCallbackChannelImpl;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.beans.BeansException;
import org.springframework.beans.DirectFieldAccessor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.parsing.BeanDefinitionParsingException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.amqp.outbound.AmqpOutboundEndpoint;
import org.springframework.integration.amqp.support.AmqpHeaderMapper;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.NullChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.context.NamedComponent;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.ReflectionUtils;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;

/**
 * @author Mark Fisher
 * @author Oleg Zhurakousky
 * @author Gary Russell
 * @author Artem Bilan
 * @author Gunnar Hillert
 * @since 2.1
 */
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class AmqpOutboundChannelAdapterParserTests {

    private static volatile int adviceCalled;

    @Autowired
    private ApplicationContext context;

    @Autowired
    @Qualifier("withCustomHeaderMapper.handler")
    private MessageHandler amqpMessageHandlerWithCustomHeaderMapper;

    @Test
    public void verifyIdAsChannel() {
        Object channel = context.getBean("rabbitOutbound");
        Object adapter = context.getBean("rabbitOutbound.adapter");
        assertEquals(DirectChannel.class, channel.getClass());
        assertEquals(EventDrivenConsumer.class, adapter.getClass());
        MessageHandler handler = TestUtils.getPropertyValue(adapter, "handler", MessageHandler.class);
        assertTrue(handler instanceof NamedComponent);
        assertEquals("amqp:outbound-channel-adapter", ((NamedComponent) handler).getComponentType());
        handler.handleMessage(new GenericMessage<String>("foo"));
        assertEquals(1, adviceCalled);
        assertTrue(TestUtils.getPropertyValue(handler, "lazyConnect", Boolean.class));
    }

    @Test
    public void withHeaderMapperCustomHeaders() {
        Object eventDrivenConsumer = context.getBean("withHeaderMapperCustomHeaders");

        AmqpOutboundEndpoint endpoint = TestUtils.getPropertyValue(eventDrivenConsumer, "handler",
                AmqpOutboundEndpoint.class);
        assertNotNull(TestUtils.getPropertyValue(endpoint, "defaultDeliveryMode"));
        assertFalse(TestUtils.getPropertyValue(endpoint, "lazyConnect", Boolean.class));
        assertEquals("42", TestUtils
                .getPropertyValue(endpoint, "delayExpression", org.springframework.expression.Expression.class)
                .getExpressionString());

        Field amqpTemplateField = ReflectionUtils.findField(AmqpOutboundEndpoint.class, "amqpTemplate");
        amqpTemplateField.setAccessible(true);
        RabbitTemplate amqpTemplate = TestUtils.getPropertyValue(endpoint, "amqpTemplate", RabbitTemplate.class);
        amqpTemplate = Mockito.spy(amqpTemplate);
        final AtomicBoolean shouldBePersistent = new AtomicBoolean();

        Mockito.doAnswer(invocation -> {
            Object[] args = invocation.getArguments();
            org.springframework.amqp.core.Message amqpMessage = (org.springframework.amqp.core.Message) args[2];
            MessageProperties properties = amqpMessage.getMessageProperties();
            assertEquals("foo", properties.getHeaders().get("foo"));
            assertEquals("foobar", properties.getHeaders().get("foobar"));
            assertNull(properties.getHeaders().get("bar"));
            assertEquals(
                    shouldBePersistent.get() ? MessageDeliveryMode.PERSISTENT : MessageDeliveryMode.NON_PERSISTENT,
                    properties.getDeliveryMode());
            return null;
        }).when(amqpTemplate).send(Mockito.any(String.class), Mockito.any(String.class),
                Mockito.any(org.springframework.amqp.core.Message.class), Mockito.any(CorrelationData.class));
        ReflectionUtils.setField(amqpTemplateField, endpoint, amqpTemplate);

        MessageChannel requestChannel = context.getBean("requestChannel", MessageChannel.class);
        Message<?> message = MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("bar", "bar")
                .setHeader("foobar", "foobar").build();
        requestChannel.send(message);
        Mockito.verify(amqpTemplate, Mockito.times(1)).send(anyString(), isNull(),
                Mockito.any(org.springframework.amqp.core.Message.class), isNull());

        shouldBePersistent.set(true);
        message = MessageBuilder.withPayload("hello").setHeader("foo", "foo").setHeader("bar", "bar")
                .setHeader("foobar", "foobar").setHeader(AmqpHeaders.DELIVERY_MODE, MessageDeliveryMode.PERSISTENT)
                .build();
        requestChannel.send(message);
    }

    @Test
    public void parseWithPublisherConfirms() {
        Object eventDrivenConsumer = context.getBean("withPublisherConfirms");
        AmqpOutboundEndpoint endpoint = TestUtils.getPropertyValue(eventDrivenConsumer, "handler",
                AmqpOutboundEndpoint.class);
        NullChannel nullChannel = context.getBean(NullChannel.class);
        MessageChannel ackChannel = context.getBean("ackChannel", MessageChannel.class);
        assertSame(ackChannel, TestUtils.getPropertyValue(endpoint, "confirmAckChannel"));
        assertSame(nullChannel, TestUtils.getPropertyValue(endpoint, "confirmNackChannel"));
    }

    @SuppressWarnings("rawtypes")
    @Test
    public void amqpOutboundChannelAdapterWithinChain() {
        Object eventDrivenConsumer = context.getBean("chainWithRabbitOutbound");

        List chainHandlers = TestUtils.getPropertyValue(eventDrivenConsumer, "handler.handlers", List.class);

        AmqpOutboundEndpoint endpoint = (AmqpOutboundEndpoint) chainHandlers.get(0);
        assertNull(TestUtils.getPropertyValue(endpoint, "defaultDeliveryMode"));

        Field amqpTemplateField = ReflectionUtils.findField(AmqpOutboundEndpoint.class, "amqpTemplate");
        amqpTemplateField.setAccessible(true);
        RabbitTemplate amqpTemplate = TestUtils.getPropertyValue(endpoint, "amqpTemplate", RabbitTemplate.class);
        amqpTemplate = Mockito.spy(amqpTemplate);

        Mockito.doAnswer(invocation -> {
            Object[] args = invocation.getArguments();
            org.springframework.amqp.core.Message amqpMessage = (org.springframework.amqp.core.Message) args[2];
            MessageProperties properties = amqpMessage.getMessageProperties();
            assertEquals("hello", new String(amqpMessage.getBody()));
            assertEquals(MessageDeliveryMode.PERSISTENT, properties.getDeliveryMode());
            return null;
        }).when(amqpTemplate).send(Mockito.any(String.class), Mockito.any(String.class),
                Mockito.any(org.springframework.amqp.core.Message.class), Mockito.any(CorrelationData.class));
        ReflectionUtils.setField(amqpTemplateField, endpoint, amqpTemplate);

        MessageChannel requestChannel = context.getBean("amqpOutboundChannelAdapterWithinChain",
                MessageChannel.class);
        Message<?> message = MessageBuilder.withPayload("hello").build();
        requestChannel.send(message);
        Mockito.verify(amqpTemplate, Mockito.times(1)).send(Mockito.any(String.class), isNull(),
                Mockito.any(org.springframework.amqp.core.Message.class), isNull());
    }

    @Test
    public void testInt2718FailForOutboundAdapterChannelAttribute() {
        try {
            new ClassPathXmlApplicationContext("AmqpOutboundChannelAdapterWithinChainParserTests-fail-context.xml",
                    this.getClass()).close();
            fail("Expected BeanDefinitionParsingException");
        } catch (BeansException e) {
            assertTrue(e instanceof BeanDefinitionParsingException);
            assertTrue(e.getMessage().contains("The 'channel' attribute isn't allowed for "
                    + "'amqp:outbound-channel-adapter' when it is used as a nested element"));
        }
    }

    @Test
    public void testInt2773UseDefaultAmqpTemplateExchangeAndRoutingLey() throws IOException {
        ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class);
        Connection mockConnection = mock(Connection.class);
        Channel mockChannel = mock(Channel.class);

        when(connectionFactory.createConnection()).thenReturn(mockConnection);
        PublisherCallbackChannelImpl publisherCallbackChannel = new PublisherCallbackChannelImpl(mockChannel);
        when(mockConnection.createChannel(false)).thenReturn(publisherCallbackChannel);

        MessageChannel requestChannel = context.getBean("toRabbitOnlyWithTemplateChannel", MessageChannel.class);
        requestChannel.send(MessageBuilder.withPayload("test").build());
        Mockito.verify(mockChannel, Mockito.times(1)).basicPublish(Mockito.eq("default.test.exchange"),
                Mockito.eq("default.routing.key"), Mockito.anyBoolean(), Mockito.any(BasicProperties.class),
                Mockito.any(byte[].class));
    }

    @Test
    public void testInt2773WithDefaultAmqpTemplateExchangeAndRoutingKey() throws IOException {
        ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class);
        Connection mockConnection = mock(Connection.class);
        Channel mockChannel = mock(Channel.class);

        when(connectionFactory.createConnection()).thenReturn(mockConnection);
        PublisherCallbackChannelImpl publisherCallbackChannel = new PublisherCallbackChannelImpl(mockChannel);
        when(mockConnection.createChannel(false)).thenReturn(publisherCallbackChannel);

        MessageChannel requestChannel = context.getBean("withDefaultAmqpTemplateExchangeAndRoutingKey",
                MessageChannel.class);
        requestChannel.send(MessageBuilder.withPayload("test").build());
        Mockito.verify(mockChannel, Mockito.times(1)).basicPublish(Mockito.eq(""), Mockito.eq(""),
                Mockito.anyBoolean(), Mockito.any(BasicProperties.class), Mockito.any(byte[].class));
    }

    @Test
    public void testInt2773WithOverrideToDefaultAmqpTemplateExchangeAndRoutingLey() throws IOException {
        ConnectionFactory connectionFactory = context.getBean(ConnectionFactory.class);
        Connection mockConnection = mock(Connection.class);
        Channel mockChannel = mock(Channel.class);

        when(connectionFactory.createConnection()).thenReturn(mockConnection);
        PublisherCallbackChannelImpl publisherCallbackChannel = new PublisherCallbackChannelImpl(mockChannel);
        when(mockConnection.createChannel(false)).thenReturn(publisherCallbackChannel);

        MessageChannel requestChannel = context.getBean("overrideTemplateAttributesToEmpty", MessageChannel.class);
        requestChannel.send(MessageBuilder.withPayload("test").build());
        Mockito.verify(mockChannel, Mockito.times(1)).basicPublish(Mockito.eq(""), Mockito.eq(""),
                Mockito.anyBoolean(), Mockito.any(BasicProperties.class), Mockito.any(byte[].class));
    }

    @Test
    public void testInt2971HeaderMapperAndMappedHeadersExclusivity() {
        try {
            new ClassPathXmlApplicationContext(
                    "AmqpOutboundChannelAdapterParserTests-headerMapper-fail-context.xml", this.getClass()).close();
        } catch (BeanDefinitionParsingException e) {
            assertTrue(e.getMessage().startsWith("Configuration problem: The 'header-mapper' attribute "
                    + "is mutually exclusive with 'mapped-request-headers' or 'mapped-reply-headers'"));
        }
    }

    @Test
    public void testInt2971AmqpOutboundChannelAdapterWithCustomHeaderMapper() {
        AmqpHeaderMapper headerMapper = TestUtils.getPropertyValue(this.amqpMessageHandlerWithCustomHeaderMapper,
                "headerMapper", AmqpHeaderMapper.class);
        assertSame(this.context.getBean("customHeaderMapper"), headerMapper);
    }

    @Test
    public void testInt3430FailForNotLazyConnect() {
        RabbitTemplate amqpTemplate = spy(new RabbitTemplate());
        ConnectionFactory connectionFactory = mock(ConnectionFactory.class);
        RuntimeException toBeThrown = new RuntimeException("Test Connection Exception");
        doThrow(toBeThrown).when(connectionFactory).createConnection();
        when(amqpTemplate.getConnectionFactory()).thenReturn(connectionFactory);
        AmqpOutboundEndpoint handler = new AmqpOutboundEndpoint(amqpTemplate);
        Log logger = spy(TestUtils.getPropertyValue(handler, "logger", Log.class));
        new DirectFieldAccessor(handler).setPropertyValue("logger", logger);
        doAnswer(new DoesNothing()).when(logger).error("Failed to eagerly establish the connection.", toBeThrown);
        ApplicationContext context = mock(ApplicationContext.class);
        handler.setApplicationContext(context);
        handler.setBeanFactory(context);
        handler.afterPropertiesSet();
        handler.start();
        handler.stop();
        verify(logger, never()).error(anyString(), any(RuntimeException.class));
        handler.setLazyConnect(false);
        handler.start();
        verify(logger).error("Failed to eagerly establish the connection.", toBeThrown);
        handler.stop();
    }

    public static class FooAdvice extends AbstractRequestHandlerAdvice {

        @Override
        protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
            adviceCalled++;
            return null;
        }

    }

}