/*
* Copyright 2013-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.channel.registry;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.lessThan;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Map;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.channel.DefaultHeaderChannelRegistry;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.MessagePublishingErrorHandler;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.context.IntegrationContextUtils;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.support.MessagingExceptionWrapper;
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
import org.springframework.integration.support.channel.HeaderChannelRegistry;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.core.DestinationResolutionException;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Gary Russell
* @since 3.0
*
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
@DirtiesContext
public class HeaderChannelRegistryTests {
@Autowired
MessageChannel input;
@Autowired
MessageChannel inputTtl;
@Autowired
MessageChannel inputCustomTtl;
@Autowired
MessageChannel inputPolled;
@Autowired
QueueChannel alreadyAString;
@Autowired
TaskScheduler taskScheduler;
@Autowired
Gateway gatewayNoReplyChannel;
@Autowired
Gateway gatewayExplicitReplyChannel;
@Autowired
DefaultHeaderChannelRegistry registry;
@Test
public void testReplace() {
MessagingTemplate template = new MessagingTemplate();
template.setDefaultDestination(this.input);
Message<?> reply = template.sendAndReceive(new GenericMessage<String>("foo"));
assertNotNull(reply);
assertEquals("echo:foo", reply.getPayload());
String stringReplyChannel = reply.getHeaders().get("stringReplyChannel", String.class);
assertThat(TestUtils.getPropertyValue(
TestUtils.getPropertyValue(registry, "channels", Map.class)
.get(stringReplyChannel), "expireAt", Long.class) - System.currentTimeMillis(),
lessThan(61000L));
}
@Test
public void testReplaceTtl() {
MessagingTemplate template = new MessagingTemplate();
template.setDefaultDestination(this.inputTtl);
Message<?> reply = template.sendAndReceive(new GenericMessage<String>("ttl"));
assertNotNull(reply);
assertEquals("echo:ttl", reply.getPayload());
String stringReplyChannel = reply.getHeaders().get("stringReplyChannel", String.class);
assertThat(TestUtils.getPropertyValue(
TestUtils.getPropertyValue(registry, "channels", Map.class)
.get(stringReplyChannel), "expireAt", Long.class) - System.currentTimeMillis(),
greaterThan(100000L));
}
@Test
public void testReplaceCustomTtl() {
MessagingTemplate template = new MessagingTemplate();
template.setDefaultDestination(this.inputCustomTtl);
Message<String> requestMessage = MessageBuilder.withPayload("ttl")
.setHeader("channelTTL", 180000)
.build();
Message<?> reply = template.sendAndReceive(requestMessage);
assertNotNull(reply);
assertEquals("echo:ttl", reply.getPayload());
String stringReplyChannel = reply.getHeaders().get("stringReplyChannel", String.class);
assertThat(TestUtils.getPropertyValue(
TestUtils.getPropertyValue(registry, "channels", Map.class)
.get(stringReplyChannel), "expireAt", Long.class) - System.currentTimeMillis(),
allOf(greaterThan(160000L), lessThan(181000L)));
// Now for Elvis...
reply = template.sendAndReceive(new GenericMessage<String>("ttl"));
assertNotNull(reply);
assertEquals("echo:ttl", reply.getPayload());
stringReplyChannel = reply.getHeaders().get("stringReplyChannel", String.class);
assertThat(TestUtils.getPropertyValue(
TestUtils.getPropertyValue(registry, "channels", Map.class)
.get(stringReplyChannel), "expireAt", Long.class) - System.currentTimeMillis(),
greaterThan(220000L));
}
@Test
public void testReplaceGatewayWithNoReplyChannel() {
String reply = this.gatewayNoReplyChannel.exchange("foo");
assertNotNull(reply);
assertEquals("echo:foo", reply);
}
@Test
public void testReplaceGatewayWithExplicitReplyChannel() {
String reply = this.gatewayExplicitReplyChannel.exchange("foo");
assertNotNull(reply);
assertEquals("echo:foo", reply);
}
/**
* MessagingTemplate sets the errorChannel to the replyChannel so it gets any async
* exceptions via the default {@link MessagePublishingErrorHandler}.
*/
@Test
public void testReplaceError() {
MessagingTemplate template = new MessagingTemplate();
template.setDefaultDestination(this.inputPolled);
Message<?> reply = template.sendAndReceive(new GenericMessage<String>("bar"));
assertNotNull(reply);
assertTrue(reply instanceof ErrorMessage);
assertNotNull(((ErrorMessage) reply).getOriginalMessage());
assertThat(reply.getPayload(), not(instanceOf(MessagingExceptionWrapper.class)));
}
@Test
public void testAlreadyAString() {
Message<String> requestMessage = MessageBuilder.withPayload("foo")
.setReplyChannelName("alreadyAString")
.setErrorChannelName("alreadyAnotherString")
.build();
this.input.send(requestMessage);
Message<?> reply = alreadyAString.receive(0);
assertNotNull(reply);
assertEquals("echo:foo", reply.getPayload());
}
@Test
public void testNull() {
Message<String> requestMessage = MessageBuilder.withPayload("foo")
.build();
try {
this.input.send(requestMessage);
fail("expected exception");
}
catch (Exception e) {
assertThat(e.getMessage(), Matchers.containsString("no output-channel or replyChannel"));
}
}
@Test
public void testExpire() throws Exception {
DefaultHeaderChannelRegistry registry = new DefaultHeaderChannelRegistry(50);
registry.setTaskScheduler(this.taskScheduler);
String id = (String) registry.channelToChannelName(new DirectChannel());
int n = 0;
while (n++ < 100 && registry.channelNameToChannel(id) != null) {
Thread.sleep(100);
}
assertNull(registry.channelNameToChannel(id));
registry.stop();
}
@Test
public void testBFCRWithRegistry() {
BeanFactoryChannelResolver resolver = new BeanFactoryChannelResolver();
BeanFactory beanFactory = mock(BeanFactory.class);
when(beanFactory.getBean(IntegrationContextUtils.INTEGRATION_HEADER_CHANNEL_REGISTRY_BEAN_NAME,
HeaderChannelRegistry.class))
.thenReturn(mock(HeaderChannelRegistry.class));
doAnswer(invocation -> {
throw new NoSuchBeanDefinitionException("bar");
}).when(beanFactory).getBean("foo", MessageChannel.class);
resolver.setBeanFactory(beanFactory);
try {
resolver.resolveDestination("foo");
fail("Expected exception");
}
catch (DestinationResolutionException e) {
assertThat(e.getMessage(),
Matchers.containsString("failed to look up MessageChannel with name 'foo' in the BeanFactory."));
}
}
@Test
public void testBFCRNoRegistry() {
BeanFactoryChannelResolver resolver = new BeanFactoryChannelResolver();
BeanFactory beanFactory = mock(BeanFactory.class);
doAnswer(invocation -> {
throw new NoSuchBeanDefinitionException("bar");
}).when(beanFactory).getBean("foo", MessageChannel.class);
resolver.setBeanFactory(beanFactory);
try {
resolver.resolveDestination("foo");
fail("Expected exception");
}
catch (DestinationResolutionException e) {
assertThat(e.getMessage(),
Matchers.containsString("failed to look up MessageChannel with name 'foo' in the BeanFactory " +
"(and there is no HeaderChannelRegistry present)."));
}
}
@Test
public void testRemoveOnGet() {
DefaultHeaderChannelRegistry registry = new DefaultHeaderChannelRegistry();
MessageChannel channel = new DirectChannel();
String foo = (String) registry.channelToChannelName(channel);
Map<?, ?> map = TestUtils.getPropertyValue(registry, "channels", Map.class);
assertEquals(1, map.size());
assertSame(channel, registry.channelNameToChannel(foo));
assertEquals(1, map.size());
registry.setRemoveOnGet(true);
assertSame(channel, registry.channelNameToChannel(foo));
assertEquals(0, map.size());
}
public static class Foo extends AbstractReplyProducingMessageHandler {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
assertThat(requestMessage.getHeaders().getReplyChannel(),
Matchers.anyOf(instanceOf(String.class), Matchers.nullValue()));
assertThat(requestMessage.getHeaders().getErrorChannel(),
Matchers.anyOf(instanceOf(String.class), Matchers.nullValue()));
if (requestMessage.getPayload().equals("bar")) {
throw new RuntimeException("intentional");
}
return MessageBuilder.withPayload("echo:" + requestMessage.getPayload())
.setHeader("stringReplyChannel", requestMessage.getHeaders().getReplyChannel())
.build();
}
}
public interface Gateway {
String exchange(String foo);
}
}