Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I send a message on connect event (SockJS, STOMP, Spring)?

I am connection through SockJS over STOMP to my Spring backend. Everything work fine, the configuration works well for all browsers etc. However, I cannot find a way to send an initial message. The scenario would be as follows:

  1. The client connects to the topic

    function connect() {
        var socket = new SockJS('http://localhost:8080/myEndpoint');
        stompClient = Stomp.over(socket);
        stompClient.connect({}, function(frame) {
            setConnected(true);
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/notify', function(message){
                showMessage(JSON.parse(message.body).content);
            });
        });
    }

and the backend config looks more or less like this:


    @Configuration
    @EnableWebSocketMessageBroker
    public class WebSocketAppConfig extends AbstractWebSocketMessageBrokerConfigurer {   
    ...
    @Override
    public void registerStompEndpoints(final StompEndpointRegistry registry) {
        registry.addEndpoint("/myEndpoint").withSockJS();
    }

  1. I want to send to the client an automatic reply from the backend (on the connection event) so that I can already provide him with some dataset (e.g. read sth from the db) without the need for him (the client) to send a GET request (or any other). So to sum up, I just want to send him a message on the topic with the SimMessagingTemplate object just after he connected.

Usually I do it the following way, e.g. in a REST controller, when the template is already autowired:


    @Autowired
    private SimpMessagingTemplate template;
    ...
    template.convertAndSend(TOPIC, new Message("it works!"));

How to achieve this on connect event?

UPDATE

I have managed to make it work. However, I am still a bit confused with the configuration. I will show here 2 configurations how the initial message can be sent:

1) First solution

JS part

stompClient.subscribe('/app/pending', function(message){
    showMessage(JSON.parse(message.body).content);
});
stompClient.subscribe('/topic/incoming', function(message){
    showMessage(JSON.parse(message.body).content);
});

Java part

@Controller
public class WebSocketBusController {
    @SubscribeMapping("/pending")

Configuration

@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    config.setApplicationDestinationPrefixes("/app");
}

... and other calls

template.convertAndSend("/topic/incoming", outgoingMessage);

2) Second solution

JS part

stompClient.subscribe('/topic/incoming', function(message){
    showMessage(JSON.parse(message.body).content);
})

Java part

@Controller
public class WebSocketBusController {
    @SubscribeMapping("/topic/incoming")

Configuration

@Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
    config.enableSimpleBroker("/topic");
    // NO APPLICATION PREFIX HERE
}

... and other calls

template.convertAndSend("/topic/incoming", outgoingMessage);

SUMMARY:

The first case uses two subscriptions - this I wanted to avoid and thought this can be managed with one only.

The second one however has no prefix for application. But at least I can have a single subscription to listen on the provided topic as well as send initial message.

like image 282
Adam Soliński Avatar asked Jul 15 '14 10:07

Adam Soliński


2 Answers

If you just want to send a message to the client upon connection, use an appropriate ApplicationListener:

@Component
public class StompConnectedEvent implements ApplicationListener<SessionConnectedEvent> {

    private static final Logger log = Logger.getLogger(StompConnectedEvent.class);

    @Autowired
    private Controller controller;

    @Override
    public void onApplicationEvent(SessionConnectedEvent event) {
        log.debug("Client connected.");
        // you can use a controller to send your msg here
    }
}
like image 64
mrUwa Avatar answered Oct 31 '22 23:10

mrUwa


You can't do that on connect, however the @SubscribeMapping does the stuff in that case.

You just need to mark the service method with that annotation and it returns a result to the subscribe function.

From Spring Reference Manual:

An @SubscribeMapping annotation can also be used to map subscription requests to @Controller methods. It is supported on the method level, but can also be combined with a type level @MessageMapping annotation that expresses shared mappings across all message handling methods within the same controller.

By default the return value from an @SubscribeMapping method is sent as a message directly back to the connected client and does not pass through the broker. This is useful for implementing request-reply message interactions; for example, to fetch application data when the application UI is being initialized. Or alternatively an @SubscribeMapping method can be annotated with @SendTo in which case the resulting message is sent to the "brokerChannel" using the specified target destination.

UPDATE

Referring to this example: https://github.com/revelfire/spring4Test how would that be possible to send anything when the line 24 of the index.html is invoked: stompClient.subscribe('/user/queue/socket/responses' ... from the spring controllers?

Well, look like this:

@SubscribeMapping("/queue/socket/responses")
public List<Employee> list() {
     return getEmployees();
}

The Stomp client part remains the same.

like image 27
Artem Bilan Avatar answered Oct 31 '22 23:10

Artem Bilan