0

Here I'm using Scatter-gather pattern using spring integration and calling 3 sub-flows parallelly. this same parallel process needs to be run for other requirements because the whole process is similar for the other few requirements it's just that for flow1 and flow2 I need I need to pass different parameter for different requirements. Here in flow1 I'm passing SourceSystem.ONEto the save method. So SourceSystem is an enum which is requirement specific. For different requirement that value is only needs to be changed, i.e., SourceSystem.TWO, SourceSystem.THREE etc.

I tried passing this as header to the gateway but not sure how do I use it to pass to the flow1 and flow 2 method.

//codebase

@Configuration
    public class SpringIntegrationConfiguration {
      @Autowired LoansServiceImpl loansService;
    
      @Value("${someservice.url}")
      private String someURL;
    
      long dbId = new SequenceGenerator().nextId();
      //   Main flow
      @Bean
      public IntegrationFlow flow() {
        return flow ->
            flow.split()
                .log()
                .channel(c -> c.executor(Executors.newCachedThreadPool()))
                .convert(LionRequest.class)
                .scatterGather(
                    scatterer ->
                        scatterer
                            .applySequence(true)
                            .recipientFlow(flow1())
                            .recipientFlow(flow2())
                            .recipientFlow(flow3()),
                    gatherer ->
                        gatherer
                            .releaseLockBeforeSend(true)
                            .releaseStrategy(group -> group.size() == 2))
                .aggregate(a -> a.outputProcessor(MessageGroup::getMessages))
                .channel("output-flow");
      }
    
      @Bean
      public IntegrationFlow flow1() {
        return integrationFlowDefinition ->
            integrationFlowDefinition
                .channel(c -> c.executor(Executors.newCachedThreadPool()))
                .transform(
                    message -> {
                      try {
                        return lionService.saveRequest(
                            (LionRequest) message,
                            String.valueOf(dbId),
                            Integer.valueOf(
                                ((LoanRequest) message)
                                    .getLionDetails()
                                    .getId()),
                            SourceSystem.ONE.getSourceSystemCode());
                      } catch (JsonProcessingException e) {
                        return e.getMessage();
                      }
                    })
                .nullChannel();
      }
    
    
      @Bean
      public IntegrationFlow flow2() {
        return integrationFlowDefinition ->
            integrationFlowDefinition
                .channel(c -> c.executor(Executors.newCachedThreadPool()))
                .transform(
                    message ->
                        loansService.prepareSomething(
                            (LionRequest) message));
      }
    
      // Calling data sourcing service
      @Bean
      public IntegrationFlow flow3() {
        return integrationFlowDefinition ->
            integrationFlowDefinition
                .channel(c -> c.executor(Executors.newCachedThreadPool()))
                .transform(
                    payload ->
                        lionService.prepareRequest(
                            (LionRequest) payload, SourceSystem.ONE))
                .handle(
                    Http.outboundGateway(someURL)
                        .httpMethod(HttpMethod.POST)
                        .expectedResponseType(String.class));
      }
    
    
      @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;
      }
    }

I tried to pass it onto the header but not sure how to pass it onto the methods. Because in transformer I'm not able to access the header only the payload is available it seems. And I can't use handle() method because it's throwing error as - "this component is a one-way 'MessageHandler' and it isn't appropriate to configure 'outputChannel'".

 //Gateway
      @Gateway(requestChannel = "flow.input", replyChannel = "output-flow")
      List<Object> triggerParallelTasks(
          @Payload LionRequest lionRequest,
          @Header("sourceSystem") SourceSystem sourceSystem);

I'm trying to invoke the gateway like this -

    gateway(lionrequest, SourceSystem.ONE);
    gateway(lionrequest, SourceSystem.TWO);
    gateway(lionrequest, SourceSystem.THREE);

If I should follow any other approach that also kindly suggest.

1 Answers1

0

What you are doing with the gateway and @Header("sourceSystem"). That's the best way to pass some additional data together with the payload.

The transform() has not only lambda variant, but some other:

/**
 * Populate the {@link MessageTransformingHandler} instance for the provided
 * {@link GenericTransformer} for the specific {@code expectedType} to convert at
 * runtime.
 * @param expectedType the {@link Class} for expected payload type. It can also be
 * {@code Message.class} if you wish to access the entire message in the transformer.
 * Conversion to this type will be attempted, if necessary.
 * @param genericTransformer the {@link GenericTransformer} to populate.
 * @param <P> the payload type - 'transform from' or {@code Message.class}.
 * @param <T> the target type - 'transform to'.
 * @return the current {@link BaseIntegrationFlowDefinition}.
 * @see MethodInvokingTransformer
 * @see LambdaMessageProcessor
 */
public <P, T> B transform(Class<P> expectedType, GenericTransformer<P, T> genericTransformer) {

Pay attention to the Message.class. This way you can call a getPayload() to get access to your LionRequest. And getHeaders() to reach your sourceSystem header.

It is also not clear why you cannot use this handle():

/**
 * Populate a {@link ServiceActivatingHandler} for the
 * {@link org.springframework.integration.handler.MethodInvokingMessageProcessor}
 * to invoke the provided {@link GenericHandler} at runtime.
 * Typically used with a Java 8 Lambda expression:
 * <pre class="code">
 * {@code
 *  .<Integer>handle((p, h) -> p / 2)
 * }
 * </pre>
 * Use {@link #handle(Class, GenericHandler)} if you need to access the entire
 * message.
 * @param handler the handler to invoke.
 * @param <P> the payload type to expect.
 * @return the current {@link IntegrationFlowDefinition}.
 * @see org.springframework.integration.handler.LambdaMessageProcessor
 */
public <P> B handle(GenericHandler<P> handler) {

Which simply let you to deal with your payload and headers directly from the contract. And this one has been designed exactly for request-reply scenarios to avoid that mentioned this component is a one-way 'MessageHandler'.

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • Hi Artem, I removed the reply channel from the mail flow now I can use handle() in the flow1. But if I have another flow which involves transforming the payload by calling a native method (which again accepts the sourceSystem) then passing the transformed payload to the handle() method where I want to have a http call then how do I do that? If I'm taking two handle() methods one after another then same error is coming -this component is a one-way 'MessageHandler' and it isn't appropriate to configure 'outputChannel'. – Somnath Mukherjee Jul 08 '22 at 23:17
  • Let me paste the flow in the answer section for ur reference. – Somnath Mukherjee Jul 08 '22 at 23:19
  • Please, read my answer carefully. The `handle((p, h) -> )` is your friend in this use-case. The `handle(m ->)` is really one-way. The first one extract a `payload` and `headers` to lambda arguments and waits for a reply. The second one deals with the whole message and it’s return is `void`. – Artem Bilan Jul 09 '22 at 02:05