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:
Gary Russell
2012-07-14 13:00:38 -04:00
committed by Oleg Zhurakousky
parent 56e3c22970
commit 08cbab08c2
114 changed files with 2661 additions and 296 deletions

View File

@@ -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"/>

View File

@@ -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();
}
}
}

View File

@@ -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">

View File

@@ -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();
}
}
}

View File

@@ -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>

View File

@@ -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";
}
}
}

View File

@@ -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());
}
}

View File

@@ -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();
}
}
}

View File

@@ -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"/>