diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Publisher.java b/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Publisher.java index 804f3da6e8..b2b8eb0c04 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Publisher.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Publisher.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2008 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. @@ -23,6 +23,8 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import org.springframework.integration.aop.MessagePublishingInterceptor; + /** * Indicates that the method's return value should be published to the specified * channel. The value will only be published if non-null. @@ -37,4 +39,7 @@ public @interface Publisher { String channel(); + MessagePublishingInterceptor.PayloadType payloadType() + default MessagePublishingInterceptor.PayloadType.RETURN_VALUE; + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/aop/AnnotationAwareMessagePublishingInterceptor.java b/org.springframework.integration/src/main/java/org/springframework/integration/aop/AnnotationAwareMessagePublishingInterceptor.java index 350e4acf4f..a9e21407b5 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/aop/AnnotationAwareMessagePublishingInterceptor.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/aop/AnnotationAwareMessagePublishingInterceptor.java @@ -70,4 +70,18 @@ public class AnnotationAwareMessagePublishingInterceptor extends MessagePublishi return super.resolveChannel(invocation); } + @Override + protected PayloadType determinePayloadType(MethodInvocation invocation) { + Class targetClass = AopUtils.getTargetClass(invocation.getThis()); + Method method = AopUtils.getMostSpecificMethod(invocation.getMethod(), targetClass); + Annotation annotation = AnnotationUtils.getAnnotation(method, this.publisherAnnotationType); + if (annotation != null) { + PayloadType payloadType = (PayloadType) AnnotationUtils.getValue(annotation, "payloadType"); + if (payloadType != null) { + return payloadType; + } + } + return super.determinePayloadType(invocation); + } + } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java b/org.springframework.integration/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java index c4af15d633..b3cd39928d 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/aop/MessagePublishingInterceptor.java @@ -26,6 +26,7 @@ import org.springframework.integration.channel.MessageChannel; import org.springframework.integration.message.GenericMessage; import org.springframework.integration.message.Message; import org.springframework.integration.message.MessageCreator; +import org.springframework.util.Assert; /** * Interceptor that publishes a target method's return value to a channel. @@ -34,17 +35,27 @@ import org.springframework.integration.message.MessageCreator; */ public class MessagePublishingInterceptor implements MethodInterceptor { + public static enum PayloadType { RETURN_VALUE, ARGUMENTS, EXCEPTION }; + + protected final Log logger = LogFactory.getLog(getClass()); private volatile MessageCreator messageCreator; private volatile MessageChannel defaultChannel; + private volatile PayloadType payloadType = PayloadType.RETURN_VALUE; + public void setDefaultChannel(MessageChannel defaultChannel) { this.defaultChannel = defaultChannel; } + public void setPayloadType(PayloadType payloadType) { + Assert.notNull(payloadType, "'payloadType' must not be null"); + this.payloadType = payloadType; + } + /** * Specify the {@link MessageCreator} to use when creating a message from the * return value Object. @@ -59,8 +70,32 @@ public class MessagePublishingInterceptor implements MethodInterceptor { * Invoke the target method and publish its return value. */ public Object invoke(MethodInvocation invocation) throws Throwable { - Object retval = invocation.proceed(); - if (retval != null) { + PayloadType payloadType = this.determinePayloadType(invocation); + if (payloadType.equals(PayloadType.ARGUMENTS)) { + this.sendMessage(invocation.getArguments(), invocation); + } + Object retval = null; + Throwable throwable = null; + try { + retval = invocation.proceed(); + return retval; + } + catch (Throwable t) { + throwable = t; + throw t; + } + finally { + if (payloadType.equals(PayloadType.RETURN_VALUE)) { + this.sendMessage(retval, invocation); + } + else if (payloadType.equals(PayloadType.EXCEPTION)) { + this.sendMessage(throwable, invocation); + } + } + } + + private void sendMessage(Object payload, MethodInvocation invocation) { + if (payload != null) { MessageChannel channel = this.resolveChannel(invocation); if (channel == null) { if (logger.isWarnEnabled()) { @@ -69,11 +104,11 @@ public class MessagePublishingInterceptor implements MethodInterceptor { } } else { - Message message = (this.messageCreator != null) ? this.messageCreator.createMessage(retval) : new GenericMessage(retval); + Message message = (this.messageCreator != null) ? + this.messageCreator.createMessage(payload) : new GenericMessage(payload); channel.send(message); } } - return retval; } /** @@ -83,4 +118,8 @@ public class MessagePublishingInterceptor implements MethodInterceptor { return this.defaultChannel; } + protected PayloadType determinePayloadType(MethodInvocation invocation) { + return this.payloadType; + } + } diff --git a/org.springframework.integration/src/test/java/org/springframework/integration/aop/PublisherAnnotationAdvisorTests.java b/org.springframework.integration/src/test/java/org/springframework/integration/aop/PublisherAnnotationAdvisorTests.java index e384639eb9..e44413f7e9 100644 --- a/org.springframework.integration/src/test/java/org/springframework/integration/aop/PublisherAnnotationAdvisorTests.java +++ b/org.springframework.integration/src/test/java/org/springframework/integration/aop/PublisherAnnotationAdvisorTests.java @@ -19,6 +19,7 @@ package org.springframework.integration.aop; 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 org.junit.Test; @@ -61,6 +62,61 @@ public class PublisherAnnotationAdvisorTests { assertNull(message); } + @Test + public void testPublishArguments() { + final QueueChannel channel = new QueueChannel(); + channel.setBeanName("testChannel"); + ChannelRegistry channelRegistry = new DefaultChannelRegistry(); + channelRegistry.registerChannel(channel); + PublisherAnnotationAdvisor advisor = new PublisherAnnotationAdvisor(channelRegistry); + TestService proxy = (TestService) this.createProxy(new TestServiceImpl("hello world"), advisor); + proxy.publishArguments("foo", 99); + Message message = channel.receive(0); + assertNotNull(message); + assertTrue(message.getPayload() instanceof Object[]); + Object[] args = (Object[]) message.getPayload(); + assertEquals(2, args.length); + assertEquals("foo", args[0]); + assertEquals(99, args[1]); + } + + @Test + public void testPublishException() { + final QueueChannel channel = new QueueChannel(); + channel.setBeanName("testChannel"); + ChannelRegistry channelRegistry = new DefaultChannelRegistry(); + channelRegistry.registerChannel(channel); + PublisherAnnotationAdvisor advisor = new PublisherAnnotationAdvisor(channelRegistry); + TestService proxy = (TestService) this.createProxy(new TestServiceImpl("hello world"), advisor); + RuntimeException caughtException = null; + try { + proxy.publishException(); + } + catch (RuntimeException e) { + caughtException = e; + } + assertNotNull(caughtException); + Message message = channel.receive(0); + assertNotNull(message); + assertTrue(message.getPayload() instanceof RuntimeException); + RuntimeException publishedException = (RuntimeException) message.getPayload(); + assertEquals(caughtException, publishedException); + } + + @Test + public void testPublishReturnValue() { + final QueueChannel channel = new QueueChannel(); + channel.setBeanName("testChannel"); + ChannelRegistry channelRegistry = new DefaultChannelRegistry(); + channelRegistry.registerChannel(channel); + PublisherAnnotationAdvisor advisor = new PublisherAnnotationAdvisor(channelRegistry); + TestService proxy = (TestService) this.createProxy(new TestServiceImpl("hello world"), advisor); + Integer actualReturnValue = proxy.publishReturnValue(); + Message message = channel.receive(0); + assertNotNull(message); + assertEquals(actualReturnValue, message.getPayload()); + } + private Object createProxy(Object target, PublisherAnnotationAdvisor advisor) { ProxyFactory factory = new ProxyFactory(target); @@ -74,6 +130,13 @@ public class PublisherAnnotationAdvisorTests { String publisherTest(); String noPublisherTest(); + + void publishArguments(String s, Integer n); + + Integer publishReturnValue(); + + void publishException(); + } @@ -94,6 +157,20 @@ public class PublisherAnnotationAdvisorTests { return this.message; } + @Publisher(channel="testChannel", payloadType=MessagePublishingInterceptor.PayloadType.ARGUMENTS) + public void publishArguments(String s, Integer n) { + } + + @Publisher(channel="testChannel", payloadType=MessagePublishingInterceptor.PayloadType.EXCEPTION) + public void publishException() { + throw new RuntimeException("test failure"); + } + + @Publisher(channel="testChannel", payloadType=MessagePublishingInterceptor.PayloadType.RETURN_VALUE) + public Integer publishReturnValue() { + return 123; + } + } }