Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send Message to all clients via SimpMessagingTemplate in ServletContextListener

I'm using the Spring framework and I have a working websocket controller that looks like this:

@Controller
public class GreetingController {

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws InterruptedException {
        return new Greeting("Hello, " + message.getName() + "!");
    }
}

I also have this configuration:

@Configuration
@EnableWebSocketMessageBroker
public class HelloWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

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

    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/hello").withSockJS();
    }
}

That part works great! I can successfully send and receive messages between two or more browsers using Stomp.js. Here's the part that doesn't work. I have implemented a ServletContextListener that contains a custom object that, for simplicity sake, I have called 'notifier'. The notifier listens for certain events to happen on the server-side. It then calls the 'notify' method, which is supposed to send details about the event to all clients. It doesn't work, though.

@WebListener
public class MessageListener implements ServletContextListener, Notifiable {

    private Notifier notifier;

    @Autowired
    private SimpMessagingTemplate messageSender;


    public MessageListener() {
        notifier = new Notifier(this);
    }

    public void contextInitialized(ServletContextEvent contextEvent) {
        WebApplicationContextUtils
        .getRequiredWebApplicationContext(contextEvent.getServletContext())
        .getAutowireCapableBeanFactory()
        .autowireBean(this);

        notifier.start();
    }

    public void contextDestroyed(ServletContextEvent contextEvent) {
        notifier.stop();
    }

    public void notify(NotifyEvent event) {
        messageSender.convertAndSend("/topic/greetings", new Greeting("Hello, " + event.subject + "!"));
    }
}

I do not get an exception. The SimpMessagingTemplate has been successfully injected by Spring, so it's not null. I have been able to step into the Spring code and have figured out that the SimpleBrokerMessageHandler's subscriptionRegistry is empty when using the SimpMessagingTemplate. So it must be a separate instance from the one that the controllers are using. How can I get the same subscriptionRegistry that is used by the controllers?

like image 587
battmanz Avatar asked Aug 29 '14 04:08

battmanz


2 Answers

The solution was to use Spring's ApplicationListener class instead of a ServletContextListener, and to specifically listen for the ContextRefreshedEvent.

This is my working example:

@Component
public class MessagingApplicationListener implements ApplicationListener<ContextRefreshedEvent>, Notifiable {
    private final NotifierFactor notifierFactory;
    private final MessageSendingOperations<String> messagingTemplate;
    private Notifier notifier;

    @Autowired
    public MessagingApplicationListener(NotifierFactor notifierFactory, MessageSendingOperations<String> messagingTemplate) {
        this.notifierFactory = notifierFactory;
        this.messagingTemplate = messagingTemplate;
    }

    @Override
    public void onApplicationEvent(ContextRefreshedEvent event) {
        if (notifier == null) {
            notifier = notifierFactory.create(this);
            notifier.start();
        }
    }

    public void notify(NotifyEvent event) {
        messagingTemplate.convertAndSend("/topic/greetings", new Greeting("Hello, " + event.subject + "!"));
    }

    @PreDestroy
    private void stopNotifier() {
        if (notifier != null) {
            notifier.stop();
        }
    }
}
like image 84
battmanz Avatar answered Oct 17 '22 22:10

battmanz


This solution worked great.

For anybody who wants a quick solution - strip out all the "Notifier" stuff and just inject MessagingApplicationListener into your class. Then call 'notify' with a String to send a message. Obviously, as @battmanz says you'll want some way to push events to the listener, but this class does the basics of what you need.

like image 32
obrienk Avatar answered Oct 17 '22 21:10

obrienk