INT-2214, INT-343, INT-2250 MessageHandler Advice
Add general capability to advise just the handleRequestMessage part of an AbstractReplyProducingMessageHandler. This is to advise just the immediate operation, and not the entire downstream flow. Uses include: * outbound gateway post processing * adding retry behavior using spring-retry * adding circuit breaker functionality Initial commit for review. Also need to advise simple message handlers (such as file etc) to allow them to post-process file operations with payload.delete(), payload.renameTo(...) etc. INT-2250 Add Circuit Breaker Advice INT-343 Add Retry Advice Stateless and Stateful retry using spring-retry. Stateless means the RetryTemplate performs the retries internally. Stateful means the exception is thrown (e.g. to JMS container) and the retry state is maintained by spring-retry. INT-2215, INT-343, INT-2250 Refactoring Factor out common abstract Advice class. INT-2214 Catch Evaluation Expression Exceptions If an onSuccess expression evaluation fails, add an option so the user can decide whether such an exception is caught, or propagated to the caller. INT-2214 etc PR Review Polishing INT-2214 etc Namespace Core, File, FTP Add <request-handler-advice-chain/> to outbound endpoints. INT-2214 etc. More Namespace Support amqp, event, gemfire, groovy, http, ip, jdbc, jms, jmx, jpa, mail, rmi, sftp, twitter, ws, xmpp INT-2214 etc Polishing PR Review INT-2214 etc Polishing Don't catch Throwable. Move Advice classes to handler.advice package.
This commit is contained in:
committed by
Oleg Zhurakousky
parent
56e3c22970
commit
08cbab08c2
@@ -23,7 +23,11 @@
|
||||
<queue capacity="1"/>
|
||||
</channel>
|
||||
|
||||
<filter ref="selectorBean" method="hasText" input-channel="adapterInput" output-channel="adapterOutput"/>
|
||||
<filter ref="selectorBean" method="hasText" input-channel="adapterInput" output-channel="adapterOutput">
|
||||
<request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.config.FilterParserTests$FooFilter" />
|
||||
</request-handler-advice-chain>
|
||||
</filter>
|
||||
|
||||
<beans:bean id="selectorBean"
|
||||
class="org.springframework.integration.config.FilterParserTests$TestSelectorBean"/>
|
||||
|
||||
@@ -22,7 +22,6 @@ import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.integration.Message;
|
||||
@@ -30,6 +29,7 @@ import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.MessageRejectedException;
|
||||
import org.springframework.integration.core.MessageSelector;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
@@ -69,13 +69,16 @@ public class FilterParserTests {
|
||||
@Autowired @Qualifier("discardAndExceptionOutput")
|
||||
PollableChannel discardAndExceptionOutput;
|
||||
|
||||
private static volatile int adviceCalled;
|
||||
|
||||
@Test
|
||||
public void filterWithSelectorAdapterAccepts() {
|
||||
adviceCalled = 0;
|
||||
adapterInput.send(new GenericMessage<String>("test"));
|
||||
Message<?> reply = adapterOutput.receive(0);
|
||||
assertNotNull(reply);
|
||||
assertEquals("test", reply.getPayload());
|
||||
assertEquals(1, adviceCalled);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -156,4 +159,13 @@ public class FilterParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class FooFilter extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
adviceCalled++;
|
||||
return callback.execute();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,10 @@
|
||||
<property name="name" expression="payload.sourceName"/>
|
||||
<property name="age" value="42"/>
|
||||
<property name="gender" expression="@testBean"/>
|
||||
|
||||
<request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.config.xml.EnricherParserTests$FooAdvice" />
|
||||
</request-handler-advice-chain>
|
||||
</enricher>
|
||||
|
||||
<beans:bean id="testBean" class="java.lang.String">
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.transformer.ContentEnricher;
|
||||
@@ -44,7 +45,8 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Gunnar Hillert
|
||||
*
|
||||
* @author Gary Russell
|
||||
*
|
||||
* @since 2.1
|
||||
*/
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
@@ -54,6 +56,7 @@ public class EnricherParserTests {
|
||||
@Autowired
|
||||
private ApplicationContext context;
|
||||
|
||||
private static volatile int adviceCalled;
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -84,13 +87,14 @@ public class EnricherParserTests {
|
||||
throw new IllegalStateException("expected 'name', 'age', and 'gender' only, not: " + e.getKey().getExpressionString());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configurationCheckTimeoutParameters() {
|
||||
|
||||
|
||||
Object endpoint = context.getBean("enricher");
|
||||
|
||||
|
||||
Long requestTimeout = TestUtils.getPropertyValue(endpoint, "handler.requestTimeout", Long.class);
|
||||
Long replyTimeout = TestUtils.getPropertyValue(endpoint, "handler.replyTimeout", Long.class);
|
||||
|
||||
@@ -98,18 +102,18 @@ public class EnricherParserTests {
|
||||
assertEquals(Long.valueOf(9876L), replyTimeout);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void configurationCheckRequiresReply() {
|
||||
|
||||
|
||||
Object endpoint = context.getBean("enricher");
|
||||
|
||||
|
||||
boolean requiresReply = TestUtils.getPropertyValue(endpoint, "handler.requiresReply", Boolean.class);
|
||||
|
||||
assertTrue("Was expecting requiresReply to be 'false'", requiresReply);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void integrationTest() {
|
||||
SubscribableChannel requests = context.getBean("requests", SubscribableChannel.class);
|
||||
@@ -128,6 +132,7 @@ public class EnricherParserTests {
|
||||
assertEquals(42, enriched.getAge());
|
||||
assertEquals("male", enriched.getGender());
|
||||
assertNotSame(original, enriched);
|
||||
assertEquals(1, adviceCalled);
|
||||
}
|
||||
|
||||
private static class Source {
|
||||
@@ -176,6 +181,7 @@ public class EnricherParserTests {
|
||||
this.gender = gender;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone() {
|
||||
Target copy = new Target();
|
||||
copy.setName(this.name);
|
||||
@@ -184,4 +190,13 @@ public class EnricherParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
adviceCalled++;
|
||||
return callback.execute();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,9 @@
|
||||
|
||||
<beans:bean id="testBean" class="org.springframework.integration.config.xml.ServiceActivatorParserTests$TestBean"/>
|
||||
|
||||
<service-activator id="withAdvice" input-channel="advisedInput" expression="'foo'">
|
||||
<request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.config.xml.ServiceActivatorParserTests$BarAdvice" />
|
||||
</request-handler-advice-chain>
|
||||
</service-activator>
|
||||
</beans:beans>
|
||||
|
||||
@@ -20,12 +20,13 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.core.MessagingTemplate;
|
||||
import org.springframework.integration.handler.ServiceActivatingHandler;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
|
||||
@@ -56,6 +57,9 @@ public class ServiceActivatorParserTests {
|
||||
@Autowired
|
||||
private MessageChannel multipleArgsFromPayloadInput;
|
||||
|
||||
@Autowired
|
||||
private MessageChannel advisedInput;
|
||||
|
||||
@SuppressWarnings("unused") // testing auto wiring only
|
||||
@Autowired
|
||||
@Qualifier("org.springframework.integration.config.ServiceActivatorFactoryBean#0")
|
||||
@@ -102,6 +106,11 @@ public class ServiceActivatorParserTests {
|
||||
assertEquals("JohnDoe", result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void advised() {
|
||||
Object result = this.sendAndReceive(advisedInput, "hello");
|
||||
assertEquals("bar", result);
|
||||
}
|
||||
|
||||
private Object sendAndReceive(MessageChannel channel, Object payload) {
|
||||
MessagingTemplate template = new MessagingTemplate(channel);
|
||||
@@ -152,4 +161,13 @@ public class ServiceActivatorParserTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class BarAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
callback.execute();
|
||||
return "bar";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright 2002-2012 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.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.junit.Test;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageHandlingException;
|
||||
import org.springframework.integration.MessageHeaders;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice;
|
||||
import org.springframework.integration.handler.advice.RequestHandlerCircuitBreakerAdvice;
|
||||
import org.springframework.integration.handler.advice.RequestHandlerRetryAdvice;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.retry.RecoveryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryState;
|
||||
import org.springframework.retry.support.DefaultRetryState;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
* @since 2.2
|
||||
*
|
||||
*/
|
||||
public class AdvisedMessageHandlerTests {
|
||||
|
||||
@Test
|
||||
public void successFailureAdvice() {
|
||||
final AtomicBoolean doFail = new AtomicBoolean();
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (doFail.get()) {
|
||||
throw new RuntimeException("qux");
|
||||
}
|
||||
return "baz";
|
||||
}
|
||||
};
|
||||
QueueChannel replies = new QueueChannel();
|
||||
handler.setOutputChannel(replies);
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
|
||||
// no advice
|
||||
handler.handleMessage(message);
|
||||
Message<?> reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("baz", reply.getPayload());
|
||||
|
||||
PollableChannel successChannel = new QueueChannel();
|
||||
PollableChannel failureChannel = new QueueChannel();
|
||||
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice(
|
||||
new SpelExpressionParser().parseExpression("'foo'"), successChannel,
|
||||
new SpelExpressionParser().parseExpression("'bar'"), failureChannel);
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
// advice with success
|
||||
handler.handleMessage(message);
|
||||
reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("baz", reply.getPayload());
|
||||
|
||||
Message<?> success = successChannel.receive(1000);
|
||||
assertNotNull(success);
|
||||
assertEquals("Hello, world!", success.getPayload());
|
||||
assertEquals("foo", success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
|
||||
|
||||
// advice with failure, not trapped
|
||||
doFail.set(true);
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("qux", e.getCause().getMessage());
|
||||
}
|
||||
|
||||
Message<?> failure = failureChannel.receive(1000);
|
||||
assertNotNull(failure);
|
||||
assertEquals("Hello, world!", failure.getPayload());
|
||||
assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
|
||||
|
||||
// advice with failure, trapped
|
||||
advice.setTrapException(true);
|
||||
handler.handleMessage(message);
|
||||
failure = failureChannel.receive(1000);
|
||||
assertNotNull(failure);
|
||||
assertEquals("Hello, world!", failure.getPayload());
|
||||
assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
|
||||
assertNull(replies.receive(1));
|
||||
|
||||
// advice with failure, eval is result
|
||||
advice.setReturnFailureExpressionResult(true);
|
||||
handler.handleMessage(message);
|
||||
failure = failureChannel.receive(1000);
|
||||
assertNotNull(failure);
|
||||
assertEquals("Hello, world!", failure.getPayload());
|
||||
assertEquals("bar", failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT));
|
||||
|
||||
reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("bar", reply.getPayload());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propagateOnSuccessExpressionFailures() {
|
||||
final AtomicBoolean doFail = new AtomicBoolean();
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (doFail.get()) {
|
||||
throw new RuntimeException("qux");
|
||||
}
|
||||
return "baz";
|
||||
}
|
||||
};
|
||||
QueueChannel replies = new QueueChannel();
|
||||
handler.setOutputChannel(replies);
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
|
||||
PollableChannel successChannel = new QueueChannel();
|
||||
PollableChannel failureChannel = new QueueChannel();
|
||||
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice(
|
||||
new SpelExpressionParser().parseExpression("1/0"), successChannel,
|
||||
new SpelExpressionParser().parseExpression("1/0"), failureChannel);
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
// failing advice with success
|
||||
handler.handleMessage(message);
|
||||
Message<?> reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("baz", reply.getPayload());
|
||||
|
||||
Message<?> success = successChannel.receive(1000);
|
||||
assertNotNull(success);
|
||||
assertEquals("Hello, world!", success.getPayload());
|
||||
assertEquals(MessageHandlingException.class, success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
|
||||
assertEquals("Expression evaluation failed: 1/0", ((Exception) success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
|
||||
|
||||
// propagate failing advice with success
|
||||
advice.setPropagateEvaluationFailures(true);
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (MessageHandlingException e) {
|
||||
assertEquals("Expression evaluation failed: 1/0", e.getMessage());
|
||||
}
|
||||
reply = replies.receive(1);
|
||||
assertNull(reply);
|
||||
|
||||
success = successChannel.receive(1000);
|
||||
assertNotNull(success);
|
||||
assertEquals("Hello, world!", success.getPayload());
|
||||
assertEquals(MessageHandlingException.class, success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
|
||||
assertEquals("Expression evaluation failed: 1/0", ((Exception) success.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propagateOnFailureExpressionFailures() {
|
||||
final AtomicBoolean doFail = new AtomicBoolean(true);
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (doFail.get()) {
|
||||
throw new RuntimeException("qux");
|
||||
}
|
||||
return "baz";
|
||||
}
|
||||
};
|
||||
QueueChannel replies = new QueueChannel();
|
||||
handler.setOutputChannel(replies);
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
|
||||
PollableChannel successChannel = new QueueChannel();
|
||||
PollableChannel failureChannel = new QueueChannel();
|
||||
ExpressionEvaluatingRequestHandlerAdvice advice = new ExpressionEvaluatingRequestHandlerAdvice(
|
||||
new SpelExpressionParser().parseExpression("1/0"), successChannel,
|
||||
new SpelExpressionParser().parseExpression("1/0"), failureChannel);
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
// failing advice with failure
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected exception");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("qux", e.getCause().getMessage());
|
||||
}
|
||||
Message<?> reply = replies.receive(1);
|
||||
assertNull(reply);
|
||||
|
||||
Message<?> failure = failureChannel.receive(1000);
|
||||
assertNotNull(failure);
|
||||
assertEquals("Hello, world!", failure.getPayload());
|
||||
assertEquals(MessageHandlingException.class, failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
|
||||
assertEquals("Expression evaluation failed: 1/0", ((Exception) failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
|
||||
|
||||
// propagate failing advice with failure; expect original exception
|
||||
advice.setPropagateEvaluationFailures(true);
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected Exception");
|
||||
}
|
||||
catch (MessageHandlingException e) {
|
||||
assertEquals("qux", e.getCause().getMessage());
|
||||
}
|
||||
reply = replies.receive(1);
|
||||
assertNull(reply);
|
||||
|
||||
failure = failureChannel.receive(1000);
|
||||
assertNotNull(failure);
|
||||
assertEquals("Hello, world!", failure.getPayload());
|
||||
assertEquals(MessageHandlingException.class, failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT).getClass());
|
||||
assertEquals("Expression evaluation failed: 1/0", ((Exception) failure.getHeaders().get(MessageHeaders.POSTPROCESS_RESULT)).getMessage());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void circuitBreakerTests() throws Exception {
|
||||
final AtomicBoolean doFail = new AtomicBoolean();
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (doFail.get()) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
return "bar";
|
||||
}
|
||||
|
||||
};
|
||||
handler.setBeanName("baz");
|
||||
handler.setOutputChannel(new QueueChannel());
|
||||
RequestHandlerCircuitBreakerAdvice advice = new RequestHandlerCircuitBreakerAdvice();
|
||||
/*
|
||||
* Circuit breaker opens after 2 failures; allows a new attempt after 100ms and
|
||||
* immediately opens again if that attempt fails. After a successful attempt,
|
||||
* we reset the failure counter.
|
||||
*/
|
||||
advice.setThreshold(2);
|
||||
advice.setHalfOpenAfter(100);
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
doFail.set(true);
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("Circuit Breaker is Open for baz", e.getMessage());
|
||||
}
|
||||
Thread.sleep(100);
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("Circuit Breaker is Open for baz", e.getMessage());
|
||||
}
|
||||
Thread.sleep(100);
|
||||
doFail.set(false);
|
||||
handler.handleMessage(message);
|
||||
doFail.set(true);
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("foo", e.getCause().getMessage());
|
||||
}
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
fail("Expected failure");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertEquals("Circuit Breaker is Open for baz", e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultRetrySucceedonThirdTry() {
|
||||
final AtomicInteger counter = new AtomicInteger(2);
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (counter.getAndDecrement() > 0) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
return "bar";
|
||||
}
|
||||
};
|
||||
QueueChannel replies = new QueueChannel();
|
||||
handler.setOutputChannel(replies);
|
||||
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
handler.handleMessage(message);
|
||||
assertTrue(counter.get() == -1);
|
||||
Message<?> reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("bar", reply.getPayload());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultStatefulRetrySucceedonThirdTry() {
|
||||
final AtomicInteger counter = new AtomicInteger(2);
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (counter.getAndDecrement() > 0) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
return "bar";
|
||||
}
|
||||
};
|
||||
QueueChannel replies = new QueueChannel();
|
||||
handler.setOutputChannel(replies);
|
||||
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
|
||||
|
||||
advice.setRetryStateGenerator(new RetryStateGenerator() {
|
||||
public RetryState determineRetryState(Message<?> message) {
|
||||
return new DefaultRetryState(message.getHeaders().getId());
|
||||
}
|
||||
});
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
for (int i = 0; i < 3; i++) {
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertTrue(i < 2);
|
||||
}
|
||||
}
|
||||
assertTrue(counter.get() == -1);
|
||||
Message<?> reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("bar", reply.getPayload());
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultStatefulRetryRecoverAfterThirdTry() {
|
||||
final AtomicInteger counter = new AtomicInteger(3);
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
if (counter.getAndDecrement() > 0) {
|
||||
throw new RuntimeException("foo");
|
||||
}
|
||||
return "bar";
|
||||
}
|
||||
};
|
||||
QueueChannel replies = new QueueChannel();
|
||||
handler.setOutputChannel(replies);
|
||||
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
|
||||
|
||||
advice.setRetryStateGenerator(new RetryStateGenerator() {
|
||||
public RetryState determineRetryState(Message<?> message) {
|
||||
return new DefaultRetryState(message.getHeaders().getId());
|
||||
}
|
||||
});
|
||||
|
||||
advice.setRecoveryCallback(new RecoveryCallback<Object>() {
|
||||
|
||||
public Object recover(RetryContext context) throws Exception {
|
||||
return "baz";
|
||||
}
|
||||
});
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
Message<String> message = new GenericMessage<String>("Hello, world!");
|
||||
for (int i = 0; i < 4; i++) {
|
||||
try {
|
||||
handler.handleMessage(message);
|
||||
}
|
||||
catch (Exception e) {
|
||||
}
|
||||
}
|
||||
assertTrue(counter.get() == 0);
|
||||
Message<?> reply = replies.receive(1000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("baz", reply.getPayload());
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2012 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.
|
||||
@@ -19,19 +19,22 @@ package org.springframework.integration.transformer;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
import org.springframework.integration.Message;
|
||||
import org.springframework.integration.MessageChannel;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class TransformerContextTests {
|
||||
|
||||
private static volatile int adviceCalled;
|
||||
|
||||
@Test
|
||||
public void methodInvokingTransformer() {
|
||||
ApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
@@ -41,6 +44,16 @@ public class TransformerContextTests {
|
||||
input.send(new GenericMessage<String>("foo"));
|
||||
Message<?> reply = output.receive(0);
|
||||
assertEquals("FOO", reply.getPayload());
|
||||
assertEquals(1, adviceCalled);
|
||||
}
|
||||
|
||||
public static class FooAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
adviceCalled++;
|
||||
return callback.execute();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,11 @@
|
||||
<queue capacity="50"/>
|
||||
</channel>
|
||||
|
||||
<transformer input-channel="input" ref="testBean" method="upperCase" output-channel="output"/>
|
||||
<transformer input-channel="input" ref="testBean" method="upperCase" output-channel="output">
|
||||
<request-handler-advice-chain>
|
||||
<beans:bean class="org.springframework.integration.transformer.TransformerContextTests$FooAdvice" />
|
||||
</request-handler-advice-chain>
|
||||
</transformer>
|
||||
|
||||
<beans:bean id="testBean" class="org.springframework.integration.transformer.TestBean"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user