spring-integration parallel split-route-aggregate flow fails due to one-way MessageHandler

谁都会走 提交于 2021-02-08 06:14:29

问题


I want to process a list of items in parallel by splitting them, routing each item to their appropriate gateway and aggregating the results. However, my application does not start, I get the following error:

BeanCreationException: The 'currentComponent' ... is a one-way 'MessageHandler' 
and it isn't appropriate to configure 'outputChannel'. 
This is the end of the integration flow.

This is a sample flow definition which illustrates the behaviour:

@Bean
public IntegrationFlow parallelSplitRouteAggregateFlow() {
    return IntegrationFlows
            .from(Http.inboundGateway("/trigger"))
            .handle(message -> Arrays.asList(1, 2, 3))
            .split()
            .channel(MessageChannels.executor(Executors.newCachedThreadPool()))
            .<Integer, Boolean>route(o -> o % 2 == 0, m -> m
                    .subFlowMapping(true, oddFlow())
                    .subFlowMapping(false, evenFlow()))
            .aggregate()
            .get();
}

@Bean
public IntegrationFlow oddFlow() {
    return flow -> flow.<Integer>handle((payload, headers) -> "odd");
}

@Bean
public IntegrationFlow evenFlow() {
    return flow -> flow.<Integer>handle((payload, headers) -> "even");
}

I have seen Error 'is a one-way 'MessageHandler' for spring-integration aggregator DSL, but the solution there does not apply here, I am not logging in a handle() method. I also tried to add .defaultOutputToParentFlow() to the mappingDefinition because the cafe example uses it, but that makes no difference either.

I should mention that this is spring-integration 5.0.4 with spring-boot 2.0.1 release.


回答1:


You problem is here:

.handle(message -> Arrays.asList(1, 2, 3))

if you would use an inline implementation, it would look like:

.handle(new MessageHandler() {

        @Override
        public void handleMessage(Message<?> message) throws MessagingException {
            Arrays.asList(1, 2, 3);
        }
})

Pay attention to the void return type. Since there is nothing to return, therefore there is nothing to send downstream - is a one-way 'MessageHandler'.

To fix your problem you need to do this:

 .handle((p, h) -> Arrays.asList(1, 2, 3))

which is equivalent to this:

.handle(new GenericHandler<Object>() {

        @Override
        public Object handle(Object p, Map<String, Object> h) {
            return Arrays.asList(1, 2, 3);
        }
})

Actually my IDEA say me for your variant like:

That gives me some hint that I'm doing something wrong.

UPDATE

The working code:

@Bean
public IntegrationFlow parallelSplitRouteAggregateFlow() {
    return IntegrationFlows
            .from(Http.inboundGateway("/trigger"))
            .handle((p, h) -> Arrays.asList(1, 2, 3))
            .split()
            .channel(MessageChannels.executor(Executors.newCachedThreadPool()))
            .<Integer, Boolean>route(o -> o % 2 == 0, m -> m
                    .subFlowMapping(true, sf -> sf.gateway(oddFlow()))
                    .subFlowMapping(false, sf -> sf.gateway(evenFlow())))
            .aggregate()
            .get();
}

@Bean
public IntegrationFlow oddFlow() {
    return flow -> flow.<Integer>handle((payload, headers) -> "odd");
}

@Bean
public IntegrationFlow evenFlow() {
    return flow -> flow.<Integer>handle((payload, headers) -> "even");
}



回答2:


@Bean
public IntegrationFlow parallelSplitRouteAggregateFlow() {
    return IntegrationFlows
            .from(Http.inboundGateway("/trigger"))
            .handle((p, h) -> Arrays.asList(1, 2, 3))
            .split()
            .channel(MessageChannels.executor(Executors.newCachedThreadPool()))
            .<Integer, Boolean>route(o -> o % 2 == 0, m -> m
                    .subFlowMapping(true, oddFlow())
                    .subFlowMapping(false, evenFlow()))
            .get();
}

@Bean
public IntegrationFlow oddFlow() {
    return flow -> flow.<Integer>handle((payload, headers) -> "odd")
            .channel("agg.input");
}

@Bean
public IntegrationFlow evenFlow() {
    return flow -> flow.<Integer>handle((payload, headers) -> "even")
        .channel("agg.input");
}

@Bean
public IntegrationFlow agg() {
    return f -> f.aggregate();
}




回答3:


If what you need is to "distribute" messages over several "workers", and have the messages back at a join point, there's the method .scatterGather(...). Deemingly, it wraps up the .route(...) functionality in a way more suitable to be used within the IntegrationFlow's domain.

It is showed in the following example:

    @Bean
    public IntegrationFlow evenOddFlows() {
        return IntegrationFlows.from(Http.inboundGateway("/trigger"))
                .handle((payload,headers)->Arrays.asList(1,2,3))
                .split()
                .scatterGather(r->r.applySequence(true)
                .recipientFlow(m->(int)m.getPayload()%2==0, evenFlow-> evenFlow.log(m->"Even flow with payload: "+m.getPayload()).<Integer,Integer>transform(h-> h+50)
                        .handle((payload,headers)->(int)payload+50).log(m->"At Even flow end with payload: "+m.getPayload())
                        .handle((payload,headers)->payload) /* This .handle(...) doesn't do a real job.
* Instead, it is to patch something that at least I regard as a bug.
* Having not the .handle(...) would leave the .log(...) at the end of the flow.
* After crossing a .log(...) if right at the flow's end, the response message doesn't arrive back the parent flow (hence my aprising there is a bug).
* With the "appended" .handle(...) afterwards, avoid the .log(...) being the last one in the flow, as well as tests show the message is sent away where the parent flow receives it.
*/
                        )
                        .recipientFlow(m->(int)m.getPayload()%2!=0, oddFlow-> oddFlow.log(m->"Odd flow with payload: "+m.getPayload()).<Integer,Integer>transform(h-> h+10)
                        .handle((payload,headers)->(int)payload+10).log(m->"At Odd flow end with payload: "+m.getPayload())
                        .handle((payload,headers)->payload) // This .handle(...) I needed as a patch because otherwise the .log(...) being the last one in the subflow swallowed the message
                        )
                        )
                        .aggregate()
                        .get()
                ;
    }
curl -i -H "Content-type: application/json" http://localhost:8080/trigger

Curl outputs:

[[21],[102],[23]]

Logs:

2019-05-17 16:19:11.061  INFO 10148 --- [nio-8080-exec-1] o.s.integration.handler.LoggingHandler   : Odd flow with payload: 1
2019-05-17 16:19:11.061  INFO 10148 --- [nio-8080-exec-1] o.s.integration.handler.LoggingHandler   : At Odd flow end with payload: 21
2019-05-17 16:19:11.061  INFO 10148 --- [nio-8080-exec-1] o.s.integration.handler.LoggingHandler   : Even flow with payload: 2
2019-05-17 16:19:11.061  INFO 10148 --- [nio-8080-exec-1] o.s.integration.handler.LoggingHandler   : At Even flow end with payload: 102
2019-05-17 16:19:11.061  INFO 10148 --- [nio-8080-exec-1] o.s.integration.handler.LoggingHandler   : Odd flow with payload: 3
2019-05-17 16:19:11.061  INFO 10148 --- [nio-8080-exec-1] o.s.integration.handler.LoggingHandler   : At Odd flow end with payload: 23


来源:https://stackoverflow.com/questions/50121384/spring-integration-parallel-split-route-aggregate-flow-fails-due-to-one-way-mess

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!