GH-3047: Add GatewayProxySpec for Java DSL
Fixes https://github.com/spring-projects/spring-integration/issues/3047 * Improve `GatewayProxyFactoryBean` to determine the return type of the method call from the interface generic types, when the `serviceInterface` is a `java.util.function.Function` * Propagate `MethodArgsHolder` as a `rootObject` for SpEL evaluations * Deprecate `#gatewayMethod` and `#args` evaluation context variables in favor of `MethodArgsHolder` as root object. They will be removed in the future release and a single `EvaluationContext` will be used for all the gateway expressions * Introduce an `IntegrationFlows.from(Class<?> serviceInterface, Consumer<GatewayProxySpec> endpointConfigurer)` to allow to configure any valid gateway proxy options similar to what we have with the `<gateway>` and `@MessagingGateway`. This way we are very close to consistency between different approaches * * Remove `default` prefix from `GatewayProxySpec` options * Document the change
This commit is contained in:
@@ -91,8 +91,8 @@
|
||||
<header name="foo" value="bar"/>
|
||||
</method>
|
||||
<method name="oneWayWithTimeouts" request-channel="otherRequestChannel"
|
||||
request-timeout="#args[1]"
|
||||
reply-timeout="#args[2]">
|
||||
request-timeout="args[1]"
|
||||
reply-timeout="args[2]">
|
||||
</method>
|
||||
</gateway>
|
||||
|
||||
|
||||
@@ -108,8 +108,8 @@ public class GatewayParserTests {
|
||||
assertThat(meta.getReplyChannelName()).isEqualTo("foo");
|
||||
meta = (GatewayMethodMetadata) methods.get("oneWayWithTimeouts");
|
||||
assertThat(meta).isNotNull();
|
||||
assertThat(meta.getRequestTimeout()).isEqualTo("#args[1]");
|
||||
assertThat(meta.getReplyTimeout()).isEqualTo("#args[2]");
|
||||
assertThat(meta.getRequestTimeout()).isEqualTo("args[1]");
|
||||
assertThat(meta.getReplyTimeout()).isEqualTo("args[2]");
|
||||
service.oneWayWithTimeouts("foo", 100L, 200L);
|
||||
result = channel.receive(10000);
|
||||
assertThat(result).isNotNull();
|
||||
@@ -118,7 +118,7 @@ public class GatewayParserTests {
|
||||
@Test
|
||||
public void testSolicitResponse() {
|
||||
PollableChannel channel = (PollableChannel) context.getBean("replyChannel");
|
||||
channel.send(new GenericMessage<String>("foo"));
|
||||
channel.send(new GenericMessage<>("foo"));
|
||||
TestService service = (TestService) context.getBean("solicitResponse");
|
||||
String result = service.solicitResponse();
|
||||
assertThat(result).isEqualTo("foo");
|
||||
@@ -161,7 +161,7 @@ public class GatewayParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryBeanObjectTypeWithServiceInterface() throws Exception {
|
||||
public void testFactoryBeanObjectTypeWithServiceInterface() {
|
||||
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) context).getBeanFactory();
|
||||
Object attribute = beanFactory.getMergedBeanDefinition("&oneWay").getAttribute(
|
||||
IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE);
|
||||
@@ -169,7 +169,7 @@ public class GatewayParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFactoryBeanObjectTypeWithNoServiceInterface() throws Exception {
|
||||
public void testFactoryBeanObjectTypeWithNoServiceInterface() {
|
||||
ConfigurableListableBeanFactory beanFactory = ((GenericApplicationContext) context).getBeanFactory();
|
||||
Object attribute = beanFactory.getMergedBeanDefinition("&defaultConfig").getAttribute(
|
||||
IntegrationConfigUtils.FACTORY_BEAN_OBJECT_TYPE);
|
||||
@@ -177,7 +177,7 @@ public class GatewayParserTests {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMonoGateway() throws Exception {
|
||||
public void testMonoGateway() {
|
||||
PollableChannel requestChannel = context.getBean("requestChannel", PollableChannel.class);
|
||||
MessageChannel replyChannel = context.getBean("replyChannel", MessageChannel.class);
|
||||
this.startResponder(requestChannel, replyChannel);
|
||||
|
||||
@@ -810,7 +810,7 @@ public class IntegrationFlowTests {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow errorRecovererFlow() {
|
||||
return IntegrationFlows.from(Function.class, "errorRecovererFunction")
|
||||
return IntegrationFlows.from(Function.class, (gateway) -> gateway.beanName("errorRecovererFunction"))
|
||||
.handle((GenericHandler<?>) (p, h) -> {
|
||||
throw new RuntimeException("intentional");
|
||||
}, e -> e.advice(retryAdvice()))
|
||||
@@ -891,7 +891,8 @@ public class IntegrationFlowTests {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow globalErrorChannelResolutionFlow(@Qualifier("taskScheduler") TaskExecutor taskExecutor) {
|
||||
return IntegrationFlows.from(Consumer.class, "globalErrorChannelResolutionFunction")
|
||||
return IntegrationFlows.from(Consumer.class,
|
||||
(gateway) -> gateway.beanName("globalErrorChannelResolutionFunction"))
|
||||
.channel(c -> c.executor(taskExecutor))
|
||||
.handle((GenericHandler<?>) (p, h) -> {
|
||||
throw new RuntimeException("intentional");
|
||||
|
||||
@@ -19,6 +19,9 @@ package org.springframework.integration.dsl.gateway;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@@ -32,6 +35,7 @@ import org.springframework.integration.config.EnableIntegration;
|
||||
import org.springframework.integration.dsl.IntegrationFlow;
|
||||
import org.springframework.integration.dsl.IntegrationFlows;
|
||||
import org.springframework.integration.dsl.MessageChannels;
|
||||
import org.springframework.integration.gateway.MethodArgsHolder;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
@@ -97,6 +101,27 @@ public class GatewayDslTests {
|
||||
.withStackTraceContaining("intentional");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private Function<Object, Message<?>> functionGateay;
|
||||
|
||||
@Test
|
||||
void testHeadersFromFunctionGateway() {
|
||||
Message<?> message = this.functionGateay.apply("testPayload");
|
||||
assertThat(message.getPayload()).isEqualTo("testPayload");
|
||||
assertThat(message.getHeaders()).containsKeys("gatewayMethod", "gatewayArgs");
|
||||
}
|
||||
|
||||
@Autowired
|
||||
private RoutingGateway routingGateway;
|
||||
|
||||
@Test
|
||||
void testRoutingGateway() {
|
||||
String result = this.routingGateway.route1("test1");
|
||||
assertThat(result).isEqualTo("route1");
|
||||
result = this.routingGateway.route2("test2");
|
||||
assertThat(result).isEqualTo("route2");
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
public static class ContextConfiguration {
|
||||
@@ -134,6 +159,40 @@ public class GatewayDslTests {
|
||||
})));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow functionGateway() {
|
||||
return IntegrationFlows.from(MessageFunction.class,
|
||||
(gateway) -> gateway
|
||||
.header("gatewayMethod", MethodArgsHolder::getMethod)
|
||||
.header("gatewayArgs", MethodArgsHolder::getArgs))
|
||||
.bridge()
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow routingGateway() {
|
||||
return IntegrationFlows.from(RoutingGateway.class,
|
||||
(gateway) -> gateway.header("gatewayMethod", MethodArgsHolder::getMethod))
|
||||
.route(Message.class, (message) ->
|
||||
message.getHeaders().get("gatewayMethod", Method.class).getName(),
|
||||
(router) -> router
|
||||
.subFlowMapping("route1", (subFlow) -> subFlow.transform((payload) -> "route1"))
|
||||
.subFlowMapping("route2", (subFlow) -> subFlow.transform((payload) -> "route2")))
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface MessageFunction extends Function<Object, Message<?>> {
|
||||
|
||||
}
|
||||
|
||||
interface RoutingGateway {
|
||||
|
||||
String route1(Object payload);
|
||||
|
||||
String route2(Object payload);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.expression.FunctionExpression;
|
||||
import org.springframework.integration.mapping.MessageMappingException;
|
||||
import org.springframework.integration.support.MessageBuilder;
|
||||
import org.springframework.messaging.Message;
|
||||
@@ -236,7 +237,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
map.put(2, "Two");
|
||||
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
|
||||
mapper.setBeanFactory(mock(BeanFactory.class));
|
||||
mapper.setPayloadExpression("'hello'");
|
||||
mapper.setPayloadExpression(new LiteralExpression("hello"));
|
||||
Message<?> message = mapper.toMessage(new Object[] { map });
|
||||
assertThat(message.getPayload()).isEqualTo("hello");
|
||||
}
|
||||
@@ -244,12 +245,12 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
@Test
|
||||
public void toMessageWithNonHeaderMapPayloadExpressionB() throws Exception {
|
||||
Method method = TestService.class.getMethod("sendNonHeadersMap", Map.class);
|
||||
Map<Integer, Object> map = new HashMap<Integer, Object>();
|
||||
Map<Integer, Object> map = new HashMap<>();
|
||||
map.put(1, "One");
|
||||
map.put(2, "Two");
|
||||
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
|
||||
mapper.setBeanFactory(mock(BeanFactory.class));
|
||||
mapper.setPayloadExpression("#args[0]");
|
||||
mapper.setPayloadExpression(new FunctionExpression<MethodArgsHolder>((methodArgs) -> methodArgs.getArgs()[0]));
|
||||
Message<?> message = mapper.toMessage(new Object[] { map });
|
||||
assertThat(message.getPayload()).isEqualTo(map);
|
||||
}
|
||||
@@ -277,7 +278,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
|
||||
mapB.put("2", "TWO");
|
||||
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
|
||||
mapper.setBeanFactory(mock(BeanFactory.class));
|
||||
mapper.setPayloadExpression("#args[0]");
|
||||
mapper.setPayloadExpression(new FunctionExpression<MethodArgsHolder>((methodArgs) -> methodArgs.getArgs()[0]));
|
||||
Message<?> message = mapper.toMessage(new Object[] { mapA, mapB });
|
||||
assertThat(message.getPayload()).isEqualTo(mapA);
|
||||
assertThat(message.getHeaders().get("1")).isEqualTo(mapB.get("1"));
|
||||
|
||||
Reference in New Issue
Block a user