Introduce EIP-methods handle() and bridge()
* Remove generic argument from `GenericEndpointSpec`: it isn't necessary * Add check for `one-way` flow in case the last component isn't `MessageProducer`
This commit is contained in:
@@ -23,9 +23,9 @@ import org.springframework.messaging.MessageHandler;
|
||||
* @author Artem Bilan
|
||||
* @since 4.0
|
||||
*/
|
||||
public final class GenericEndpointSpec<H extends MessageHandler> extends ConsumerEndpointSpec<GenericEndpointSpec<H>, H> {
|
||||
public final class GenericEndpointSpec extends ConsumerEndpointSpec<GenericEndpointSpec, MessageHandler> {
|
||||
|
||||
GenericEndpointSpec(H messageHandler) {
|
||||
GenericEndpointSpec(MessageHandler messageHandler) {
|
||||
super(messageHandler);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.integration.dsl;
|
||||
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.config.SourcePollingChannelAdapterFactoryBean;
|
||||
@@ -29,12 +29,14 @@ import org.springframework.integration.dsl.support.EndpointConfigurer;
|
||||
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
|
||||
import org.springframework.integration.filter.MessageFilter;
|
||||
import org.springframework.integration.filter.MethodInvokingSelector;
|
||||
import org.springframework.integration.handler.BridgeHandler;
|
||||
import org.springframework.integration.transformer.ExpressionEvaluatingTransformer;
|
||||
import org.springframework.integration.transformer.GenericTransformer;
|
||||
import org.springframework.integration.transformer.MessageTransformingHandler;
|
||||
import org.springframework.integration.transformer.MethodInvokingTransformer;
|
||||
import org.springframework.integration.transformer.Transformer;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.messaging.MessageHandler;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
@@ -65,6 +67,11 @@ public final class IntegrationFlowBuilder {
|
||||
|
||||
public IntegrationFlowBuilder channel(MessageChannel messageChannel) {
|
||||
Assert.notNull(messageChannel);
|
||||
if (this.currentMessageChannel != null) {
|
||||
GenericEndpointSpec endpointSpec = new GenericEndpointSpec(new BridgeHandler());
|
||||
endpointSpec.get().getT1().setInputChannel(this.currentMessageChannel);
|
||||
this.addComponent(endpointSpec).currentComponent(endpointSpec.get().getT2());
|
||||
}
|
||||
this.currentMessageChannel = messageChannel;
|
||||
return this.addComponent(this.currentMessageChannel).registerOutputChannelIfCan(this.currentMessageChannel);
|
||||
}
|
||||
@@ -75,11 +82,7 @@ public final class IntegrationFlowBuilder {
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder transform(String expression) {
|
||||
return this.transform(PARSER.parseExpression(expression));
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder transform(Expression expression) {
|
||||
return this.transform(new ExpressionEvaluatingTransformer(expression));
|
||||
return this.transform(new ExpressionEvaluatingTransformer(PARSER.parseExpression(expression)));
|
||||
}
|
||||
|
||||
public <S, T> IntegrationFlowBuilder transform(GenericTransformer<S, T> genericTransformer) {
|
||||
@@ -87,18 +90,14 @@ public final class IntegrationFlowBuilder {
|
||||
}
|
||||
|
||||
public <S, T> IntegrationFlowBuilder transform(GenericTransformer<S, T> genericTransformer,
|
||||
EndpointConfigurer<GenericEndpointSpec<MessageTransformingHandler>> endpointConfigurer) {
|
||||
EndpointConfigurer<GenericEndpointSpec> endpointConfigurer) {
|
||||
Transformer transformer = genericTransformer instanceof Transformer
|
||||
? (Transformer) genericTransformer : new MethodInvokingTransformer(genericTransformer);
|
||||
return this.register(new GenericEndpointSpec<MessageTransformingHandler>(new MessageTransformingHandler(transformer)), endpointConfigurer);
|
||||
return this.handle(new MessageTransformingHandler(transformer), endpointConfigurer);
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder filter(String expression) {
|
||||
return this.filter(PARSER.parseExpression(expression));
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder filter(Expression expression) {
|
||||
return this.filter(new ExpressionEvaluatingSelector(expression));
|
||||
return this.filter(new ExpressionEvaluatingSelector(PARSER.parseExpression(expression)));
|
||||
}
|
||||
|
||||
public <S> IntegrationFlowBuilder filter(GenericSelector<S> genericSelector) {
|
||||
@@ -111,6 +110,18 @@ public final class IntegrationFlowBuilder {
|
||||
return this.register(new FilterEndpointSpec(new MessageFilter(selector)), endpointConfigurer);
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder handle(MessageHandler messageHandler) {
|
||||
return this.handle(messageHandler, null);
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder handle(MessageHandler messageHandler, EndpointConfigurer<GenericEndpointSpec> endpointConfigurer) {
|
||||
return this.register(new GenericEndpointSpec(messageHandler), endpointConfigurer);
|
||||
}
|
||||
|
||||
public IntegrationFlowBuilder bridge(EndpointConfigurer<GenericEndpointSpec> endpointConfigurer) {
|
||||
return this.register(new GenericEndpointSpec(new BridgeHandler()), endpointConfigurer);
|
||||
}
|
||||
|
||||
private IntegrationFlowBuilder registerOutputChannelIfCan(MessageChannel outputChannel) {
|
||||
this.flow.addComponent(outputChannel);
|
||||
if (this.currentComponent != null) {
|
||||
@@ -120,6 +131,10 @@ public final class IntegrationFlowBuilder {
|
||||
else if (this.currentComponent instanceof SourcePollingChannelAdapterFactoryBean) {
|
||||
((SourcePollingChannelAdapterFactoryBean) this.currentComponent).setOutputChannel(outputChannel);
|
||||
}
|
||||
else {
|
||||
throw new BeanCreationException("The 'currentComponent' (" + this.currentComponent + ") is a one-way 'MessageHandler'" +
|
||||
"and it isn't appropriate to configure 'outputChannel'. This is the end of the integration flow.");
|
||||
}
|
||||
this.currentComponent = null;
|
||||
}
|
||||
return this;
|
||||
|
||||
@@ -67,4 +67,12 @@ public abstract class ConsumerEndpointSpec<S extends ConsumerEndpointSpec<S, H>,
|
||||
return _this();
|
||||
}
|
||||
|
||||
public S sendTimeout(long sendTimeout) {
|
||||
H handler = this.target.getT2();
|
||||
if (handler instanceof AbstractReplyProducingMessageHandler) {
|
||||
((AbstractReplyProducingMessageHandler) handler).setSendTimeout(sendTimeout);
|
||||
}
|
||||
return _this();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.springframework.integration.dsl.core;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.GenericTypeResolver;
|
||||
import org.springframework.integration.dsl.support.PollerSpec;
|
||||
import org.springframework.integration.dsl.tuple.Tuple;
|
||||
import org.springframework.integration.dsl.tuple.Tuple2;
|
||||
@@ -16,7 +16,7 @@ public abstract class EndpointSpec<S extends EndpointSpec<S, F, H>, F extends Be
|
||||
@SuppressWarnings("unchecked")
|
||||
protected EndpointSpec(H handler) {
|
||||
try {
|
||||
Class<?> fClass = ResolvableType.forClass(this.getClass()).as(EndpointSpec.class).resolveGenerics()[1];
|
||||
Class<?> fClass = GenericTypeResolver.resolveTypeArguments(this.getClass(), EndpointSpec.class)[1];
|
||||
F endpointFactoryBean = (F) fClass.newInstance();
|
||||
this.target = Tuple.of(endpointFactoryBean, handler);
|
||||
}
|
||||
|
||||
@@ -18,21 +18,27 @@ package org.springframework.integration.dsl.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.context.ApplicationEvent;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
@@ -51,6 +57,8 @@ import org.springframework.integration.dsl.channel.QueueChannelSpec;
|
||||
import org.springframework.integration.dsl.support.PollerSpec;
|
||||
import org.springframework.integration.dsl.support.Pollers;
|
||||
import org.springframework.integration.endpoint.MethodInvokingMessageSource;
|
||||
import org.springframework.integration.event.core.MessagingEvent;
|
||||
import org.springframework.integration.event.outbound.ApplicationEventPublishingMessageHandler;
|
||||
import org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice;
|
||||
import org.springframework.integration.scheduling.PollerMetadata;
|
||||
import org.springframework.integration.store.SimpleMessageStore;
|
||||
@@ -60,6 +68,7 @@ import org.springframework.integration.transformer.PayloadSerializingTransformer
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageDeliveryException;
|
||||
import org.springframework.messaging.PollableChannel;
|
||||
import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
@@ -71,18 +80,43 @@ import org.springframework.test.context.support.AnnotationConfigContextLoader;
|
||||
@RunWith(SpringJUnit4ClassRunner.class)
|
||||
public class IntegrationFlowTests {
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("flow1QueueChannel")
|
||||
private PollableChannel outputChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("inputChannel")
|
||||
private DirectChannel inputChannel;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("successChannel")
|
||||
private PollableChannel successChannel;
|
||||
|
||||
@Autowired
|
||||
private BeanFactory beanFactory;
|
||||
@Qualifier("flow3Input")
|
||||
private DirectChannel flow3Input;
|
||||
|
||||
@Autowired
|
||||
private AtomicReference<Object> eventHolder;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlowInput")
|
||||
private PollableChannel bridgeFlowInput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlowOutput")
|
||||
private PollableChannel bridgeFlowOutput;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlow2Input")
|
||||
private DirectChannel bridgeFlow2Input;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("bridgeFlow2Output")
|
||||
private PollableChannel bridgeFlow2Output;
|
||||
|
||||
@Test
|
||||
public void testPollingFlow() {
|
||||
@@ -119,6 +153,50 @@ public class IntegrationFlowTests {
|
||||
assertEquals(100, successMessage.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandle() {
|
||||
assertNull(this.eventHolder.get());
|
||||
this.flow3Input.send(new GenericMessage<>("foo"));
|
||||
assertNotNull(this.eventHolder.get());
|
||||
assertEquals("foo", this.eventHolder.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBridge() {
|
||||
GenericMessage<String> message = new GenericMessage<>("test");
|
||||
this.bridgeFlowInput.send(message);
|
||||
Message<?> reply = this.bridgeFlowOutput.receive(5000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("test", reply.getPayload());
|
||||
|
||||
try {
|
||||
this.bridgeFlow2Input.send(message);
|
||||
fail("Expected MessageDispatchingException");
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, Matchers.instanceOf(MessageDeliveryException.class));
|
||||
assertThat(e.getCause(), Matchers.instanceOf(MessageDispatchingException.class));
|
||||
assertThat(e.getMessage(), Matchers.containsString("Dispatcher has no subscribers"));
|
||||
}
|
||||
this.beanFactory.getBean("bridge", Lifecycle.class).start();
|
||||
this.bridgeFlow2Input.send(message);
|
||||
reply = this.bridgeFlow2Output.receive(5000);
|
||||
assertNotNull(reply);
|
||||
assertEquals("test", reply.getPayload());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongLastComponent() {
|
||||
try {
|
||||
new AnnotationConfigApplicationContext(InvalidLastComponentFlowContext.class);
|
||||
}
|
||||
catch (Exception e) {
|
||||
assertThat(e, Matchers.instanceOf(BeanCreationException.class));
|
||||
assertThat(e.getMessage(), Matchers.containsString("is a one-way 'MessageHandler'"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Configuration
|
||||
@EnableIntegration
|
||||
@@ -182,6 +260,7 @@ public class IntegrationFlowTests {
|
||||
.transform((Integer p) -> p * 2, c -> c.advice(this.expressionAdvice()))
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@MessageEndpoint
|
||||
@@ -193,5 +272,59 @@ public class IntegrationFlowTests {
|
||||
}
|
||||
}
|
||||
|
||||
@Configuration
|
||||
public static class ContextConfiguration3 {
|
||||
|
||||
@Bean
|
||||
public AtomicReference<Object> eventHolder() {
|
||||
return new AtomicReference<>();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ApplicationListener<MessagingEvent> eventListener() {
|
||||
return new ApplicationListener<MessagingEvent>() {
|
||||
|
||||
@Override
|
||||
public void onApplicationEvent(MessagingEvent event) {
|
||||
eventHolder().set(event.getMessage().getPayload());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow flow3() {
|
||||
return IntegrationFlows.from(MessageChannels.direct("flow3Input"))
|
||||
.handle(new ApplicationEventPublishingMessageHandler())
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow bridgeFlow() {
|
||||
return IntegrationFlows.from(MessageChannels.queue("bridgeFlowInput"))
|
||||
.channel(MessageChannels.queue("bridgeFlowOutput"))
|
||||
.get();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow bridgeFlow2() {
|
||||
return IntegrationFlows.from(MessageChannels.direct("bridgeFlow2Input"))
|
||||
.bridge(c -> c.autoStartup(false).id("bridge"))
|
||||
.channel(MessageChannels.queue("bridgeFlow2Output"))
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class InvalidLastComponentFlowContext {
|
||||
|
||||
@Bean
|
||||
public IntegrationFlow wrongLastComponent() {
|
||||
return IntegrationFlows.from(MessageChannels.direct())
|
||||
.handle(Object::toString)
|
||||
.channel(MessageChannels.direct())
|
||||
.get();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user