INT-2994 Advice Chain Config via Annotations
Allow configuration of request handler advice chain using - ServiceActivator - Filter - Splitter - Transformer annotations. Also, with splitter, allow setting discardWithinAdvice (See INT-2938). INT-2994 Polishing: PR Comments - Change adviceChain attribute to an array - Use a boolean for the discardWithinAdvice attribute INT-2994 Handler Advice Doc Polishing Add a paragraph about Advice Order.
This commit is contained in:
committed by
Gary Russell
parent
7ea5998016
commit
573c692957
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -33,8 +33,9 @@ import java.lang.annotation.Target;
|
||||
* as Message parameters by using the {@link Header @Header} parameter annotation.
|
||||
* <p>
|
||||
* The return type of the annotated method must be a boolean (or Boolean).
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@@ -46,4 +47,8 @@ public @interface Filter {
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
String[] adviceChain() default {};
|
||||
|
||||
boolean discardWithinAdvice() default true;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -36,8 +36,9 @@ import java.lang.annotation.Target;
|
||||
* Return values from the annotated method may be of any type. If the return
|
||||
* value is not a Message, a reply Message will be created with that object
|
||||
* as its payload.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -49,4 +50,6 @@ public @interface ServiceActivator {
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
String[] adviceChain() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -36,8 +36,9 @@ import java.lang.annotation.Target;
|
||||
* Return values from the annotated method may be either a Collection or Array
|
||||
* with elements of any type. If the type is not a Message, each will be used
|
||||
* as the payload for creating a new Message.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -48,4 +49,6 @@ public @interface Splitter {
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
String[] adviceChain() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -26,8 +26,9 @@ import java.lang.annotation.Target;
|
||||
/**
|
||||
* Indicates that a method is capable of transforming a message, message header,
|
||||
* or message payload.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -39,4 +40,6 @@ public @interface Transformer {
|
||||
|
||||
String outputChannel() default "";
|
||||
|
||||
String[] adviceChain() default {};
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -18,6 +18,11 @@ package org.springframework.integration.config.annotation;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
@@ -31,6 +36,7 @@ import org.springframework.integration.core.MessageHandler;
|
||||
import org.springframework.integration.core.SubscribableChannel;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
|
||||
import org.springframework.integration.support.channel.ChannelResolver;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -40,11 +46,14 @@ import org.springframework.util.StringUtils;
|
||||
* Base class for Method-level annotation post-processors.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation> implements MethodAnnotationPostProcessor<T> {
|
||||
|
||||
private static final String INPUT_CHANNEL_ATTRIBUTE = "inputChannel";
|
||||
|
||||
private static final String ADVICE_CHAIN_ATTRIBUTE = "adviceChain";
|
||||
|
||||
|
||||
protected final BeanFactory beanFactory;
|
||||
|
||||
@@ -60,6 +69,7 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
|
||||
public Object postProcess(Object bean, String beanName, Method method, T annotation) {
|
||||
MessageHandler handler = this.createHandler(bean, method, annotation);
|
||||
setAdviceChainIfPresent(beanName, annotation, handler);
|
||||
if (handler instanceof Orderable) {
|
||||
Order orderAnnotation = AnnotationUtils.findAnnotation(method, Order.class);
|
||||
if (orderAnnotation != null) {
|
||||
@@ -76,6 +86,40 @@ public abstract class AbstractMethodAnnotationPostProcessor<T extends Annotation
|
||||
return handler;
|
||||
}
|
||||
|
||||
|
||||
protected final void setAdviceChainIfPresent(String beanName, T annotation, MessageHandler handler) {
|
||||
String[] adviceChainNames = (String[]) AnnotationUtils.getValue(annotation, ADVICE_CHAIN_ATTRIBUTE);
|
||||
if (adviceChainNames != null && adviceChainNames.length > 0) {
|
||||
if (!(handler instanceof AbstractReplyProducingMessageHandler)) {
|
||||
throw new IllegalArgumentException("Cannot apply advice chain to " + handler.getClass().getName());
|
||||
}
|
||||
List<Advice> adviceChain = new ArrayList<Advice>();
|
||||
for (String adviceChainName : adviceChainNames) {
|
||||
Object adviceChainBean = this.beanFactory.getBean(adviceChainName);
|
||||
if (adviceChainBean instanceof Advice) {
|
||||
adviceChain.add((Advice) adviceChainBean);
|
||||
}
|
||||
else if (adviceChainBean instanceof Advice[]) {
|
||||
for (Advice advice : (Advice[]) adviceChainBean) {
|
||||
adviceChain.add(advice);
|
||||
}
|
||||
}
|
||||
else if (adviceChainBean instanceof Collection) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Advice> adviceChainEntries = (Collection<Advice>) adviceChainBean;
|
||||
for (Advice advice : adviceChainEntries) {
|
||||
adviceChain.add(advice);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Invalid advice chain type:" +
|
||||
adviceChainName.getClass().getName() + " for bean '" + beanName + "'");
|
||||
}
|
||||
}
|
||||
((AbstractReplyProducingMessageHandler) handler).setAdviceChain(adviceChain);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean shouldCreateEndpoint(T annotation) {
|
||||
return (StringUtils.hasText((String) AnnotationUtils.getValue(annotation, INPUT_CHANNEL_ATTRIBUTE)));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -30,6 +30,7 @@ import org.springframework.util.StringUtils;
|
||||
* Post-processor for Methods annotated with {@link Filter @Filter}.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FilterAnnotationPostProcessor extends AbstractMethodAnnotationPostProcessor<Filter> {
|
||||
@@ -49,6 +50,7 @@ public class FilterAnnotationPostProcessor extends AbstractMethodAnnotationPostP
|
||||
if (StringUtils.hasText(outputChannelName)) {
|
||||
filter.setOutputChannel(this.channelResolver.resolveChannelName(outputChannelName));
|
||||
}
|
||||
filter.setDiscardWithinAdvice(annotation.discardWithinAdvice());
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2013 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.config.annotation;
|
||||
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.endpoint.annotation.TestService;
|
||||
|
||||
/**
|
||||
* @author Gary Russell
|
||||
*/
|
||||
@MessageEndpoint
|
||||
public class AnnotatedTestServiceWithAdvice implements TestService {
|
||||
|
||||
@ServiceActivator(inputChannel="inputChannel", outputChannel="outputChannel")
|
||||
public String sayHello(String name) {
|
||||
return "hello " + name;
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel="advisedIn", outputChannel="advisedOut", adviceChain="advice")
|
||||
public String sayHelloWithAdvice(String name) {
|
||||
return "hello " + name;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -16,8 +16,15 @@
|
||||
|
||||
package org.springframework.integration.config.annotation;
|
||||
|
||||
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.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -27,12 +34,15 @@ import org.springframework.integration.annotation.Filter;
|
||||
import org.springframework.integration.annotation.MessageEndpoint;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.endpoint.EventDrivenConsumer;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.test.util.TestUtils;
|
||||
import org.springframework.integration.test.util.TestUtils.TestApplicationContext;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
* @since 2.0
|
||||
*/
|
||||
public class FilterAnnotationPostProcessorTests {
|
||||
@@ -45,7 +55,6 @@ public class FilterAnnotationPostProcessorTests {
|
||||
|
||||
private final QueueChannel outputChannel = new QueueChannel();
|
||||
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
context.registerChannel("input", inputChannel);
|
||||
@@ -60,6 +69,99 @@ public class FilterAnnotationPostProcessorTests {
|
||||
testValidFilter(new TestFilterWithBooleanPrimitive());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceDiscardWithin() {
|
||||
TestAdvice advice = new TestAdvice();
|
||||
context.registerBean("adviceChain", advice);
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithin());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
assertSame(advice, TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class).get(0));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceDiscardWithinTwice() {
|
||||
TestAdvice advice1 = new TestAdvice();
|
||||
TestAdvice advice2 = new TestAdvice();
|
||||
context.registerBean("adviceChain1", advice1);
|
||||
context.registerBean("adviceChain2", advice2);
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithinTwice());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
List<?> adviceList = TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class);
|
||||
assertEquals(2, adviceList.size());
|
||||
assertSame(advice1, adviceList.get(0));
|
||||
assertSame(advice2, adviceList.get(1));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceDiscardWithout() {
|
||||
TestAdvice advice = new TestAdvice();
|
||||
context.registerBean("adviceChain", advice);
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithout());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
assertSame(advice, TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class).get(0));
|
||||
assertFalse(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceArray() {
|
||||
TestAdvice advice = new TestAdvice();
|
||||
context.registerBean("adviceChain", new TestAdvice[] {advice});
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithin());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
assertSame(advice, TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class).get(0));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceArrayTwice() {
|
||||
TestAdvice advice1 = new TestAdvice();
|
||||
TestAdvice advice2 = new TestAdvice();
|
||||
context.registerBean("adviceChain1", new TestAdvice[] {advice1, advice2});
|
||||
TestAdvice advice3 = new TestAdvice();
|
||||
TestAdvice advice4 = new TestAdvice();
|
||||
context.registerBean("adviceChain2", new TestAdvice[] {advice3, advice4});
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithinTwice());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
List<?> adviceList = TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class);
|
||||
assertEquals(4, adviceList.size());
|
||||
assertSame(advice1, adviceList.get(0));
|
||||
assertSame(advice2, adviceList.get(1));
|
||||
assertSame(advice3, adviceList.get(2));
|
||||
assertSame(advice4, adviceList.get(3));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceCollection() {
|
||||
TestAdvice advice = new TestAdvice();
|
||||
context.registerBean("adviceChain", Arrays.asList(new TestAdvice[] {advice}));
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithin());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
assertSame(advice, TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class).get(0));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithAdviceCollectionTwice() {
|
||||
TestAdvice advice1 = new TestAdvice();
|
||||
TestAdvice advice2 = new TestAdvice();
|
||||
context.registerBean("adviceChain1", new TestAdvice[] {advice1, advice2});
|
||||
TestAdvice advice3 = new TestAdvice();
|
||||
TestAdvice advice4 = new TestAdvice();
|
||||
context.registerBean("adviceChain2", new TestAdvice[] {advice3, advice4});
|
||||
testValidFilter(new TestFilterWithAdviceDiscardWithinTwice());
|
||||
EventDrivenConsumer endpoint = (EventDrivenConsumer) context.getBean("testFilter.filter.filter");
|
||||
List<?> adviceList = TestUtils.getPropertyValue(endpoint, "handler.adviceChain", List.class);
|
||||
assertEquals(4, adviceList.size());
|
||||
assertSame(advice1, adviceList.get(0));
|
||||
assertSame(advice2, adviceList.get(1));
|
||||
assertSame(advice3, adviceList.get(2));
|
||||
assertSame(advice4, adviceList.get(3));
|
||||
assertTrue(TestUtils.getPropertyValue(endpoint, "handler.postProcessWithinAdvice", Boolean.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void filterAnnotationWithBooleanWrapperClass() {
|
||||
testValidFilter(new TestFilterWithBooleanWrapperClass());
|
||||
@@ -91,7 +193,6 @@ public class FilterAnnotationPostProcessorTests {
|
||||
|
||||
|
||||
@MessageEndpoint
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestFilterWithBooleanPrimitive {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output")
|
||||
@@ -100,9 +201,35 @@ public class FilterAnnotationPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
private static class TestFilterWithAdviceDiscardWithin {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output", adviceChain="adviceChain")
|
||||
public boolean filter(String s) {
|
||||
return !s.contains("bad");
|
||||
}
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
private static class TestFilterWithAdviceDiscardWithinTwice {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output", adviceChain={"adviceChain1", "adviceChain2"})
|
||||
public boolean filter(String s) {
|
||||
return !s.contains("bad");
|
||||
}
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
private static class TestFilterWithAdviceDiscardWithout {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output",
|
||||
adviceChain="adviceChain", discardWithinAdvice=false)
|
||||
public boolean filter(String s) {
|
||||
return !s.contains("bad");
|
||||
}
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestFilterWithBooleanWrapperClass {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output")
|
||||
@@ -113,7 +240,6 @@ public class FilterAnnotationPostProcessorTests {
|
||||
|
||||
|
||||
@MessageEndpoint
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestFilterWithStringReturnType {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output")
|
||||
@@ -124,7 +250,6 @@ public class FilterAnnotationPostProcessorTests {
|
||||
|
||||
|
||||
@MessageEndpoint
|
||||
@SuppressWarnings("unused")
|
||||
private static class TestFilterWithVoidReturnType {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output")
|
||||
@@ -132,4 +257,12 @@ public class FilterAnnotationPostProcessorTests {
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
return callback.execute();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -37,6 +37,7 @@ import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.core.PollableChannel;
|
||||
import org.springframework.integration.endpoint.AbstractEndpoint;
|
||||
import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice;
|
||||
import org.springframework.integration.message.GenericMessage;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.integration.support.channel.BeanFactoryChannelResolver;
|
||||
@@ -46,6 +47,7 @@ import org.springframework.integration.test.util.TestUtils.TestApplicationContex
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public class MessagingAnnotationPostProcessorTests {
|
||||
|
||||
@@ -82,9 +84,16 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
context.start();
|
||||
MessageChannel inputChannel = (MessageChannel) context.getBean("inputChannel");
|
||||
PollableChannel outputChannel = (PollableChannel) context.getBean("outputChannel");
|
||||
inputChannel.send(new GenericMessage<String>("world"));
|
||||
GenericMessage<String> messageToSend = new GenericMessage<String>("world");
|
||||
inputChannel.send(messageToSend);
|
||||
Message<?> message = outputChannel.receive(1000);
|
||||
assertEquals("hello world", message.getPayload());
|
||||
|
||||
inputChannel = context.getBean("advisedIn", MessageChannel.class);
|
||||
outputChannel = context.getBean("advisedOut", PollableChannel.class);
|
||||
inputChannel.send(messageToSend);
|
||||
message = outputChannel.receive(1000);
|
||||
assertEquals("hello world advised", message.getPayload());
|
||||
context.stop();
|
||||
}
|
||||
|
||||
@@ -301,7 +310,7 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
|
||||
private String messageText;
|
||||
|
||||
private CountDownLatch latch;
|
||||
private final CountDownLatch latch;
|
||||
|
||||
|
||||
public OutboundOnlyTestBean(CountDownLatch latch) {
|
||||
@@ -313,7 +322,6 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
}
|
||||
|
||||
@ServiceActivator(inputChannel="testChannel")
|
||||
@SuppressWarnings("unused")
|
||||
public void countdown(String input) {
|
||||
this.messageText = input;
|
||||
latch.countDown();
|
||||
@@ -343,7 +351,6 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
@MessageEndpoint
|
||||
private static class ServiceActivatorAnnotatedBean {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ServiceActivator(inputChannel="inputChannel")
|
||||
public String test(String s) {
|
||||
return s + s;
|
||||
@@ -355,11 +362,18 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
@MessageEndpoint
|
||||
private static class TransformerAnnotationTestBean {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Transformer(inputChannel="inputChannel", outputChannel="outputChannel")
|
||||
public String transformBefore(String input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ServiceActivatorAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) throws Exception {
|
||||
return callback.execute() + " advised";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,15 @@
|
||||
<integration:queue capacity="5"/>
|
||||
</integration:channel>
|
||||
|
||||
<bean id="endpoint" class="org.springframework.integration.config.annotation.AnnotatedTestService"/>
|
||||
<bean id="endpoint" class="org.springframework.integration.config.annotation.AnnotatedTestServiceWithAdvice"/>
|
||||
|
||||
<integration:channel id="advisedIn"/>
|
||||
|
||||
<integration:channel id="advisedOut">
|
||||
<integration:queue capacity="1"/>
|
||||
</integration:channel>
|
||||
|
||||
<bean id="advice"
|
||||
class="org.springframework.integration.config.annotation.MessagingAnnotationPostProcessorTests$ServiceActivatorAdvice"/>
|
||||
|
||||
</beans>
|
||||
|
||||
@@ -55,6 +55,7 @@ import org.springframework.util.StringUtils;
|
||||
* @author Iwein Fuld
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @author Gary Russell
|
||||
*/
|
||||
public abstract class TestUtils {
|
||||
|
||||
@@ -142,11 +143,15 @@ public abstract class TestUtils {
|
||||
"channel name has already been set with a conflicting value");
|
||||
}
|
||||
}
|
||||
registerBean(channelName, channel, this);
|
||||
TestUtils.registerBean(channelName, channel, this);
|
||||
}
|
||||
|
||||
public void registerEndpoint(String endpointName, AbstractEndpoint endpoint) {
|
||||
registerBean(endpointName, endpoint, this);
|
||||
TestUtils.registerBean(endpointName, endpoint, this);
|
||||
}
|
||||
|
||||
public void registerBean(String beanName, Object bean) {
|
||||
TestUtils.registerBean(beanName, bean, this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -326,6 +326,9 @@ public class FooService {
|
||||
still available. For more detail see <xref linkend="bridge"/>.
|
||||
</note>
|
||||
</para>
|
||||
<para>
|
||||
Also see <xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
<section id="message-mapping-rules">
|
||||
|
||||
@@ -164,6 +164,10 @@
|
||||
<para>The filter can be either referenced explicitly from XML or, if
|
||||
the <interfacename>@MessageEndpoint</interfacename> annotation is defined
|
||||
on the class, detected automatically through classpath scanning.</para>
|
||||
|
||||
<para>
|
||||
Also see <xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -458,6 +458,13 @@ protected abstract Object doInvoke(ExecutionCallback callback, Object target, Me
|
||||
</para>
|
||||
</note>
|
||||
</section>
|
||||
<section id="other-advice">
|
||||
<title>Other Advice Chain Elements</title>
|
||||
<para>
|
||||
While the abstract class mentioned above is provided as a convenience, you can add any <classname>
|
||||
Advice</classname> to the chain, including a transaction advice.
|
||||
</para>
|
||||
</section>
|
||||
<section id="advising-filters">
|
||||
<title>Advising Filters</title>
|
||||
<para>
|
||||
@@ -474,4 +481,39 @@ protected abstract Object doInvoke(ExecutionCallback callback, Object target, Me
|
||||
(or exception) occurs after the advice chain is called.
|
||||
</para>
|
||||
</section>
|
||||
<section id="advising-with-annotations">
|
||||
<title>Advising Endpoints Using Annotations</title>
|
||||
<para>
|
||||
When configuring certain endpoints using annotations (<code>@Filter</code>, <code>@ServiceActivator</code>,
|
||||
<code>@Splitter</code>, and <code>@Transformer</code>), you can supply a bean name for the advice
|
||||
chain in the <code>adviceChain</code> attribute. In addition, the <code>@Filter</code> annotation
|
||||
also has the <code>discardWithinAdvice</code> attribute, which can be used to configure the discard
|
||||
behavior as discussed in <xref linkend="advising-filters"/>. An example with the discard being
|
||||
performed after the advice is shown below.
|
||||
</para>
|
||||
<programlisting language="java"><![CDATA[@MessageEndpoint
|
||||
public class MyAdvisedFilter {
|
||||
|
||||
@Filter(inputChannel="input", outputChannel="output",
|
||||
adviceChain="adviceChain", discardWithinAdvice="false")
|
||||
public boolean filter(String s) {
|
||||
return s.contains("good");
|
||||
}
|
||||
}]]></programlisting>
|
||||
</section>
|
||||
<section id="Advice Order">
|
||||
<title>Ordering Advices within an Advice Chain</title>
|
||||
<para>
|
||||
Advice classes are "around" advices and are applied in a nested fashion. The first advice is the
|
||||
outermost, the last advice the innermost (closest to the handler being advised). It is important
|
||||
to put the advice classes in the correct order to achieve the functionality you desire.
|
||||
</para>
|
||||
<para>
|
||||
For example, let's say you want to add a retry advice and a transaction advice.
|
||||
You may want to place the retry advice advice first, followed by the transaction advice.
|
||||
Then, each retry will be performed in a new transaction. On the other hand, if you want all the attempts,
|
||||
and any recovery operations (in the retry <classname>RecoveryCallback</classname>), to be scoped within
|
||||
the transaction, you would put the transaction advice first.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -161,6 +161,9 @@
|
||||
List<LineItem> extractItems(Order order) {
|
||||
return order.getItems()
|
||||
}</programlisting></para>
|
||||
<para>
|
||||
Also see <xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
@@ -350,6 +350,9 @@ Order generateOrder(String productId, @Header("customerName") String customer) {
|
||||
return new Order(productId, customer);
|
||||
}</programlisting>
|
||||
</para>
|
||||
<para>
|
||||
Also see <xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
</section>
|
||||
|
||||
@@ -92,6 +92,13 @@
|
||||
be performed after the advice chain completes. See <xref linkend="advising-filters"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-annotation-advice">
|
||||
<title>Advising Endpoints using Annotations</title>
|
||||
<para>
|
||||
Request Handler Advice Chains can now be configured using annotations. See
|
||||
<xref linkend="advising-with-annotations"/>.
|
||||
</para>
|
||||
</section>
|
||||
<section id="3.0-o-t-s-t">
|
||||
<title>ObjectToStringTransformer Improvements</title>
|
||||
<para>
|
||||
|
||||
Reference in New Issue
Block a user