Latest blog

  • Spring SimpMessagingTemplate Example

    Want to send notifications to your WebSocket clients? What if you want to send messages to connected clients so they can update their UI? You definitely can send messages from the back-end (e.g. from any part of your application). Any application component can send messages to the message broker. The easiest way to do so is to inject a SimpMessagingTemplate and use it to send messages. Typically, you would inject it by type. If another bean of the same type exists, qualify it by name (@Qualifier).

    My example is taken from my simple monitoring app, Crowsnest (Crowsnest repository in GitHub). Here’s the code where I use SimpMessagingTemplate. This web app just checks whether sites are online or offline.

    package net.codesamples.crowsnest;
    
    ... imports snipped ...
    
    @Component
    public class ScheduledPings {
        private static final Logger log = LoggerFactory.getLogger(ScheduledPings.class);
    
        private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    
        @Autowired
        EnvironmentConfigurationWatcher environmentConfigurationWatcher;
    
        @Autowired
        SimpMessagingTemplate simpMessagingTemplate;
    
        private WebClient webClient = WebClient.create();
    
        private List<Environment> internalEnvironmentList = new ArrayList<>();
    
        private ObjectMapper mapper = new ObjectMapper();
    
        @Scheduled(cron = "${cron.expression}")
        public void pinger() {
            log.info("The time is now {}", dateFormat.format(new Date()));
            List<Environment> environmentList = environmentConfigurationWatcher.getEnvironmentList();
            log.info("Environment list: {}", environmentList);
    
            for (Environment environment : environmentList) {
                for (App app : environment.getApps()) {
                    Mono<String> mono = webClient.get()
                            .uri(app.getUrl())
                            .exchangeToMono(clientResponse -> {
                                HttpStatusCode statusCode = clientResponse.statusCode();
                                if (statusCode.is2xxSuccessful()) {
                                    app.setStatus("up");
                                }
                                log.info("StatusCode: {} = {}", statusCode, app.getUrl());
                                return clientResponse.bodyToMono(String.class);
                            })
                            .onErrorResume(Exception.class, exception -> {
                                log.info("Exception {}", exception.getMessage());
                                app.setStatus("down");
                                return Mono.empty();
                            });
                    mono.subscribe();
                }
            }
    
            if (internalEnvironmentList.isEmpty()) {
                internalEnvironmentList = deepCopy(environmentList);
                simpMessagingTemplate.convertAndSend("/topic/environments", internalEnvironmentList);
            } else {
                if (!areTheSame(environmentList, internalEnvironmentList)) {
                    log.info("environmentList NOT equal to internalEnvironmentList: \n {} \n {}",
                            environmentList, internalEnvironmentList);
                    internalEnvironmentList = deepCopy(environmentList);
                    simpMessagingTemplate.convertAndSend("/topic/environments", internalEnvironmentList);
                } else {
                    log.info("environmentList equal to internalEnvironmentList");
                }
            }
        }
    
        private List<Environment> deepCopy(List<Environment> src) {
    	    ... snipped ...
        }
    
        private boolean areTheSame(List<Environment> list1, List<Environment> list2) {
            ... snipped ...
        }
    }

    Straight to the point and no lollygagging, on lines 15, 52, and 58 are where the magic happens. So we let Spring inject the SimpMessagingTemplate, then send the message to the target destination. In this example, the destination is “/topic/environments”. This is mapped to a method in a controller class. And that is how you update your connected clients via WebSocket.

    In this example, on initial load where the environment list is still empty, we update all connected clients. The clients are updated as well when there is a change in the environment list. Here easily validated by checking if the old list matches the new list.

    For setting up WebSocket on your Spring Boot app, front-end and back-end basics, I’ll point you to my previous blog, Spring Boot WebSocket Example

    Demonstration

    I’m using IntelliJ IDEA 2023.3.4 (Community Edition). You can use any IDE you like. You can even go command line! Start up the Crowsnest web app. Once it has started, it should be accessible on http://localhost:8080 and you should have something like below when connected. Go ahead, click the connect button. Pay attention to the web console logs. Notice the JSON environments list.

    Spring SimpMessagingTemplate Example – Connected

    Now, let’s edit the environments.json file to force the back-end to notify the clients. For this, I’m just going to add “New” to the title. From “Development” to “New Development”. On the back-end logs. The old environment list will not be equal to the new environment list which will trigger a publish message to the topic. You should see something like this on the logs.

    Spring SimpMessagingTemplate Example – New Title Log

    Alright, we got a new title. Let’s head back to the browser. It should now render the new title and you’ll also see it log the new environment list. Like so.

    Spring SimplMessagingTemplate Example – Title Updated

    Spring SimpMessagingTemplate

    Now you don’t need to poll the back-end incessantly. All you have to do is connect via WebSocket and wait for any notification. That should relieve the front-end of keeping up to date. Thank you for reading and happy WebSocket-ing.

    WebSocket to me…