2

I'm testing the behavior of the .channel() method and I've observed things that I don't understand.

@Bean
    public IntegrationFlow flow() {
        return IntegrationFlows.from("my-gateway")
                .channel("first-channel")
                .channel("second-channel")
                .get();
    }

If I place print statements in "first-channel", they aren't printed. But some of the business logic still appears to happen. Edit: Added code for service activators

@ServiceActivator(inputChannel = "first-channel")
   public Message testFlow(Message message) {
   System.out.println("Entered First Channel " + "\n" + "Message Header: " + message.getHeaders() + "\n" + "Message Payload" + "\n" + message.getPayload());
return message;

}

@ServiceActivator(inputChannel = "second-channel")
   public Message testFlow(Message message) {
   System.out.println("Entered Second Channel " + "\n" + "Message Header: " + message.getHeaders() + "\n" + "Message Payload" + "\n" + message.getPayload());
return message;

}

application.properties:

logging.level.root=TRACE

Am I allowed to pass a message through multiple channels in the same java dsl IntegrationFlow? Or are all IntegrationFlows restricted to one channel/ServiceActivator each?

Edit: Only the second print statement appears in the logs. Why is that?

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
Sid
  • 33
  • 5

1 Answers1

1

No, you definitely can build a flow with multiple channels and you even can have a configuration like that. A bridge() is placed in between then internally by the framework. Image you need to dump messages from direct call to some queue or vise versa. Or your message channel even can be based on some persistent storage for messages like JMS, AMQP etc.

The MessageChannel abstraction is a first-class citizen in Spring Integration and that comes from the canonical integration model described in the EIP: https://www.enterpriseintegrationpatterns.com/patterns/messaging/MessageChannel.html

The importance of such an implementation between endpoints comes handy when see how those endpoints are loosely-coupled and the target MessageChannel implementation may dictate us some behavior change in the middle of the flow.

Another aspect as an argument that it is valid to have a flow like you define is a ChannelInterceptor. You still may have definition with just channel names, but ChannelInterceptor can be applied to them globally according its pattern option.

The opposite is also true: you can declare a flow just only with endpoints and the framework places channel in between internally.

Please, see docs for more info: https://docs.spring.io/spring-integration/docs/5.2.3.RELEASE/reference/html/dsl.html#java-dsl

Artem Bilan
  • 113,505
  • 11
  • 91
  • 118
  • 3
    Bear in mind, though, that if you add another consumer on `first-channel` (in another flow - `IntegrationFlow.from("first-channel")...`), messages will alternately go to the bridge between the two channels and the "other" consumer. By default, round robin distribution between multiple consumers is used. – Gary Russell Feb 19 '20 at 19:04
  • Thank you, Artem. That documentation and analogy were extremely helpful. But one thing that still escapes me is that I have System.out statements in "first-channel" that don't seem to get executed. Even when I turn on the trace logs, I still don't see them. Is there something more that I'm missing? – Sid Feb 20 '20 at 16:33
  • Please, show how you do that. Your question just mentions that part, but no code snippet to analyze. Thanks – Artem Bilan Feb 20 '20 at 16:36
  • 1
    See Gary's comment. The `.channel("first-channel")` means a default `DirectChannel` with round-robin strategy. Since you place another channel in the flow, a `bridge()` is added as a subscriber to this channel. But then you have that `@ServiceActivator(inputChannel = "first-channel")` which becomes a second subscriber to the same channel. According round-robin behavior a first message goes to a first subscriber, the second to second and so on. So, you are confusing yourself adding that second subscriber with a `@ServiceActivator`. – Artem Bilan Feb 20 '20 at 20:11
  • 1
    The second channel is just the end of your flow and there is no other subscribers on it - only your `@ServiceActivator(inputChannel = "second-channel")` – Artem Bilan Feb 20 '20 at 20:12
  • I did indeed confuse myself. Thanks so much for the explanation. I think I follow it now. Thank you! – Sid Feb 20 '20 at 22:10
  • If you could spare the time. Would you be able to tell me if there is a standard or best practice for passing messages down channels? If I wanted to do a .channel("first-channel) .channel("second-channel) .channel("third-channel) And so on, is there a way to organize this? I might be phrasing my question poorly here. I just want to be able to pass a message down multiple/consecutive channels, what's the best way? – Sid Feb 21 '20 at 20:53
  • Please, be more specific in the separate SO thread. But keep in mind that have just channels in the flow make no sense: you definitely need to have some processing in between. – Artem Bilan Feb 21 '20 at 20:59
  • Is it not possible to do processing within channels? Perhaps I misunderstood the purpose of channels. – Sid Feb 21 '20 at 21:04
  • 1
    Correct. You are missing the part that channel per se is just a pipe in between endpoints. If your talk about those annotations like `@ServiceActivator`, then you don't need an `IntegrationFlow`. You build it already with those annotations. Or instead of annotations configure everything in the `IntegraionFlow`. – Artem Bilan Feb 21 '20 at 21:08