The problem
<websocket:message-broker>
effectively supports only one instance per application, which is a painful limitation in some projects. It would be much better if you could encapsulate different business concerns into separate endpoints with different sets of prefixes and other rules.
I debugged into the issue, trying to find out what could be done and realized that there was not much at the moment.
Specifically, org.springframework.web.socket.config.MessageBrokerBeanDefinitionParser
has constants for the names for all beans it is registering, including an instance of SimpMessagingTemplate
under the name "brokerMessagingTemplate"
. This is even mentioned in the official documentation:
The bean name is
"brokerMessagingTemplate"
if required for qualification with@Qualifier.
"
This design has its benefits because obviously MessageBrokerBeanDefinitionParser
registers quite a couple of beans with some complex wiring. Hiding this complexity is a good thing in general. However, if you look more closely you must admit that this approach totally defeats the idea of IoC because there's nothing you can configure and tweak anymore. Everything is hard-wired, just like direct construction.
I'm sure there would be a better solution.
My current suggestion/vision
<websocket:message-broker>
gets an optional argument named something like "broker-messaging-template" by which one can reference any own bean that is an instance ofSimpMessagingTemplate
to be used for this message broker.- There's another (yet to be implemented) bean definition parser that is able to create these beans, something like
<websocket:simp-message-template id="abc">
. If no other arguments are given, this creates the default wiring under the name/qualifier "abc". However you should be able to adjust everything (converters, channels, scheduler) if needed.
Comment From: rstoyanchev
It is true that the XML config is not very extensible + fairly extensive, but the components used in it (channels and message handlers) are meant to be possible to assemble independently without that config. Also the Java config is much more extensible than the XML config. I have not tried it but I can imagine it would be possible to re-use WebSocketMessageBrokerConfigurationSupport
and create more than one beans like the stompBrokerRelayMessageHandler
bean, each connected to the same channels but configured with different destination prefixes.
I'm also wondering if it wouldn't be possible to push this further out to the broker and for example sub-divide the different topic destinations within the same broker to keep it simpler.
In any case I think built-in support for this would involve a fair amount to work to ensure the new feature works well with all other features of the STOMP/WebSocket support.
Comment From: spring-projects-issues
If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.
Comment From: TLN2
In #22525 you concluded that it was a different suggestion or request from this one. I don't agree with this conclusion. However, I admit I'm not so familar with the topic anymore and I also can't know with absolute certainty what #22525 was about.
How I understand the problem and why I think it's basically the same in #22525 and this ticket:
As a Spring user I want to have the option of instantiating and running an arbitrary number of completely independently operating WebSocket/STOMP subsystems in one application. They should be completely independent, even though they're running in the same Spring Application Context.
According to my memory there are four relevant interfaces: - Endpoints through which to connect from outside the app server. - Message broker (e.g. RabbitMQ) to dispatch messages around. - Java API for subscribing to messages and submitting own messages. - Configuration.
This is how I judge the current degree of encapsulation, again, only based on what I remember, which may be wrong or incomplete: - Endpoints: They seem to provide no encapsulation. If you are connected to one endpoint, you can subscribe to any topic, even if it's meant to be for another endpoint only. - Message brokers: It doesn't seem to be possible to use several message brokers. - Java API: Annotations don't allow any distinction from which subsystem to receive/subscribe to messages, mainly because the idea of a subsystem doesn't exist in the current design. Likewise, submitting messages through SimpMessagingTemplate gives no choice which subsystem to use. There can be only one instance of SimpMessagingTemplate. - Configuration: You say Java-based configuration might help. Please consider giving examples in the documentation. The configuration options I found so far didn't give me the impression there was any solution for configuring several independent subsystems.
In #22525 you interpreted my request from this ticket like that I want to use several message brokers which I want to distinguish by several destination prefixes. While this is a use case that would be nice to solve as a side effect, this is NOT the main scenario I had in mind when filing the ticket.
Rather, this is a scenario I have in mind: (1) Assume your project needs to integrate with two existing message brokers, for instance two RabbitMQ instances that have nothing to do with each other. They're already running, they have a lot of stuff going on already, you can't change the way it is, and both of them use the "/topic" prefix, as an example. To write your application to cleanly interact with them, you need completely separate subsystems, otherwise things get messed up or won't work at all. This doesn't seem to be supported so far.
(2) Another scenario that I really missed was this, as described in https://github.com/spring-projects/spring-framework/issues/22525#issuecomment-470917791: If your application wants to expose two endpoints /public and /internal, where you configure the reverse proxy to allow access to /public from the Internet while you allow access to /internal only from the internal network, this doesn't help you much. If a user is connected to /public, he can subscribe to any topics, even those that are meant to be available only on /internal endpoint. Effectively there is no way of having several access levels in your WebSocket/STOMP infrastructure.
(3) In a nutshell, the current design is so that it's none or all: Either a part of the software or an external subscriber doesn't have access to STOMP at all, or he can access everything with no exception, can subscribe to any topic, and so on.
You suggested to configure the distribution of messages in RabbitMQ instead of trying to deal with it in Spring. This is indeed possible in some scenarios, but it can't solve the two use cases I just described ((1), (2), and (3)).
Also, another problem is (4) if you use nested application contexts, each of which configures STOMP. This is totally unpredictable and problably will never do what you expect.
On a more generic note: The current design handles WebSockets/STOMP like a singleton: There's at most one instance in the system, and all users have to share it.
I have problems seeing a compelling reason why the same thing (code base) can't be made so that you can create as many independent instances as you want.
Last not least I want to stress that according to what I remember even completely independent subsystems would only solve half of the problems I see with the current architecture design. Another part of the problem comes from the current Spring code hard-wiring everything when creating brokerMessagingTemplate
. As a Spring user you have no influence on how the beans are plugged into each other, and you can't inject your own beans if you want to.
The reason why you may have thought #22525 and this ticket (#22535) may be different may be that the decision to currently allow only one WebSocket/STOMP subsystem per application causes a multitude of different problems and limitations. Both ticket approached them from different ends, but eventually they boil down to the same concern: Is it possible to run any number of independent WebSocket/STOMP subsystems in one application?
I understand, even if you wanted to do it, it would be difficult for you. All I can say is: We appreciate all your efforts and respect your decisions not to invest into particular features, but if something is difficult for you to solve, it's usually even more difficult for Spring users to solve or work around. Hence, the actual key question is how frequently a feature is needed in real life and how many users would benefit. I hope I was able to explain that at least it's not just a corner requirement but has several reasons why and scenarios where would be useful.
Thanks.