org.springframework.cloud.stream.binder.AbstractBinderTests.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.cloud.stream.binder.AbstractBinderTests.java

Source

/*
 * Copyright 2013-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.cloud.stream.binder;

import java.util.UUID;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Test;

import org.springframework.cloud.stream.binding.MessageConverterConfigurer;
import org.springframework.cloud.stream.config.BindingProperties;
import org.springframework.cloud.stream.config.BindingServiceProperties;
import org.springframework.cloud.stream.converter.CompositeMessageConverterFactory;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.Lifecycle;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.PollableChannel;
import org.springframework.util.MimeTypeUtils;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * @author Gary Russell
 * @author Ilayaperumal Gopinathan
 * @author David Turanski
 * @author Mark Fisher
 * @author Marius Bogoevici
 */
@SuppressWarnings("unchecked")
public abstract class AbstractBinderTests<B extends AbstractTestBinder<? extends AbstractBinder<MessageChannel, CP, PP>, CP, PP>, CP extends ConsumerProperties, PP extends ProducerProperties> {

    protected final Log logger = LogFactory.getLog(this.getClass());

    protected B testBinder;

    /**
     * Subclasses may override this default value to have tests wait longer for a message receive, for example if
     * running
     * in an environment that is known to be slow (e.g. travis).
     */
    protected double timeoutMultiplier = 1.0D;

    /**
     * Attempt to receive a message on the given channel,
     * waiting up to 1s (times the {@link #timeoutMultiplier}).
     */
    protected Message<?> receive(PollableChannel channel) {
        return receive(channel, 1);
    }

    /**
     * Attempt to receive a message on the given channel,
     * waiting up to 1s * additionalMultiplier * {@link #timeoutMultiplier}).
     *
     * Allows accomodating tests which are slower than normal (e.g. retry).
     */
    protected Message<?> receive(PollableChannel channel, int additionalMultiplier) {
        long startTime = System.currentTimeMillis();
        Message<?> receive = channel.receive((int) (1000 * timeoutMultiplier * additionalMultiplier));
        long elapsed = System.currentTimeMillis() - startTime;
        logger.debug("receive() took " + elapsed / 1000 + " seconds");
        return receive;
    }

    @Test
    public void testClean() throws Exception {
        Binder binder = getBinder();
        Binding<MessageChannel> foo0ProducerBinding = binder.bindProducer("foo.0", new DirectChannel(),
                createProducerProperties());
        Binding<MessageChannel> foo0ConsumerBinding = binder.bindConsumer("foo.0", "test", new DirectChannel(),
                createConsumerProperties());
        Binding<MessageChannel> foo1ProducerBinding = binder.bindProducer("foo.1", new DirectChannel(),
                createProducerProperties());
        Binding<MessageChannel> foo1ConsumerBinding = binder.bindConsumer("foo.1", "test", new DirectChannel(),
                createConsumerProperties());
        Binding<MessageChannel> foo2ProducerBinding = binder.bindProducer("foo.2", new DirectChannel(),
                createProducerProperties());
        foo0ProducerBinding.unbind();
        assertThat(TestUtils.getPropertyValue(foo0ProducerBinding, "lifecycle", Lifecycle.class).isRunning())
                .isFalse();
        foo0ConsumerBinding.unbind();
        foo1ProducerBinding.unbind();
        assertThat(TestUtils.getPropertyValue(foo0ConsumerBinding, "lifecycle", Lifecycle.class).isRunning())
                .isFalse();
        assertThat(TestUtils.getPropertyValue(foo1ProducerBinding, "lifecycle", Lifecycle.class).isRunning())
                .isFalse();
        foo1ConsumerBinding.unbind();
        foo2ProducerBinding.unbind();
        assertThat(TestUtils.getPropertyValue(foo1ConsumerBinding, "lifecycle", Lifecycle.class).isRunning())
                .isFalse();
        assertThat(TestUtils.getPropertyValue(foo2ProducerBinding, "lifecycle", Lifecycle.class).isRunning())
                .isFalse();
    }

    @Test
    public void testSendAndReceive() throws Exception {
        Binder binder = getBinder();
        BindingProperties outputBindingProperties = createProducerBindingProperties(createProducerProperties());
        DirectChannel moduleOutputChannel = createBindableChannel("output", outputBindingProperties);
        QueueChannel moduleInputChannel = new QueueChannel();
        Binding<MessageChannel> producerBinding = binder.bindProducer("foo.0", moduleOutputChannel,
                outputBindingProperties.getProducer());
        Binding<MessageChannel> consumerBinding = binder.bindConsumer("foo.0", "test", moduleInputChannel,
                createConsumerProperties());
        Message<?> message = MessageBuilder.withPayload("foo").setHeader(MessageHeaders.CONTENT_TYPE, "foo/bar")
                .build();
        // Let the consumer actually bind to the producer before sending a msg
        binderBindUnbindLatency();
        moduleOutputChannel.send(message);
        Message<?> inbound = receive(moduleInputChannel);
        assertThat(inbound).isNotNull();
        assertThat(inbound.getPayload()).isEqualTo("foo");
        assertThat(inbound.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull();
        assertThat(inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo("foo/bar");
        producerBinding.unbind();
        consumerBinding.unbind();
    }

    @Test
    public void testSendAndReceiveMultipleTopics() throws Exception {
        Binder binder = getBinder();

        DirectChannel moduleOutputChannel1 = createBindableChannel("output1",
                createProducerBindingProperties(createProducerProperties()));
        DirectChannel moduleOutputChannel2 = createBindableChannel("output2",
                createProducerBindingProperties(createProducerProperties()));

        QueueChannel moduleInputChannel = new QueueChannel();

        Binding<MessageChannel> producerBinding1 = binder.bindProducer("foo.x", moduleOutputChannel1,
                createProducerProperties());
        Binding<MessageChannel> producerBinding2 = binder.bindProducer("foo.y", moduleOutputChannel2,
                createProducerProperties());

        Binding<MessageChannel> consumerBinding1 = binder.bindConsumer("foo.x", "test", moduleInputChannel,
                createConsumerProperties());
        Binding<MessageChannel> consumerBinding2 = binder.bindConsumer("foo.y", "test", moduleInputChannel,
                createConsumerProperties());

        String testPayload1 = "foo" + UUID.randomUUID().toString();
        Message<?> message1 = MessageBuilder.withPayload(testPayload1.getBytes()).build();
        String testPayload2 = "foo" + UUID.randomUUID().toString();
        Message<?> message2 = MessageBuilder.withPayload(testPayload2.getBytes()).build();

        // Let the consumer actually bind to the producer before sending a msg
        binderBindUnbindLatency();
        moduleOutputChannel1.send(message1);
        moduleOutputChannel2.send(message2);

        Message<?>[] messages = new Message[2];
        messages[0] = receive(moduleInputChannel);
        messages[1] = receive(moduleInputChannel);

        assertThat(messages[0]).isNotNull();
        assertThat(messages[1]).isNotNull();
        assertThat(messages).extracting("payload").containsExactlyInAnyOrder(testPayload1.getBytes(),
                testPayload2.getBytes());

        producerBinding1.unbind();
        producerBinding2.unbind();

        consumerBinding1.unbind();
        consumerBinding2.unbind();
    }

    @Test
    public void testSendAndReceiveNoOriginalContentType() throws Exception {
        Binder binder = getBinder();

        BindingProperties producerBindingProperties = createProducerBindingProperties(createProducerProperties());
        DirectChannel moduleOutputChannel = createBindableChannel("output", producerBindingProperties);
        QueueChannel moduleInputChannel = new QueueChannel();
        Binding<MessageChannel> producerBinding = binder.bindProducer("bar.0", moduleOutputChannel,
                producerBindingProperties.getProducer());
        Binding<MessageChannel> consumerBinding = binder.bindConsumer("bar.0", "test", moduleInputChannel,
                createConsumerProperties());
        binderBindUnbindLatency();

        Message<?> message = MessageBuilder.withPayload("foo").build();
        moduleOutputChannel.send(message);
        Message<?> inbound = receive(moduleInputChannel);
        assertThat(inbound).isNotNull();
        assertThat(inbound.getPayload()).isEqualTo("foo");
        assertThat(inbound.getHeaders().get(BinderHeaders.BINDER_ORIGINAL_CONTENT_TYPE)).isNull();
        assertThat(inbound.getHeaders().get(MessageHeaders.CONTENT_TYPE)).isEqualTo(MimeTypeUtils.TEXT_PLAIN_VALUE);
        producerBinding.unbind();
        consumerBinding.unbind();
    }

    protected abstract B getBinder() throws Exception;

    protected abstract CP createConsumerProperties();

    protected abstract PP createProducerProperties();

    protected final BindingProperties createConsumerBindingProperties(CP consumerProperties) {
        BindingProperties bindingProperties = new BindingProperties();
        bindingProperties.setConsumer(consumerProperties);
        return bindingProperties;
    }

    protected BindingProperties createProducerBindingProperties(PP producerProperties) {
        BindingProperties bindingProperties = new BindingProperties();
        bindingProperties.setProducer(producerProperties);
        return bindingProperties;
    }

    protected DirectChannel createBindableChannel(String channelName, BindingProperties bindingProperties)
            throws Exception {
        BindingServiceProperties bindingServiceProperties = new BindingServiceProperties();
        bindingServiceProperties.getBindings().put(channelName, bindingProperties);
        ConfigurableApplicationContext applicationContext = new GenericApplicationContext();
        applicationContext.refresh();
        bindingServiceProperties.setApplicationContext(applicationContext);
        bindingServiceProperties.setConversionService(new DefaultConversionService());
        bindingServiceProperties.afterPropertiesSet();
        DirectChannel channel = new DirectChannel();
        channel.setBeanName(channelName);
        MessageConverterConfigurer messageConverterConfigurer = new MessageConverterConfigurer(
                bindingServiceProperties, new CompositeMessageConverterFactory(null, null));
        messageConverterConfigurer.setBeanFactory(applicationContext.getBeanFactory());
        messageConverterConfigurer.afterPropertiesSet();
        messageConverterConfigurer.configureOutputChannel(channel, channelName);
        return channel;
    }

    @After
    public void cleanup() {
        if (testBinder != null) {
            testBinder.cleanup();
        }
    }

    /**
     * If appropriate, let the binder middleware settle down a bit while binding/unbinding actually happens.
     */
    protected void binderBindUnbindLatency() throws InterruptedException {
        // default none
    }

    /**
     * Create a new spy on the given 'queue'. This allows de-correlating the creation of
     * the 'connection' from its actual usage, which may be needed by some implementations
     * to see messages sent after connection creation.
     */
    public abstract Spy spyOn(final String name);
}