/*
* 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.handler;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf;
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.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.concurrent.atomic.AtomicReference;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.channel.QueueChannel;
import org.springframework.integration.endpoint.EventDrivenConsumer;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.transformer.MessageTransformationException;
import org.springframework.integration.util.StackTraceUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.SettableListenableFuture;
/**
* See INT-1688 for background.
*
* @author Mark Fisher
* @author Artem Bilan
* @author Gary Russell
*
* @since 2.0.1
*/
@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class ServiceActivatorDefaultFrameworkMethodTests {
@Autowired
private MessageChannel gatewayTestInputChannel;
@Autowired
private MessageChannel replyingHandlerTestInputChannel;
@Autowired
private MessageChannel optimizedRefReplyingHandlerTestInputChannel;
@Autowired
private MessageChannel replyingHandlerWithStandardMethodTestInputChannel;
@Autowired
private MessageChannel replyingHandlerWithOtherMethodTestInputChannel;
@Autowired
private MessageChannel handlerTestInputChannel;
@Autowired
private MessageChannel processorTestInputChannel;
@Autowired
private EventDrivenConsumer processorTestService;
@Autowired
private TestMessageProcessor testMessageProcessor;
@Autowired
private MessageChannel asyncIn;
@Autowired
private AsyncService asyncService;
@Autowired
private PollableChannel errorChannel;
@Test
public void testGateway() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.gatewayTestInputChannel.send(message);
Message<?> reply = replyChannel.receive(0);
assertEquals("gatewayTestInputChannel,gatewayTestService,gateway,requestChannel,replyChannel",
reply.getHeaders().get("history").toString());
message = MessageBuilder.withPayload("foo").setReplyChannel(replyChannel).build();
try {
this.gatewayTestInputChannel.send(message);
fail("Exception expected");
}
catch (Exception e) {
assertThat(e, instanceOf(MessageHandlingException.class));
assertThat(e.getCause(), instanceOf(MessageTransformationException.class));
assertThat(e.getCause().getCause(), instanceOf(MessageHandlingException.class));
assertThat(e.getCause().getCause().getCause(), instanceOf(java.lang.IllegalStateException.class));
assertThat(e.getMessage(), containsString("Expression evaluation failed"));
assertThat(e.getMessage(), containsString("Wrong payload"));
}
}
@Test
public void testReplyingMessageHandler() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.replyingHandlerTestInputChannel.send(message);
Message<?> reply = replyChannel.receive(0);
assertEquals("TEST", reply.getPayload());
assertEquals("replyingHandlerTestInputChannel,replyingHandlerTestService",
reply.getHeaders().get("history").toString());
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("AbstractSubscribableChannel",
"MethodInvokerHelper", st)); // close to the metal
}
@Test
public void testOptimizedReplyingMessageHandler() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.optimizedRefReplyingHandlerTestInputChannel.send(message);
Message<?> reply = replyChannel.receive(0);
assertEquals("TEST", reply.getPayload());
assertEquals("optimizedRefReplyingHandlerTestInputChannel,optimizedRefReplyingHandlerTestService",
reply.getHeaders().get("history").toString());
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("AbstractSubscribableChannel",
"MethodInvokerHelper", st)); // close to the metal
}
@Test
public void testReplyingMessageHandlerWithStandardMethod() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.replyingHandlerWithStandardMethodTestInputChannel.send(message);
Message<?> reply = replyChannel.receive(0);
assertEquals("TEST", reply.getPayload());
assertEquals("replyingHandlerWithStandardMethodTestInputChannel,replyingHandlerWithStandardMethodTestService",
reply.getHeaders().get("history").toString());
StackTraceElement[] st = (StackTraceElement[]) reply.getHeaders().get("callStack");
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("AbstractSubscribableChannel",
"MethodInvokerHelper", st)); // close to the metal
}
@Test
public void testReplyingMessageHandlerWithOtherMethod() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.replyingHandlerWithOtherMethodTestInputChannel.send(message);
Message<?> reply = replyChannel.receive(0);
assertEquals("bar", reply.getPayload());
assertEquals("replyingHandlerWithOtherMethodTestInputChannel,replyingHandlerWithOtherMethodTestService",
reply.getHeaders().get("history").toString());
}
@Test
public void testMessageHandler() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.handlerTestInputChannel.send(message);
}
// INT-2399
@Test
public void testMessageProcessor() {
Object processor = TestUtils.getPropertyValue(processorTestService, "handler.processor");
assertSame(testMessageProcessor, processor);
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("bar").setReplyChannel(replyChannel).build();
this.processorTestInputChannel.send(message);
Message<?> reply = replyChannel.receive(0);
assertEquals("foo:bar", reply.getPayload());
assertEquals("processorTestInputChannel,processorTestService", reply.getHeaders().get("history").toString());
}
@Test
public void testFailOnDoubleReference() {
try {
new ClassPathXmlApplicationContext(this.getClass().getSimpleName() + "-fail-context.xml",
this.getClass()).close();
fail("Expected exception due to 2 endpoints referencing the same bean");
}
catch (Exception e) {
assertThat(e, Matchers.instanceOf(BeanCreationException.class));
assertThat(e.getCause(), Matchers.instanceOf(BeanCreationException.class));
assertThat(e.getCause().getCause(), Matchers.instanceOf(IllegalArgumentException.class));
assertThat(e.getCause().getCause().getMessage(),
Matchers.containsString("An AbstractMessageProducingMessageHandler may only be referenced once"));
}
}
@Test
public void testAsync() {
QueueChannel replyChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setReplyChannel(replyChannel).build();
this.asyncIn.send(message);
Message<?> reply = replyChannel.receive(0);
assertNull(reply);
this.asyncService.future.set(this.asyncService.payload.toUpperCase());
reply = replyChannel.receive(0);
assertNotNull(reply);
assertEquals("TEST", reply.getPayload());
}
@Test
public void testAsyncWithDirectReply() {
DirectChannel replyChannel = new DirectChannel();
final AtomicReference<Message<?>> reply = new AtomicReference<Message<?>>();
replyChannel.subscribe(reply::set);
Message<?> message = MessageBuilder.withPayload("testing").setReplyChannel(replyChannel).build();
this.asyncIn.send(message);
assertNull(reply.get());
this.asyncService.future.set(this.asyncService.payload.toUpperCase());
assertNotNull(reply.get());
assertEquals("TESTING", reply.get().getPayload());
}
@Test
public void testAsyncError() {
QueueChannel errorChannel = new QueueChannel();
Message<?> message = MessageBuilder.withPayload("test").setErrorChannel(errorChannel).build();
this.asyncIn.send(message);
this.asyncService.future.setException(new RuntimeException("intended"));
Message<?> error = errorChannel.receive(0);
assertNotNull(error);
assertThat(error, instanceOf(ErrorMessage.class));
assertThat(error.getPayload(), instanceOf(MessagingException.class));
assertThat(((MessagingException) error.getPayload()).getCause(), instanceOf(RuntimeException.class));
assertThat(((MessagingException) error.getPayload()).getCause().getMessage(), equalTo("intended"));
assertEquals("test", ((MessagingException) error.getPayload()).getFailedMessage().getPayload());
}
@Test
public void testAsyncErrorNoHeader() {
Message<?> message = MessageBuilder.withPayload("test").build();
this.asyncIn.send(message);
this.asyncService.future.setException(new RuntimeException("intended"));
Message<?> error = this.errorChannel.receive(0);
assertNotNull(error);
assertThat(error, instanceOf(ErrorMessage.class));
assertThat(error.getPayload(), instanceOf(MessagingException.class));
assertThat(((MessagingException) error.getPayload()).getCause(), instanceOf(RuntimeException.class));
assertThat(((MessagingException) error.getPayload()).getCause().getMessage(), equalTo("intended"));
assertEquals("test", ((MessagingException) error.getPayload()).getFailedMessage().getPayload());
}
public static void throwIllegalStateException(String message) {
throw new IllegalStateException(message);
}
@SuppressWarnings("unused")
private static class TestReplyingMessageHandler extends AbstractReplyProducingMessageHandler {
@Override
protected Object handleRequestMessage(Message<?> requestMessage) {
Exception e = new RuntimeException();
StackTraceElement[] st = e.getStackTrace();
return MessageBuilder.withPayload(requestMessage.getPayload().toString().toUpperCase())
.setHeader("callStack", st);
}
public String foo(String in) {
Exception e = new RuntimeException();
StackTraceElement[] st = e.getStackTrace();
// use this to test that StackTraceUtils works as expected and returns false
assertFalse(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("AbstractSubscribableChannel",
"MethodInvokerHelper", st));
return "bar";
}
}
@SuppressWarnings("unused")
private static class TestMessageHandler implements MessageHandler {
@Override
public void handleMessage(Message<?> requestMessage) {
Exception e = new RuntimeException();
StackTraceElement[] st = e.getStackTrace();
assertTrue(StackTraceUtils.isFrameContainingXBeforeFrameContainingY("AbstractSubscribableChannel",
"MethodInvokerHelper", st)); // close to the metal
}
}
private static class TestMessageProcessor implements MessageProcessor<String> {
private String prefix;
@SuppressWarnings("unused")
public void setPrefix(String prefix) {
this.prefix = prefix;
}
@Override
public String processMessage(Message<?> message) {
return prefix + ":" + message.getPayload();
}
}
private static class AsyncService {
private volatile SettableListenableFuture<String> future;
private volatile String payload;
@SuppressWarnings("unused")
public ListenableFuture<String> process(String payload) {
this.future = new SettableListenableFuture<String>();
this.payload = payload;
return this.future;
}
}
}