0

I'm using Spring-integration in my project and the pattern used is scatter-gather. Here three parallel processes are being carried out. Before any of the parallel process starts I want to call a method which returns nothing but that will validate the Request body that is being sent via gateway to the main flow.

Below is the code - -

//Config file

@Configuration
    public class IntegrationConfiguration {
      @Autowired LionsServiceImpl lionsService;
    
      long dbId = new SequenceGenerator().nextId();
  //   Main flow
  @Bean
  public IntegrationFlow flow() {
    return flow ->
        flow.split()
            .log()
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .convert(LoanProvisionRequest.class)
            .scatterGather(
                scatterer ->
                    scatterer
                        .applySequence(true)
                        .recipientFlow(flow1())
                        .recipientFlow(flow2())
                        .recipientFlow(flow3()),
                gatherer -> gatherer.releaseLockBeforeSend(true))
            .log()
            .aggregate(a -> a.outputProcessor(MessageGroup::getMessages))
            .channel("output-flow");
  }
  //   flow1
  @Bean
  public IntegrationFlow flow1() {
    return integrationFlowDefination ->
        integrationFlowDefination
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .handle(
                message -> {
                  try {
                    lionService.saveLionRequest(
                        (LionRequest) message.getPayload(), String.valueOf(dbId));
                  } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                  }
                });
  }

  //   flow2
  @Bean
  public IntegrationFlow flow2() {
    return integrationFlowDefination ->
        integrationFlowDefination
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .handle(
                message ->
                    lionService.getData(
                        (LionRequest) message.getPayload(), SourceSystem.PROVISION))
            .log();
  }

  //  flow3
  @Bean
  public IntegrationFlow flow3() {
    return integrationFlowDefination ->
        integrationFlowDefination
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .handle(
                message ->
                    lionService.prepareCDRequest(
                        (LionRequest) message));
  }

  @Bean
  public MessageChannel replyChannel() {
    return MessageChannels.executor("output-flow", outputExecutor()).get();
  }

  @Bean
  public ThreadPoolTaskExecutor outputExecutor() {
    ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
    pool.setCorePoolSize(4);
    pool.setMaxPoolSize(4);
    return pool;
  }
}  

Here before passing LionRequest to the parallel flows in the scatter block I want to call a method probably just before/after calling split() to validate that request and if the request is not as per my standard then my method will throw exception and the main flow also should stop there. Where and How should I call that method. That method returns nothing and accept LionRequest as input parameter.

Gateway

 @MessagingGateway
    public interface LionGateway {
    
      @Gateway(requestChannel = "flow.input", replyChannel = "output-flow")
      List<?> echo(LionRequest lionRequest);
    }

1 Answers1

2

You have that flow.split(). So, to validate whatever is an input, you just need to insert one more endpoint before. Something like this:

flow
   .<LionRequest>handle((p, h) -> validate(p))
   .split()

for example.

Then you go to the Spring Validation API: https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#validator. Inject a Validator into your @Configuration and call it something like this:

LionRequest validate(LionRequest value) {
    BeanPropertyBindingResult errors = new BeanPropertyBindingResult(value, "request");
    ValidationUtils.invokeValidator(this.validator, value, errors);
    if (errors.hasErrors()) {
        throw new BindException(errors);
    }
    return value;
}

Or you do something similar in the dedicated component, and use its method call in that handle() lambda.

We definitely need to revise an out-of-the-box validation support: https://jira.spring.io/browse/INT-4068 ...

UPDATE

If you have some void validate(LionRequest) method, you can delegate into it like this:

 .<LionRequest>handle((p, h) -> {
           validate(p);
           return p;
        })

The point is that you need to continue the flow somehow, but void return is a signal to stop the flow. So, to validate and go on when no errors, you need to delegate and return the request payload since it is valid.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Hi @Artem, if I'm using the handle method as you've suggested it's showing compile error as -> Bad return type in lambda expression: void cannot be converted to object. Kindly help. – Somnath Mukherjee Jul 13 '22 at 16:39
  • Yeah... Sorry, must be `return value`. Just copied it from some my other code blindly. Fixing my answer. – Artem Bilan Jul 13 '22 at 16:40
  • actually I have my own validate method which doesn't return anything. It's just validate and throw exception if find any bad request. So kindly suggest how do I add that void method into here. – Somnath Mukherjee Jul 13 '22 at 16:45
  • See an UPDATE in my answer. – Artem Bilan Jul 13 '22 at 16:49
  • Hi Artem, is there a way that if I use overloaded method and in the same way I can send two arguments? – Somnath Mukherjee Jul 18 '22 at 16:56
  • I think that fully different question and overloaded is only going to work from the current message context: you can use only a `payload` and headers for those arguments. So, you have to have a respective mapping for their expectations: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations – Artem Bilan Jul 18 '22 at 17:00
  • Can u please elaborate a bit? if u want I'll ask a different question on this – Somnath Mukherjee Jul 18 '22 at 17:04
  • Yes, please, a new SO question. This one about calling a validation from the integration flow an d we are good with that I believe. – Artem Bilan Jul 18 '22 at 17:08
  • Yes Artem. Good with that. – Somnath Mukherjee Jul 18 '22 at 17:09