1

I'm using spring integration in my current project. I want to have a reusable flow for different requests.

Config file

@Configuration
    public class IntegrationConfiguration {
      @Autowired LionsServiceImpl lionsService;
    
      long dbId = new SequenceGenerator().nextId();
  //   Main flow
  @Bean
  public IntegrationFlow flow() {
    return flow ->
        flow.handle(
            (payload, header) -> {
              lionService.validateRequest((LionRequest) payload);
              return payload;
            })
            .split()
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .convert(LionRequest.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 integrationFlowDefinition ->
        integrationFlowDefinition
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .handle(
                (payload, header) -> {
                  try {
                    return lionService.saveRequest(
                        payload,
                        String.valueOf(dbId),
                        Integer.valueOf(
                            ((LionRequest) payload)
                                .getDetails()
                                .getId()),
                        ((SourceSystem) Objects.requireNonNull(header.get("sourceSystem")))
                            .getSourceSystemCode());
                  } catch (JsonProcessingException e) {
                    return e.getMessage();
                  }
                })
            .nullChannel();
  }
    
  //   flow2
  @Bean
  public IntegrationFlow flow2() {
    return integrationFlowDefination ->
        integrationFlowDefination
            .channel(c -> c.executor(Executors.newCachedThreadPool()))
            .handle(
                message ->
                    lionService.getData(
                        (LionRequest) message.getPayload(), SourceSystem.ONE))
            .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;
  }
} 

Gateway

  @Gateway(requestChannel = "flow.input")
  void processLionRequest(
      @Payload LionRequest lionRequest,
      @Header("sourceSystem") SourceSystem sourceSystem);
}

I'm casting the payload with LionRequest in few of the places but for other requests I want it to work. If I pass CatRequest and my flow is similar then how do I overcome the Object casting that I'm doing in few places.

1 Answers1

0

Such a casting must go away from your flow definitions. Move it (or remove) altogether into the service method calls level.

You may have in your service overloaded methods for particular types - the framework determines the target method to call automatically by the payload in the message.

For example if we take your validation part, it could look like this:

class MyValidationService {

    LionRequest validateRequest(LionRequest request) {
        
    }

    CatRequest validateRequest(CatRequest request) {
        
    }
}

Then in that flow you just do:

flow.handle(myValidationService).

The framework will wrap this into a ServiceActivatingHandler which is going to delegate to this or that method according the payload type you send to the input channel of this flow.

Something similar could be done for any other endpoints you use in your flows.

Probably even that .convert(LoanProvisionRequest.class) could be delegated to some POJO via transform() with similar overloaded method signatures. Or... those methods can have a different names - the framework doesn't care what is that: it just takes a payload type and tries to find a candidate in the methods of the POJO you provide for this endpoint.

There is some explanation in docs: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#service-activator-namespace

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • The way u suggested is really good. But if I want to consider for the other methods that are called inside the sub-flow e.g., saveRequest() method. Where we have to dig down to the specific POJO to get the field level value. What solution u would suggest in that case? – Somnath Mukherjee Jul 15 '22 at 15:31
  • In general: you must not do the hard logic in the flow definition. Better to delegate to some services or their method and do everything else (like your `Integer.valueOf()`) over there. Just think about it how would you make an API a generic way if there would not be Spring Integration... – Artem Bilan Jul 15 '22 at 15:35
  • Hi @Artem, you see flow1 i.e., dbFlow? I need to change the object to Json and want to access the customerID from that JSON. Could you please suggest me a proper way to do that? – Somnath Mukherjee Jul 18 '22 at 07:46
  • I don't see any JSON parsing in that lambda. Looks like you already have there that `LionRequest`. So, that exception handling is double. You can just implement some other intermediate service which would has a signature like `saveRequest(LionRequest request, @Header("sourceSystem") String sourceSystem)`. The rest of hard logic can be done inside that method. You can have an overloaded method for other requests as well. Your flow would look just like `handle(myService)` - and the framework will determine by the payload type which method to call. – Artem Bilan Jul 18 '22 at 17:21