spring-projects / spring-integration

Spring Integration provides an extension of the Spring programming model to support the well-known Enterprise Integration Patterns (EIP)
http://projects.spring.io/spring-integration/
Apache License 2.0
1.54k stars 1.1k forks source link

Unable to set my customized ChannelResolver on Aggregator [INT-1882] #5870

Closed spring-operator closed 6 years ago

spring-operator commented 13 years ago

Kek opened INT-1882 and commented

There is no possibility to configure my customized ChannelResover on Aggregator. The CorrelatingMessageHandler uses the MessagingTemplate that is strictly defined as private final MessagingTemplate messagingTemplate = new MessagingTemplate(); and there is no support for setChannelResolver. So I could not configure the template nor the ChannelResover.

In my opinion there should be provided this feature as used on AbstractMessageRouter to provide consistent API.

Now I´m using the workaround based on Aggregator+Router encapsulated by Chain and the ChannelResover is set on Router. But it could be much simpler, when the ChannelResolver could be set directly on Aggregator.

Thank you for a great integration framework!


Affects: 2.0.3

spring-operator commented 13 years ago

Oleg Zhurakousky commented

I am not sure I understand why there is a need for ChannelResolver on the Aggregator. Are you trying to avoid the Router all together and trying to use ChannelResolver to determine where the output of the aggregator will go?

spring-operator commented 13 years ago

Kek commented

No, I use the Aggregator to aggregate reply messages. And the aggregated message has set the message.getHeaders().getReplyChannel(). The ReplyChannel is set as String-Channel-name so must be resolved. And as I look at sendReplies(...) method in CorrelatingMessageHandler, the aggregator could use the message.getHeaders().getReplyChannel() instead of explicitly configured OutputChannel. So I want to use this feature and route the messages from CorrelatingMessageHandler directly by message.getHeaders().getReplyChannel().

But in my solution I have my ChannelResover implementation instead of the default one. My implementation resolves/creates the JMS-Channels dynamically by channel name, because I need more flexibility than statically define channels in Spring-Context.

I use RecipientListRouter with configured my DynamicJmsChannelResolver and now I need to aggregate responses from recipients and send the aggregated message to proper ReplyChannel - that must be resolved with the DynamicJmsChannelResolver.

spring-operator commented 13 years ago

Oleg Zhurakousky commented

First, there is no issue with setting channel as a String. It will be resolved by BeanFactoryChannelResolver.

However, the real issue i see is that you are trying to use ChannelResolver for something it was not designed to do in the first place and that is ". . ./create ...Channels. . .". The intention for channel-resolver is to provide a level of indirection between channel identifier and the actual and existing channel.

Could you please explain your use case which prompts you to create channels dynamically?

spring-operator commented 13 years ago

Kek commented

In my opinion it does no matter if I create new or resolve existing channel. From my point of view, the problem is in consistency of components design. If I can set the resolver on Router, why not on Aggregator ? It is the same principle - I need to resolve the name to Channel as documented in javadoc "Strategy for resolving a name to a MessageChannel." So if I can use this strategy on router, why can not use it on Aggregator in the same way. If we omit my dynamic behaviour, I will have the same problem when my ChannelResolver will be implemented as ChannelName=prefix+Name and then delegate to BeanFactoryChannelResolver.resolveChannelName("ChannelName") - I can set it on Router, but can not on aggregator. How can I use my naming strategy on all components that use the resolver? It is not strategy pattern, when I have to use only the one implementation, provided by default in current implementation.

In my opinion there are following possibilities:

1) The aggregator should provide the SET for ChannelResolver, than the sendReplies(...) method on CorrelatingMessageHandler is OK. It is possible to route output message by ReplyMessageHeader if output channel is not set.

2) The aggregator should not provide the SET for ChannelResolver, than the the sendReplies(...) method should not be implemented on CorrelatingMessageHandler in current way. The ReplyMessageHeader should be always ignored, the OutputChannel must be mandatory and if someone wants to route by ReplyMessageHeader, than the Router must be chained with Aggregator to provide this facility.

3) The ChannelResolver could be deleted from API, because it is not about strategy, when I have to use only the default BeanFactoryChannelResolver.

But the current solution is something between these options.

And now,it is not easy do describe my usage scenario, but I will try it. We have large system that is composed from many subsystems. The subsystems must be integrated in dynamic way. The creation of new subsystems is not under central control. So we need to create some integration platform for these subsystems. I know that some kind of ESB should be used for this, but the ESB is too much complicated. So we decide to create own simple integration platform based on Spring integration. But as the subsystems are dynamically added, we need to provide dynamic routing between subsystems. The typical scenario is: 1) subsystem registers themselves for out MessageBus 2) the IN/OUT channels are created through API (like on Amazon SQS) 3) the the subsystem defines routing rules between its channels and channels of other subsystems ( routing rules are used by RecipientListRouter associated with OUT-Channel) 4) subsystem sends the message to OUT-Channel, RecipientListRouter replicates the messages by routing rules to other subsystems 5) the subsystems could answer to IN-Channel 6) the aggregator associated with IN-Channel should aggregate the reply messages.

The MessageBus is provided as a service on cluster of servers and managed by simple API. It is inspired by Amazon SQS but adds the routing. So we can not use the static ApplicationContext for definition of Channels - in our solution anybody could use simple remote API to manage its channels and routing rules. Sorry for my inaccuracy - I don´t create the channels directly in resolver. I have ChannelFactory that creates the channels based on metadata stored in database - managed by APIs used by subsystems. And my Channel resolver is used to determine the concrete Channel instance by naming-convention. When it is not the "dynamic" channel, the call is delegated to standard Spring ChannelResolver to resolve the "Static" Channels from Context. "Dynamic" channels are registered only in Hashmap associated with my DynamicChannelResolver, not in Context. My factory is inspired by original JmsChannelFactoryBean, but this is other story.

This is my motivation.

If you think that this issue is irrelevant, feel free to close it. I can use the workaround based on Aggregator+Router+Chain.

If you will have more questions, please be patient, I will be away for two weeks.

spring-operator commented 13 years ago

Oleg Zhurakousky commented

From my point of view, the problem is in consistency of components design. If I can set the resolver on Router, why not on Aggregator There is a huge difference. Router's responsibility is to discover what channel to send a Message too and therefore it is Router's responsibility to resolve channels with BFCR being default.

Aggregator on the other hand deals with Messages just like other endpoints. Its not the responsibility of the Aggregator to resolve channels

spring-operator commented 13 years ago

Kek commented

Yes, it is not responsibility of Aggregator to resolve channels, its responsibility of ChannelResolver. For me the ChannelResolver is responsible to translate Channel-identifier to Channel. And when I have my own strategy how to translate Channel-identifier to Channel, than I need to use my customized ChannelResolver.And all endpoints should deal with messages with the consistent way - so they should have pluggable ChannelResolver, when there is some need to translate Channel-identifier to channel instance. Or more common solution - it should be possible to plug the configured MessagingTemplate, there I can configure the ChannelResolver in a standard way. But now it is not possible, there is

private final MessagingTemplate messagingTemplate = new MessagingTemplate();

on many places, so nobody can use DI configuration, to inject some specific MessagingTemplate.

And yes, the "Router's responsibility is to discover what channel to send..." it is OK, the result of discovery process could be Channel, Channel-identifier, etc. and than the ChannelResolver is used to translate the Channel-identifier to Channel-instance, it is consistent.

Responsibility of Aggregator is aggregate messages and than uses the ChannelResolver to translate Reply-Channel-identifier to Output-Channel. It is not about discovery of output channel like in Router, it is only about resolving the Channel-identifier. So it is consistent.

I don´t need to discover output channel, I only need to translate the my Channel-identifier to Channel-instance - so this is the responsibility of ChannelResolver that is used by MessagingTemplate in all places in the same way. But some endpoints allow to setup ChannelResolver (like AbstractMessageRouter), but some endpoints (like CorrelatingMessageHandler) avoid this, and my issue is, why ? I want to have possibility to use the same strategy, how to resolve Channel-identifier to Channel-instance on all places, where the Channel-identifier could be used to reference the channel, nothing more, nothing less. I don't want to put any routing logic/rules to ChannelResolver.

spring-operator commented 13 years ago

Oleg Zhurakousky commented

Let me try it once again

Spring Integration follows "Pipes and Filters" architectural style - http://www.eaipatterns.com/PipesAndFilters.html. Filter - ". . . it receives messages on the inbound pipe, processes the message, and publishes the results to the outbound pipe"

By definition Router breaks the "Pipes and Filter" paradigm by the fact that it connects to multiple channels (pipes) - http://www.eaipatterns.com/MessageRouter.html. Further more to be useful router must deal with routing rules that might come in as part of the Message. Such rule could be a channel name in the headers or channel instance in the headers. If instance then the Message can just simply be sent to that channel otherwise the instance of the existing channel must be found through a resolution process.

In your suggestion where the resolution of the output-channel (pipe) is based on some dynamic resolution process, would give every SI component a routing capabilities, thus mixing concerns and responsibilities of individual patterns. responsibility.

So IMHO our behavior is very consistent where all components with the exception fo the Routers follow "Pipes and Filters" idiom.

spring-operator commented 13 years ago

Kek commented

Please read my last comment again, there is "I want to have possibility to use the same strategy, how to resolve Channel-identifier to Channel-instance on all places, where the Channel-identifier could be used to reference the channel, nothing more, nothing less. I don't want to put any routing logic/rules to ChannelResolver." Once again "I don't want to put any routing logic/rules to ChannelResolver." Only translate Channel-name to Instance like you, but in my way - it is not about routing, it is about my realization of "ChannelRegistry" - simply I don´t use the static ApplicationContext, so I need to use my ChannelResolver to obtain the reference to Channel by its name - this is not about routing, it is about dynamic registration and unregistration of some channels from this registry.

I already has simple solution and don´t need the setChannelResolver method anymore. It is not nice, but it is working :). If I inject the ChannelResolver to my MessageGroupProcessor, I can resolve the name to Channel there and than set the channel instance on aggregated Message as replyChannel header, than your CorrelatingMessageHandler will use my Channel instance - this is the my last workaround solution.

But OK, if you want, you can close this issue, I know, it is very special usage of SI. But still I think you are not consistent.

If the use of ChannelResolver is valid only for Router, than I don´t understand, why you use it on other places too? Is there some sample for what purpose should be the ChannelResolver strategy used?

And why you break the idiom "Pipes and Filters" in your aggregator? As I already noted: " in CorrelatingMessageHandler, the aggregator could use the message.getHeaders().getReplyChannel() instead of explicitly configured OutputChannel." In my opinion, this is routing too. So if you want to strictly follow "Pipes and Filters" architectural style, than in my opinion only outputchannel is correct usage, there should not be routing by replyChannel-header in aggregator. Or this is not routing ?

But once again, I don´t need routing, I only need to use ChannelResolver strategy to translate the name to instace - resolve the channel from my "ChannelRegistry" , nothing more, nothing less.

spring-operator commented 6 years ago

Artem Bilan commented

Has been fixed via #7565.

Now all the IntegrationObjectSupport implementations can be supplied with any custom DestinationResolver. At the same time they use BeanFactoryChannelResolver by default.