Is there a way to fork the Spring IntegrationFlow using DSL? - spring-integration

I want do something like this where the gateway payload is a String and serviceA & serviceB both return lists.
final IntegrationFlow flowA = flow -> flow
.handle(serviceA)
.handle((payload, headers) -> payload); // List<Object>
final IntegrationFlow flowB = flow -> flow
.handle(serviceB)
.handle((payload, headers) -> payload); // List<Object>
return IntegrationFlows
.from(myGateway) // String payload
.forkAndMerge(flowA, flowB, executor)
.handle((payload, headers) -> payload)
.get();
Is it possible to fork the flow into two and then gather up the results? Most examples of splitter & aggregators involve splitting up a list.

See the .scatterGather() variants.
Main docs for the ScatterGatherer here.
EDIT
Example:
#SpringBootApplication
public class So63605348Application {
private static final Logger log = LoggerFactory.getLogger(So63605348Application.class);
public static void main(String[] args) {
SpringApplication.run(So63605348Application.class, args);
}
#Bean
IntegrationFlow flow(TaskExecutor exec) {
return IntegrationFlows.from(() -> "foo", e -> e.poller(Pollers.fixedDelay(5000)))
.scatterGather(s -> s.applySequence(true)
.recipientFlow(subFlow -> subFlow.channel(c -> c.executor(exec))
.<String>handle((p, h) -> {
log.info(p.toString());
return p.toUpperCase();
}))
.recipientFlow(subFlow -> subFlow.channel(c -> c.executor(exec))
.<String>handle((p, h) -> {
log.info(p.toString());
return p + p;
})))
.handle(System.out::println)
.get();
}
#Bean
public TaskExecutor exec() {
ThreadPoolTaskExecutor exec = new ThreadPoolTaskExecutor();
exec.setCorePoolSize(2);
return exec;
}
}
Result
2020-08-26 17:33:56.769 INFO 50829 --- [ exec-1] com.example.demo.So63605348Application : foo
2020-08-26 17:33:56.769 INFO 50829 --- [ exec-2] com.example.demo.So63605348Application : foo
GenericMessage [payload=[foofoo, FOO], headers=...
EDIT2
If you prefer not to nest the subflows, you can factor them out...
#Bean
IntegrationFlow flow(TaskExecutor exec) {
return IntegrationFlows.from(() -> "foo", e -> e.poller(Pollers.fixedDelay(5000)))
.scatterGather(s -> s.applySequence(true)
.recipientFlow(flow2())
.recipientFlow(flow3()))
.handle(System.out::println)
.get();
}
#Bean
IntegrationFlow flow2() {
return f -> f
.<String>handle((p, h) -> {
log.info(p.toString());
return p + p;
});
}
#Bean
IntegrationFlow flow3() {
return f -> f
.<String>handle((p, h) -> {
log.info(p.toString());
return p.toUpperCase();
});
}
Or you can use the pub/sub channel variant...
#Bean
IntegrationFlow flow() {
return IntegrationFlows.from(() -> "foo", e -> e.poller(Pollers.fixedDelay(5000)))
.scatterGather(pubSub())
.handle(System.out::println)
.get();
}
#Bean
PublishSubscribeChannel pubSub() {
PublishSubscribeChannel pubSub = new PublishSubscribeChannel(exec());
pubSub.setApplySequence(true);
return pubSub;
}
#Bean
IntegrationFlow flow2() {
return IntegrationFlows.from("pubSub")
.<String>handle((p, h) -> {
log.info(p.toString());
return p + p;
})
.get();
}
#Bean
IntegrationFlow flow3() {
return IntegrationFlows.from("pubSub")
.<String>handle((p, h) -> {
log.info(p.toString());
return p.toUpperCase();
})
.get();
}

Related

Routing to error channel but getting "Dispatcher has no subscribers for channel"

I have to alter an existing flow in Spring Integration (4.3.12, Java DSL). There is an existing SOAP call, after that I have to insert a new SOAP call (it is done) and if the existing SOAP call wasn't successful then the new SOAP call has to be skipped (it is where I have problems). In the flow below the acmePreCompEnricher is the existing call and the ifMLCallRequiredEnricher is the new one.
return flow -> flow.channel(ORCH_REQUEST_INPUT)
.<HomeRequest, HomeModel>transform(requestToModelTransformer)
...
//
.enrich(this::acmePreCompRequestEnricher)
.enrich(this::acmePreCompEnricher)
.handle(this.acmePreCompResponseValidator())
//
.enrich(this::ifMLCallRequiredEnricher)
//
.enrich(this::acmeRequestEnricher)
.enrich(this::acmeEnricher)
...
So in the acmePreCompEnricher I set the error channel that will handle the error:
ContentEnricher contentEnricher = enricherSpec
.requestPayload(Message::getPayload)
.requestChannel(acmePreCompEnrichmentInputChannel())
.replyChannel(acmePreCompEnrichmentOutputChannel())
.get();
contentEnricher.setErrorChannel(skipMLInputChannel());
#Bean(name = "skip.ml.input")
public MessageChannel skipMLInputChannel() {
return MessageChannels.direct().get();
}
In case of SOAP fault the message will go to the following flow:
#Bean
public IntegrationFlow processSkipML() {
return flow -> flow.channel("skip.ml.input")
.transform(ErrorMessage.class, (ErrorMessage m) -> {
Message originalMessage = ((MessageHandlingException)m.getPayload()).getFailedMessage();
return MessageBuilder.withPayload(originalMessage.getHeaders().get(HEADER_MODEL, HomeModel.class))
.copyHeaders(originalMessage.getHeaders())
.build();
})
.enrich(e -> e.propertyFunction("skipMLCall", m -> true))
.channel("enrich.ifMLCallNeeded.input");
}
Behind the ifMLCallRequiredEnricher the following flow can be found:
#Bean
public IntegrationFlow processIfMLCallRequiredFlow() {
return flow -> flow.channel("enrich.ifMLCallNeeded.input")
.route(ifMLCallRequired(), routeToMLGatewayOrBypassCall())
.channel("enrich.ifMLCallNeeded.output");
}
The ifMLCallRequired() checks if the skipMLCall is false (in case of error it is set to true in the flow after the error channel) and it will execute the new SOAP call otherwise it will skip it.
When there isn't SOAP fault the flow will go through fine.
However when SOAP fault is thrown (i.e. the message goes through the error channel) then I get the following exception:
2020-05-22 10:10:48,023 ERROR com.acme.webservice.OrchestrationServiceEndpoint Thread=qtp14486859-13 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d Error
org.springframework.messaging.MessagingException: failure occurred in error-handling flow; nested exception is org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'enrich.ifMLCallNeeded.output'.; nested exception is org.springframework.integration.MessageDispatchingException: Dispatcher has no subscribers, failedMessage=GenericMessage [payload=uk.co.acme.payload.request._2017._06.Message#4a5e6c, headers={replyChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#19d4520, errorChannel=org.springframework.messaging.core.GenericMessagingTemplate$TemporaryReplyChannel#19d4520, ws_soapAction=http://www.acme.co.uk/XRTEService/ProcessTran, id=902bd270-89d8-62e9-b00f-b69399241bd1, timestamp=1590138648017}], ...}]
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:489)
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:426)
at org.springframework.integration.transformer.ContentEnricher$Gateway.sendAndReceiveMessage(ContentEnricher.java:481)
at org.springframework.integration.transformer.ContentEnricher.handleRequestMessage(ContentEnricher.java:383)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:109)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:148)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:121)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:89)
So when there is not SOAP fault then everything is fine so the channel enrich.ifMLCallNeeded.output has subscribers which is the next enricher, see the following log entry:
2020-05-24 20:37:58,819 INFO org.springframework.integration.channel.DirectChannel Thread=qtp14486859-10 MDC=16d7cc4c-c9da-449b-8bfa-504e6d81185d Channel 'enrich.ifMLCallNeeded.output' has 1 subscriber(s).
However when SOAP fault arises then the channel won't have a subscriber (I cannot find any log entry). I think it is because I am trying to hijack the flow with the error channel.
But what can I do in this case?
I'd appreciate any help as I am stuck at the moment. Thank you very much!
Regards,
V.
Here is an example of how to properly handle errors on an enricher subFlow:
#SpringBootApplication
public class So61991580Application {
public static void main(String[] args) {
SpringApplication.run(So61991580Application.class, args);
}
private final AtomicBoolean which = new AtomicBoolean();
#Bean
public IntegrationFlow flow() {
return IntegrationFlows.from(() -> new Foo(this.which.getAndSet(!which.get()) ? "foo" : "qux"),
e -> e.poller(Pollers.fixedDelay(5000)))
.enrich(spec -> spec.requestChannel("soap1.input")
.errorChannel("soap1Error.input"))
.route("payload.bar", r -> r
.channelMapping("good", "soap2.input")
.channelMapping("bad", "cleanUp.input"))
.get();
}
#Bean
public IntegrationFlow soap1() {
return f -> f
.handle(Foo.class, (payload, headers) -> {
if (payload.getFoo().equals("foo")) {
throw new RuntimeException("test enrich failure");
}
payload.setBar("good");
return payload;
});
}
#Bean
public IntegrationFlow soap2() {
return f -> f
.handle(Foo.class, (payload, headers) -> {
payload.setBaz("soap2");
return payload;
})
.channel("cleanUp.input");
}
#Bean
public IntegrationFlow soap1Error() {
return f -> f.<MessagingException, Foo>transform(ex -> {
Foo foo = (Foo) ex.getFailedMessage().getPayload();
foo.setBar("bad");
return foo;
});
}
#Bean
public IntegrationFlow cleanUp() {
return f -> f.log();
}
public static class Foo {
private final String foo;
private String bar;
private String baz;
public Foo(String foo) {
this.foo = foo;
}
public String getFoo() {
return this.foo;
}
public String getBar() {
return this.bar;
}
public void setBar(String bar) {
this.bar = bar;
}
public String getBaz() {
return this.baz;
}
public void setBaz(String baz) {
this.baz = baz;
}
#Override
public String toString() {
return "Foo [foo=" + this.foo + ", bar=" + this.bar + ", baz=" + this.baz + "]";
}
}
}
GenericMessage [payload=Foo [foo=foo, bar=bad, baz=null], headers={id=e5a943c7-dcf1-47f3-436e-5d0350a1c6f5, timestamp=1590511422083}]
GenericMessage [payload=Foo [foo=qux, bar=good, baz=soap2], headers={id=a99d7ddb-2f40-f0f7-08b6-6340563e011d, timestamp=1590511427086}]
2

EnpointSpec fails in Spring Integration Java DSL

#SpringBootApplication
#EnableIntegration
public class SpringIntegrationHttpApplication {
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationHttpApplication.class, args);
}
#Bean
public HttpRequestHandlerEndpointSpec httpInboundAdapter() {
return Http
.inboundChannelAdapter("/failing-test")
.requestMapping(r -> r.methods(HttpMethod.GET)
.params("rObjectId"))
.payloadExpression("#requestParams.rObjectId[0]")
;
}
#Bean
public IntegrationFlow myFlow() {
return IntegrationFlows.from(Http
.inboundChannelAdapter("/test")
.requestMapping(r -> r.methods(HttpMethod.GET)
.params("rObjectId"))
.payloadExpression("#requestParams.rObjectId[0]"))
.transform(p -> p)
.handle(p -> System.out.println(p))
.get();
}
#Bean
public IntegrationFlow yourFlow() {
return IntegrationFlows.from(httpInboundAdapter())
.transform(p -> p)
.handle(p -> System.out.println(p))
.get();
}
}
For the above code, the link: /test works but not /failing-test.
I get "Endpoint is stopped" on chrome.
What might be the reason?
I am not sure why, yet, but remove the #Bean from the spec (just use a simple method that returns the Spec).

publishSubscribeChannel unit test can't work well

my integration config class is below,when i do some unit test on them,found that:
when i send message to UserRecipientSubscribeCacheChannel,it work well;
when i send a message to an upper level of channel userReportWriteCompletedRouteChannel, it work failed,and it don't throws any exceptions yet. i can't understand it. the messages that i sent is same,of course.
because of the fail section, the next handler can't work ok.
ty!!
it work ok below, it print ===>ip location channel message:GenericMessage [payload=[MailRecipientActionDocumen...and ===>user recipient channel message:GenericMessage [payload=[UserRecipientSubscribeDataRedisStructure...
#Test
public void test_sendMessageUserRecipientSubscribeCacheChannel(){
UserRecipientSubscribeCacheChannel.send(createMessageWithIp());
}
it work fail below, it print ===>ip location channel message:GenericMessage [payload=[MailRecipientActionDocumen... only
notice that: the fail section, In front of handler has a transformer.
#Test
public void test_sendMessageToRouteChannel() {
userReportWriteCompletedRouteChannel.send(createMessageWithIp());
}
my code config below:
#Bean
public SubscribableChannel userReportWriteCompletedSubscribeChannel() {
return new DirectChannel();
}
#Bean
public QueueChannel userReportWriteCompletedRouteChannel() {
return new QueueChannel();
}
#Bean
public MessageChannel ipLocationResolveCacheChannel() {
return new DirectChannel();
}
#Bean
public MessageChannel userRecipientSubscribeCacheChannel() {
return new DirectChannel();
}
#MessagingGateway(name = "userReportWriteCompletedListener",
defaultRequestChannel = "userReportWriteCompletedRouteChannel")
public interface UserReportWriteCompletedListener {
#Gateway
void receive(List<UserMailRecipientActionDocument> docs);
}
#Bean
public IntegrationFlow bridgeFlow() {
return flow -> flow.channel("userReportWriteCompletedRouteChannel")
.bridge(bridgeSpe -> bridgeSpe
.poller(pollerFactory -> pollerFactory.fixedRate(500).maxMessagesPerPoll(1)))
.channel("userReportWriteCompletedSubscribeChannel")
;
}
#Bean
public IntegrationFlow subscribeFlow() {
return IntegrationFlows.from("userReportWriteCompletedSubscribeChannel")
.publishSubscribeChannel(publishSubscribeSpec -> publishSubscribeSpec
.subscribe(flow -> flow
.channel(IP_LOCATION_RESOLVE_CACHE_CHANNEL)
)
.subscribe(flow -> flow
.channel(USER_RECIPIENT_SUBSCRIBE_CACHE_CHANNEL)
))
.get();
}
#Bean
public RedisStoreWritingMessageHandler ipLocationResolveCacheHandler(RedisTemplate<String, ?> redisTemplate) {
final RedisStoreWritingMessageHandler ipLocationResolveCacheHandler =
new RedisStoreWritingMessageHandler(redisTemplate);
ipLocationResolveCacheHandler.setKey("IP_LOCATION_RESOLVE_CACHE");
return ipLocationResolveCacheHandler;
}
#Bean
public RedisStoreWritingMessageHandler userRecipientSubscribeCacheHandler(RedisTemplate<String, ?> redisTemplate) {
final RedisStoreWritingMessageHandler userRecipientSubscribeCacheHandler =
new RedisStoreWritingMessageHandler(redisTemplate);
userRecipientSubscribeCacheHandler.setKey("USER_RECIPIENT_SUBSCRIBE_CACHE");
return userRecipientSubscribeCacheHandler;
}
#Bean
public IpLocationResolveRedisStructureFilterAndTransformer recipientActionHasIpFilterAndTransformer() {
return new IpLocationResolveRedisStructureFilterAndTransformer();
}
#Bean
public UserRecipientSubscribeDataRedisStructureTransformer subscribeDataRedisStructureTransformer(
IpLocationClient ipLocationClient) {
return new UserRecipientSubscribeDataRedisStructureTransformer(ipLocationClient);
}
#Bean
public IntegrationFlow ipLocationResolveCacheFlow(
#Qualifier("ipLocationResolveCacheHandler") RedisStoreWritingMessageHandler writingMessageHandler) {
return flow -> flow.channel(IP_LOCATION_RESOLVE_CACHE_CHANNEL)
.handle(message -> {
System.out.println("===>ip location channel message:" + message);
})
;
}
#Bean
public IntegrationFlow userRecipientActionDataCacheFlow(
#Qualifier("userRecipientSubscribeCacheHandler") RedisStoreWritingMessageHandler messageHandler,
UserRecipientSubscribeDataRedisStructureTransformer transformer) {
return flow -> flow.channel(USER_RECIPIENT_SUBSCRIBE_CACHE_CHANNEL)
.transform(transformer)
.handle(message -> {
System.out.println("===>user recipient channel message:" + message);
})
}
i expect 2 print message info ,but print 1 only.
Today, i found that the bridge flow may had some problem, When I move the handler behind the channeluserReportWriteCompletedSubscribeChannel, it can't print any message;
when i remove channel and add handler directly, it will print message.
does i use the bridge wrong?
#Bean
public IntegrationFlow bridgeFlow() {
return flow -> flow.channel("userReportWriteCompletedRouteChannel")
.bridge(bridgeSpe -> bridgeSpe
.poller(pollerFactory -> pollerFactory.fixedRate(100).maxMessagesPerPoll(1)))
.handle(message -> {
System.out.println("===>route channel message:" + message);
}) // handle ok , will print message
.channel("userReportWriteCompletedSubscribeChannel")
// .handle(message -> {
// System.out.println("===>route channel message:" + message);
// }) // handle fail , will not printing message
;
}
test:
#Test
//invalid
public void test_sendMessageToRouteChannel() {
userReportWriteCompletedRouteChannel.send(createMessageWithIp());
}

Async split/aggregate gateway flows

I'm trying to build a recipe for asynchronous orchestration using spring integration gateways (both inbound and outbound). After seeing an example here, I tried using scatter-gather like this:
#Configuration
public class IntegrationComponents {
#Value("${rest.endpoint.base}")
private String endpointBase;
#Bean
public HttpRequestHandlingMessagingGateway inboundGateway() {
return Http.inboundGateway("/test-inbound-gateway-resource")
.requestMapping(mapping -> mapping.methods(HttpMethod.POST))
.requestTimeout(3000)
.replyTimeout(3000)
.get();
}
#Bean
public HttpRequestExecutingMessageHandler outboundGateway1() {
return Http.outboundGateway(endpointBase + "/test-resource-1")
.httpMethod(HttpMethod.POST)
.expectedResponseType(String.class)
.get();
}
#Bean
public HttpRequestExecutingMessageHandler outboundGateway2() {
return Http.outboundGateway(endpointBase + "/test-resource-2")
.httpMethod(HttpMethod.POST)
.expectedResponseType(String.class)
.get();
}
#Bean
public StandardIntegrationFlow integrationFlow() {
ExecutorService executor = Executors.newCachedThreadPool();
IntegrationFlow flow1 = IntegrationFlows.from(MessageChannels.executor(executor))
.handle(outboundGateway1())
.get();
IntegrationFlow flow2 = IntegrationFlows.from(MessageChannels.executor(executor))
.handle(outboundGateway2())
.get();
return IntegrationFlows
.from(inboundGateway())
.transform(String.class, String::toUpperCase)
.channel(MessageChannels.executor(executor))
.scatterGather(
scatterer -> scatterer
.applySequence(true)
.recipientFlow(flow1)
.recipientFlow(flow2),
gatherer -> gatherer
.outputProcessor(messageGroup -> {
List<Message<?>> list = new ArrayList<>(messageGroup.getMessages());
String payload1 = (String) list.get(0).getPayload();
String payload2 = (String) list.get(1).getPayload();
return MessageBuilder.withPayload(payload1 + "+" + payload2).build();
}))
.get();
}
}
This executes, but my payloads are swapped, because in this case outboundGateway1 takes longer to execute than outboundGateway2. Payload 2 comes first, then payload 1.
Is there a way to tell scatter-gather to define/maintain order when sending to the output processor?
On a similar note, maybe split/aggregate and/or using a router is a better pattern here? But if so, what would that look like?
I tried the following split/route/aggregate, but it failed saying "The 'currentComponent' (org.springframework.integration.router.RecipientListRouter#b016b4e) is a one-way 'MessageHandler' and it isn't appropriate to configure 'outputChannel'. This is the end of the integration flow.":
#Configuration
public class IntegrationComponents {
#Value("${rest.endpoint.base}")
private String endpointBase;
#Bean
public HttpRequestHandlingMessagingGateway inboundGateway() {
return Http.inboundGateway("/test-inbound-gateway-resource")
.requestMapping(mapping -> mapping.methods(HttpMethod.POST))
.requestTimeout(3000)
.replyTimeout(3000)
.get();
}
#Bean
public HttpRequestExecutingMessageHandler outboundGateway1() {
return Http.outboundGateway(endpointBase + "/test-resource-1")
.httpMethod(HttpMethod.POST)
.expectedResponseType(String.class)
.get();
}
#Bean
public HttpRequestExecutingMessageHandler outboundGateway2() {
return Http.outboundGateway(endpointBase + "/test-resource-2")
.httpMethod(HttpMethod.POST)
.expectedResponseType(String.class)
.get();
}
#Bean
public StandardIntegrationFlow integrationFlow() {
ExecutorService executor = Executors.newCachedThreadPool();
IntegrationFlow flow1 = IntegrationFlows.from(MessageChannels.executor(executor))
.handle(outboundGateway1())
.get();
IntegrationFlow flow2 = IntegrationFlows.from(MessageChannels.executor(executor))
.handle(outboundGateway2())
.get();
return IntegrationFlows
.from(inboundGateway())
.transform(String.class, String::toUpperCase)
.split()
.channel(MessageChannels.executor(executor))
.routeToRecipients(r -> r
.recipientFlow(flow1)
.recipientFlow(flow2))
.aggregate()
.get();
}
}
Can you not simply Collections.sort() the list in the output processor? Each message will have a IntegrationMessageHeaderAccessor.SEQUENCE_NUMBER header since you set applySequence.

Breaking up DSL IntegrationFlows

I've been playing around with Spring Integration (SI) DSL. I have a Rest service with the following Async Gateway defined :
#MessagingGateway
public interface Provision {
#Async
#Gateway(requestChannel = "provision.input")
ListenableFuture<List<ResultDto>> provision(List<ItemsDto> stuff);
}
From the Line-by-line walk-through I have the follow example IntegrationFlow.
#Bean
public IntegrationFlow provision() {
return f -> f
.split(ArrayList.class, List::toArray)
.channel(c -> c.executor(Executors.newCachedThreadPool()))
.<ItemsDto, String>route(ItemsDto::getType, m -> m
.subFlowMapping("IPTV", sf -> sf
.<ItemsDto, String>route(ItemsDto::getAction, m2 -> m2
.subFlowMapping("OPEN", sf2 -> sf2
.handle((p, h) -> iptvService.open((ItemsDto) p))))
)
)
.aggregate();
}
I have several layers of routing as you can see. I need to break things up a bit. I've tried several things which don't work (here I don't get a response...the thread doesn't wait):
#Bean(name = "routerInput")
private MessageChannel routerInput() {
return MessageChannels.direct().get();
}
#Bean
public IntegrationFlow provision() {
return f -> f
.split(ArrayList.class, List::toArray)
.channel(c -> c.executor(Executors.newCachedThreadPool()))
.<ItemsDto, String>route(ItemsDto::getType, m ->
m.subFlowMapping("IPTV", sf -> sf.channel("routerInput"))
)
.aggregate();
}
#Bean
public IntegrationFlow action() {
return IntegrationFlows.from("routerInput")
.<ItemsDto, String>route(ItemsDto::getAction, m -> m
.subFlowMapping("OPEN", sf -> sf
.handle(p -> iptvService.open((ItemsDto) p.getPayload())))).get();
}
I'm obviously conceptually missing something :) Can someone perhaps assist with a "how to and why" opinion?
I have a list of items that need to be split, routed by "type", then routed by "action", and finally aggregated (containing response of handler). Each handled item needs to process in parallel.
Thanks in advance
Update:
From Artem's suggestion I removed all the async stuff. I trimmed it down to almost nothing...
#Bean(name = "routerInput")
private MessageChannel routerInput() {
return MessageChannels.direct().get();
}
#Bean
public IntegrationFlow provision() {
return f -> f
.split()
.<ItemDto, String>route(ItemDto::getType, m ->
m.subFlowMapping("IPTV", sf -> sf.channel("routerInput")))
.aggregate();
}
#Bean
public IntegrationFlow action() {
return IntegrationFlows.from("routerInput")
.<ItemDto, String>route(ItemDto::getAction, m -> m
.subFlowMapping("OPEN", sf -> sf
.handle((p, h) -> iptvService.open((ItemDto) p)))).get();
}
I got it to respond by changing
.handle(p ->
to this
.handle((p, h) ->
So it at least responds, but it does not aggregate the 3 test items that were split. Output consists of 1 item. Do I need to use a stream collect? Release policy? Shouldn't this be fine?
It's probably simpler to use channelMapping rather than subflowMapping if you want to break it apart...
#Bean
public IntegrationFlow typeRoute() {
return IntegrationFlows.from(foo())
.split()
.<ItemsDto, String>route(ItemsDto::getType, m -> m
.channelMapping("foo", "channel1")
.channelMapping("bar", "channel2"))
.get();
}
#Bean
public IntegrationFlow fooActionRoute() {
return IntegrationFlows.from(channel1())
.<ItemsDto, String>route(ItemsDto::getAction, m -> m
.channelMapping("foo", "channel3")
.channelMapping("bar", "channel4"))
.get();
}
#Bean
public IntegrationFlow barActionRoute() {
return IntegrationFlows.from(channel1())
.<ItemsDto, String>route(ItemsDto::getAction, m -> m
.channelMapping("foo", "channel5")
.channelMapping("bar", "channel6"))
.get();
}
#Bean
public IntegrationFlow fooFooHandle() {
return IntegrationFlows.from(channel3())
// handle
.channel(aggChannel())
.get();
}
Create flows for the other options and aggregate each result:
// fooBarHandle(), barFooHandle(), barBarHandle()
#Bean IntegrationFlow agg() {
return IntegrationFlows.from(aggChannel())
.aggregate()
.get();
}
The parallelism is handled by using ExecutorChannels...
#Bean
public MessageChannel channel1() {
return new ExecutorChannel(exec());
}
#Bean
public MessageChannel channel2() {
return new ExecutorChannel(exec());
}
#Bean
public MessageChannel channel3() {
return new DirectChannel();
}
#Bean
public MessageChannel channel4() {
return new DirectChannel();
}
#Bean
public MessageChannel channel5() {
return new DirectChannel();
}
#Bean
public MessageChannel channel6() {
return new DirectChannel();
}
#Bean
public MessageChannel aggChannel() {
return new DirectChannel();
}
I don't see any big problems in your configuration and it really should work.
Want I don't like there is:
If you use async Gateway (ListenableFuture<List<ResultDto>>) you don't need #Async annotation there, because it will be already by the Gateway contract.
You don't need convert List::toArray if your payload is a List already. It is just enough to use .split() without params.
That's for the design style.
I'm not sure yet what's the problem there, but would you mind to make your whole flow sync and share with us here the DEBUG for the flow and point out where you see the problem.
Moved the aggregate to the "action" Bean and it worked.
Thanks for your patience :)

Resources