/*
* 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.dispatcher;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.List;
import java.util.concurrent.Callable;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.junit.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.ProxyMethodInvocation;
import org.springframework.aop.framework.Advised;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.integration.endpoint.PollingConsumer;
import org.springframework.integration.test.util.TestUtils;
import org.springframework.integration.util.TestTransactionManager;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandlingException;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import org.springframework.transaction.support.DefaultTransactionStatus;
/**
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Gary Russell
* @author Andreas Baer
* @author Artem Bilan
*/
public class PollingTransactionTests {
@Test
public void transactionWithCommit() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"transactionTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
MessageChannel input = (MessageChannel) context.getBean("goodInput");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
assertEquals(0, txManager.getRollbackCount());
input.send(new GenericMessage<String>("test"));
txManager.waitForCompletion(1000);
Message<?> message = output.receive(0);
assertNotNull(message);
assertEquals(1, txManager.getCommitCount());
assertEquals(0, txManager.getRollbackCount());
context.close();
}
@Test
@SuppressWarnings("unchecked")
public void transactionWithCommitAndAdvices() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"transactionTests.xml", this.getClass());
PollingConsumer advicedPoller = context.getBean("advicedSa", PollingConsumer.class);
List<Advice> adviceChain = TestUtils.getPropertyValue(advicedPoller, "adviceChain", List.class);
assertEquals(4, adviceChain.size());
Runnable poller = TestUtils.getPropertyValue(advicedPoller, "poller", Runnable.class);
Callable<?> pollingTask = TestUtils.getPropertyValue(poller, "pollingTask", Callable.class);
assertTrue("Poller is not Advised", pollingTask instanceof Advised);
Advisor[] advisors = ((Advised) pollingTask).getAdvisors();
assertEquals(4, advisors.length);
assertThat("First advisor is not TX", advisors[0].getAdvice(), instanceOf(TransactionInterceptor.class));
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
MessageChannel input = (MessageChannel) context.getBean("goodInputWithAdvice");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
assertEquals(0, txManager.getRollbackCount());
input.send(new GenericMessage<>("test"));
txManager.waitForCompletion(10000);
Message<?> message = output.receive(0);
assertNotNull(message);
assertEquals(0, txManager.getRollbackCount());
context.close();
}
@Test
public void transactionWithRollback() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"transactionTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
MessageChannel input = (MessageChannel) context.getBean("badInput");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
assertEquals(0, txManager.getRollbackCount());
input.send(new GenericMessage<String>("test"));
txManager.waitForCompletion(1000);
Message<?> message = output.receive(0);
assertNull(message);
assertEquals(0, txManager.getCommitCount());
assertEquals(1, txManager.getRollbackCount());
context.close();
}
@Test
public void propagationRequired() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"propagationRequiredTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
PollableChannel input = (PollableChannel) context.getBean("input");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
input.send(new GenericMessage<String>("test"));
Message<?> reply = output.receive(3000);
assertNotNull(reply);
txManager.waitForCompletion(3000);
assertEquals(1, txManager.getCommitCount());
assertEquals(Propagation.REQUIRED.value(), txManager.getLastDefinition().getPropagationBehavior());
context.close();
}
@Test
public void propagationRequiresNew() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"propagationRequiresNewTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
PollableChannel input = (PollableChannel) context.getBean("input");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
input.send(new GenericMessage<String>("test"));
Message<?> reply = output.receive(3000);
assertNotNull(reply);
txManager.waitForCompletion(3000);
assertEquals(1, txManager.getCommitCount());
assertEquals(Propagation.REQUIRES_NEW.value(), txManager.getLastDefinition().getPropagationBehavior());
context.close();
}
@Test
public void propagationSupports() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"propagationSupportsTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
PollableChannel input = (PollableChannel) context.getBean("input");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
input.send(new GenericMessage<String>("test"));
Message<?> reply = output.receive(3000);
assertNotNull(reply);
assertEquals(0, txManager.getCommitCount());
assertNull(txManager.getLastDefinition());
context.close();
}
@Test
public void propagationNotSupported() throws InterruptedException {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"propagationNotSupportedTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
PollableChannel input = (PollableChannel) context.getBean("input");
PollableChannel output = (PollableChannel) context.getBean("output");
assertEquals(0, txManager.getCommitCount());
input.send(new GenericMessage<String>("test"));
Message<?> reply = output.receive(3000);
assertNotNull(reply);
assertEquals(0, txManager.getCommitCount());
assertNull(txManager.getLastDefinition());
context.close();
}
@Test
public void propagationMandatory() throws Throwable {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"propagationMandatoryTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManager");
PollableChannel input = (PollableChannel) context.getBean("input");
PollableChannel output = (PollableChannel) context.getBean("output");
PollableChannel errorChannel = (PollableChannel) context.getBean("errorChannel");
assertEquals(0, txManager.getCommitCount());
input.send(new GenericMessage<String>("test"));
Message<?> errorMessage = errorChannel.receive(3000);
assertNotNull(errorMessage);
Object payload = errorMessage.getPayload();
assertEquals(MessagingException.class, payload.getClass());
MessagingException messagingException = (MessagingException) payload;
assertEquals(IllegalTransactionStateException.class, messagingException.getCause().getClass());
assertNull(output.receive(0));
assertEquals(0, txManager.getCommitCount());
context.close();
}
@Test
public void commitFailureAndHandlerFailureTest() throws Throwable {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"transactionFailureTests.xml", this.getClass());
TestTransactionManager txManager = (TestTransactionManager) context.getBean("txManagerBad");
PollableChannel inputTxFail = (PollableChannel) context.getBean("inputTxFailure");
PollableChannel inputHandlerFail = (PollableChannel) context.getBean("inputHandlerFailure");
PollableChannel output = (PollableChannel) context.getBean("output");
PollableChannel errorChannel = (PollableChannel) context.getBean("errorChannel");
assertEquals(0, txManager.getCommitCount());
inputTxFail.send(new GenericMessage<>("commitFailureTest"));
Message<?> errorMessage = errorChannel.receive(10000);
assertNotNull(errorMessage);
Object payload = errorMessage.getPayload();
assertEquals(MessagingException.class, payload.getClass());
MessagingException messagingException = (MessagingException) payload;
assertEquals(IllegalTransactionStateException.class, messagingException.getCause().getClass());
assertNotNull(messagingException.getFailedMessage());
assertNotNull(output.receive(0));
assertEquals(0, txManager.getCommitCount());
inputHandlerFail.send(new GenericMessage<>("handlerFalilureTest"));
errorMessage = errorChannel.receive(10000);
assertNotNull(errorMessage);
payload = errorMessage.getPayload();
assertEquals(MessageHandlingException.class, payload.getClass());
messagingException = (MessageHandlingException) payload;
assertEquals(RuntimeException.class, messagingException.getCause().getClass());
assertNotNull(messagingException.getFailedMessage());
assertNull(output.receive(0));
assertEquals(0, txManager.getCommitCount());
context.close();
}
public static class SampleAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return invocation.proceed();
}
}
public static class SimpleRepeatAdvice implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
((ProxyMethodInvocation) invocation).invocableClone().proceed();
return invocation.proceed();
}
}
@SuppressWarnings("serial")
public static class FailingCommitTransactionManager extends TestTransactionManager {
@Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
throw new IllegalTransactionStateException("intentional test commit failure");
}
}
}