From 170bc6ea797a155598c8cbdd19e78e81e200893f Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Sun, 28 Sep 2008 22:15:22 +0000 Subject: [PATCH] Added the @Gateway annotation for per-method configuration when using GatewayProxyFactoryBean (INT-383). Also, the AbstractMessagingGateway now supports non-pollable reply channels. Note however, if using a gateway for no-arg receive() invocations, a PollableChannel is required and otherwise an IllegalStateException will be thrown (INT-384). --- .../integration/annotation/Gateway.java | 61 +++++++++++++++++++ .../gateway/AbstractMessagingGateway.java | 2 +- .../gateway/GatewayProxyFactoryBean.java | 31 ++++++++-- 3 files changed, 88 insertions(+), 6 deletions(-) create mode 100644 org.springframework.integration/src/main/java/org/springframework/integration/annotation/Gateway.java diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Gateway.java b/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Gateway.java new file mode 100644 index 0000000000..575a33b3b8 --- /dev/null +++ b/org.springframework.integration/src/main/java/org/springframework/integration/annotation/Gateway.java @@ -0,0 +1,61 @@ +/* + * 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. + * 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.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Indicates that a method is capable of mapping its parameters to a message + * or message payload. These method-level annotations are detected by the + * {@link org.springframework.integration.gateway.GatewayProxyFactoryBean} + * where the annotation attributes can override the default channel settings. + * + *

A method annotated with @Gateway may accept a single non-annotated + * parameter of type {@link org.springframework.integration.message.Message} + * or of the intended Message payload type. Method parameters may be mapped + * to individual Message header values by using the {@link Header @Header} + * parameter annotation. Alternatively, to pass the entire Message headers + * map, a Map-typed parameter may be annotated with {@link Headers}. + * + *

Return values from the annotated method may be of any type. If the + * declared return value is not a Message, the reply Message's payload will be + * returned and any type conversion as supported by Spring's + * {@link org.springframework.beans.SimpleTypeConverter} will be applied to + * the return value if necessary. + * + * @author Mark Fisher + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +@Documented +public @interface Gateway { + + String requestChannel() default ""; + + String replyChannel() default ""; + + long requestTimeout() default -1; + + long replyTimeout() default -1; + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/gateway/AbstractMessagingGateway.java b/org.springframework.integration/src/main/java/org/springframework/integration/gateway/AbstractMessagingGateway.java index 0dbb9cd750..966f0b81ce 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/gateway/AbstractMessagingGateway.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/gateway/AbstractMessagingGateway.java @@ -65,7 +65,7 @@ public abstract class AbstractMessagingGateway implements MessagingGateway, Mess * * @param replyChannel the channel from which reply messages will be received */ - public void setReplyChannel(PollableChannel replyChannel) { + public void setReplyChannel(MessageChannel replyChannel) { this.replyChannel = replyChannel; } diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java b/org.springframework.integration/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java index f4030949f4..3069c8346b 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java @@ -32,6 +32,7 @@ import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; +import org.springframework.integration.annotation.Gateway; import org.springframework.integration.bus.MessageBus; import org.springframework.integration.channel.MessageChannel; import org.springframework.integration.channel.PollableChannel; @@ -41,6 +42,7 @@ import org.springframework.integration.message.Message; import org.springframework.integration.message.MethodParameterMessageMapper; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * Generates a proxy for the provided service interface to enable interaction @@ -214,11 +216,30 @@ public class GatewayProxyFactoryBean implements FactoryBean, MethodInterceptor, SimpleMessagingGateway gateway = new SimpleMessagingGateway( new MethodParameterMessageMapper(method), new SimpleMessageMapper()); gateway.setMessageBus(this.messageBus); - //TODO: get request and reply channels from annotation, else fall back to these defaults - gateway.setRequestChannel(this.defaultRequestChannel); - gateway.setReplyChannel(this.defaultReplyChannel); - gateway.setRequestTimeout(this.defaultRequestTimeout); - gateway.setReplyTimeout(this.defaultReplyTimeout); + Gateway gatewayAnnotation = method.getAnnotation(Gateway.class); + MessageChannel requestChannel = this.defaultRequestChannel; + MessageChannel replyChannel = this.defaultReplyChannel; + long requestTimeout = this.defaultRequestTimeout; + long replyTimeout = this.defaultReplyTimeout; + if (gatewayAnnotation != null) { + Assert.state(this.messageBus != null, "MessageBus is required for channel resolution"); + String requestChannelName = gatewayAnnotation.requestChannel(); + if (StringUtils.hasText(requestChannelName)) { + requestChannel = this.messageBus.lookupChannel(requestChannelName); + Assert.notNull(requestChannel, "failed to resolve request channel '" + requestChannelName + "'"); + } + String replyChannelName = gatewayAnnotation.replyChannel(); + if (StringUtils.hasText(replyChannelName)) { + replyChannel = this.messageBus.lookupChannel(replyChannelName); + Assert.notNull(replyChannel, "failed to resolve reply channel '" + replyChannelName + "'"); + } + requestTimeout = gatewayAnnotation.requestTimeout(); + replyTimeout = gatewayAnnotation.replyTimeout(); + } + gateway.setRequestChannel(requestChannel); + gateway.setReplyChannel(replyChannel); + gateway.setRequestTimeout(requestTimeout); + gateway.setReplyTimeout(replyTimeout); return gateway; }