direndd
direndd

Reputation: 652

Sending a message from server to client via Websocket in Spring

My requirement is to send(broadcast) a message via web-socket once some change in back end occurs.

I followed this tutorial and utilized the answer in this question to come up with a solution that send a message by periodically calling a method.

My web socket controller class - GreetingController class is like this,

@Controller
public class GreetingController {

    @Autowired
    private SimpMessagingTemplate template;

    @MessageMapping("/hello")
    @SendTo("/topic/greetings")
    public Greeting greeting(HelloMessage message) throws Exception {
        FireGreeting r = new FireGreeting( this );
        new Thread(r).start();
        return new Greeting("Hello world !");
    }

    public void fireGreeting() {
        System.out.println("Fire");
        this.template.convertAndSend("/topic/greetings", new Greeting("Fire"));
    }
}

fireGreeting method is called in the thread class - FireGreeting

public class FireGreeting implements Runnable {

    private GreetingController listener;

    public FireGreeting( GreetingController listener) {
        this.listener = listener;
    }

    @Override
    public void run() {
        while (true) {
            try {
                Thread.sleep( 2000 );
                listener.fireGreeting();
            } catch ( InterruptedException e ) {
                e.printStackTrace();
            }
        }
    }
}

When i run this project i can send a message initially and then the thread starts periodically calling the fireGreeting() method.

What i wan't is to without calling the greeting(HelloMessage message) method, call the fireGreeting() method whenever my back-end operation is run.

I tried calling the method separately but it gives a null pointer exception because at that point SimpMessagingTemplate template; is not initialized.

My questions are as follows, 1.When does the template object get initialized? (So i can do the same thing and use it to call convertAndSend() method)

  1. Is it impossible to call the convertAndSend method like the way im trying to?

  2. Is there any other way i can achieve this requirement in spring and websocket?

I also tried this code in the documentation, but i don't have a clear understanding on how it works. It seems like, i can send a POST call and invoke the greet() method so it sends a message to server. But what i want is to invoke the method solely from the server side.

Any advice regarding this would be a great help. :)

Upvotes: 2

Views: 7839

Answers (1)

Vincent KERDRAON
Vincent KERDRAON

Reputation: 193

A bit late, but I had the question. Found the answer on How to send message to client through websocket using Spring

I can give another code example :

package xxx.web.websocket;

import xxx.web.websocket.dto.GameEventDTO;

import java.security.Principal;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.messaging.handler.annotation.*;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.stereotype.Controller;
import org.springframework.web.socket.messaging.SessionDisconnectEvent;

@Controller
public class GameEventService implements ApplicationListener<SessionDisconnectEvent> {

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

    public GameEventService() {
    }

    // this method is in all examples : receive from a client, send to all
    @MessageMapping("/topic/eventToServer")
    @SendTo("/topic/eventToClient")
    public GameEventDTO sendGameEvent(@Payload GameEventDTO dto, StompHeaderAccessor stompHeaderAccessor,
            Principal principal) {
        log.debug("Sending game event {}", dto);
        return dto;
    }

    @Override
    public void onApplicationEvent(SessionDisconnectEvent event) {
        log.debug("game event SessionDisconnectEvent");
    }

    @Autowired
    private SimpMessagingTemplate template;

    // the business logic can call this to update all connected clients
    public void sendGameEventFromJava(GameEventDTO dto) {
        log.debug("Sending game event from java {}", dto);
        this.template.convertAndSend("/topic/eventToClient", dto);
    }

}

And my business logic calling it :

@Service
@Transactional
public class GameService {

    private final Logger log = LoggerFactory.getLogger(GameService.class);

    private final GameRepository gameRepository;
    private final GameEventService gameEventService;

    public GameService(GameRepository gameRepository, GameEventService gameEventService) {
        this.gameRepository = gameRepository;
        this.gameEventService = gameEventService;
    }

    /**
     * Save a game.
     *
     * @param game the entity to save
     * @return the persisted entity
     */
    public Game save(Game game) {
        log.debug("Request to save Game : {}", game);
        Game result = gameRepository.save(game);
        GameEventDTO dto = new GameEventDTO();
        dto.setGameId(result.getId());
        this.gameEventService.sendGameEventFromJava(dto);
        return result;
    }

    ...

In your case, SimpMessagingTemplate template should be init. this is some project configuration issue. For example, make sure you have all the maven/graddle components. I add to add these in my pom.xml :

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
   <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-messaging</artifactId>
    </dependency>

Good luck to everyone with websockets.

Upvotes: 1

Related Questions