Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I am injecting the SimpMessagingTemplate into some of my @Component classes in my Springboot app, but simpMessagingTemplate.convertAndSend(dest, payload) only sends from the @Controller class where the websocket endpoints are handled.

Controller

@MessageMapping("/gpio_ws")
    @SendTo("/topic/gpio")
    public GPIOMessage sendGPIOMessage(GPIOMessage message) {
        log.info("Sending WS message: " + message);
        simpMessagingTemplate.convertAndSend("/topic/gpio", message);

I have some Javascript in the frontend with Stomp and SockJS which connects and sends messages through this endpoint without a problem.

Example use in another class

// constructor injected, it won't allow field injection here
private final SimpMessagingTemplate template;
@Autowired
public PiGPIO(SimpMessagingTemplate template) {
        this.template = template;
// in a method
Arrays.stream(piFace.getInputPins())
                    .forEach(inputPin -> inputPin.addListener(new GpioPinListenerDigital() {
                        @Override
                        public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent 
                                                                         event) {
                            log.info("PiFace input had an event - PIN: " + event.getPin().getName()
                                    + " STATE: " + event.getState().getName());
                            template.convertAndSend("/topic/gpio", new 
                                                   GPIOMessage(event.getPin().getName(),
                                    event.getState().getName().equals("LOW INPUT") ? 1 : 0));

I did have this working the other day and I'm now at a complete loss. I checked the bean by clicking the little icon in IntelliJ and all instances of SimpMessagingTemplate seem to be pointing to the same bean.

Can SimpMessagingTemplate work outside the class where the endpoint is declared? Why not?

Edit: Here is the websocket config

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
        registry.setApplicationDestinationPrefixes("/app");
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/gpio_ws");
        registry.addEndpoint("/gpio_ws").withSockJS();
        registry.addEndpoint("/track"); // for another SockJS, which works
        registry.addEndpoint("/track").withSockJS();

A snippet of the Javascript. During putting together the websocket code, I used JS to receive and send, but for this particular problem I just need it to receive. Although I have left the send/receive functionality at the moment so I can test the WS is still working.

function connect() {
    const sock_track = new SockJS('/track');
    stomp_track = Stomp.over(sock_track);
    stomp_track.connect({}, function (frame) {
        console.log('stomp_track connected: ' + frame)
    const sock_gpio = new SockJS('/gpio_ws');
    stomp_gpio = Stomp.over(sock_gpio);
    stomp_gpio.connect({}, function(frame) {
        console.log('stomp_gpio connected: ' + frame);
        stomp_gpio.subscribe('/topic/gpio', function(messageOutput) {
            parseMessage(JSON.parse(messageOutput.body));
            console.log(messageOutput.body)
function sendMessage() {
    const pin = document.getElementById('pin').value;
    const state = document.getElementById('state').value;
    console.log("state: " + state + " pin: " + pin)
    stomp_gpio.send("/app/gpio_ws", {},
        JSON.stringify({'pin':pin, 'state':state}));
function trackOn() {
    stomp_track.send("/app/track", {}, true)
function trackOff() {
    stomp_track.send("/app/track", {}, false)

Edit: The class where SimpMessagingTemplate template.convertAndSend() previously worked, before I had to refactor as I had a circular dependency (I injected PiGPIO here, and GPIOService into PiGPIO). So previously, GPIOService was injected to PiGPIO and within PiGPIO the method sendEvent() was called and this successfully sent a message to the JS client.

@Service
public class GPIOService {
    private static final Logger log = LoggerFactory.getLogger(GPIOService.class);
    private final SimpMessagingTemplate template;
    private final PiGPIO piGPIO;
    @Autowired
    public GPIOService(SimpMessagingTemplate template, PiGPIO piGPIO) {
        this.template = template;
        this.piGPIO = piGPIO;
    /** Sends a message to the websocket on /gpio endpoint.
     * @param message is a model of the PIN state, 1 being high, 0 low */
    public void sendEvent(GPIOMessage message) {
        log.info("GPIOMessage sent to /topic/gpio");
        this.template.convertAndSend("/topic/gpio", message);
    public void trackPower(boolean isPowered) {
        if (isPowered) {
            piGPIO.turnOnTrack();
        } else {
            piGPIO.turnOffTrack();

Edit: Added @PostConstruct to two classes where I am trying to use SimpMessagingTemplate.

@PostConstruct
void onMade() {
    log.info("Bean initialised");

Starting the app, result seems to show both classes are being found and initialised by Spring?

2020-11-16 10:34:16.153  INFO 22346 --- [           main] io.github.siaust.slotcar.service.PiGPIO  : Bean initialised
2020-11-16 10:34:16.168  INFO 22346 --- [           main] i.g.siaust.slotcar.service.GPIOService   : Bean initialised

SimpleMessageBrokerHandler seems to have zero destinations, I'm not sure if this is a problem or as the app runs this changes.

2020-11-16 10:34:24.448  INFO 22346 --- [           main] o.s.m.s.b.SimpleBrokerMessageHandler     : BrokerAvailabilityEvent[available=true, SimpleBrokerMessageHandler [DefaultSubscriptionRegistry[cache[0 destination(s)], registry[0 sessions]]]]

Edit: I added some more log calls to see what is happening with SimpMessagingTemplate in the class after it's initialised.

log.info(template.getDefaultDestination());
log.info(template.getUserDestinationPrefix());
log.info(template.getMessageChannel().toString());

Result

2020-11-16 11:37:17.522  INFO 23925 --- [           main] io.github.siaust.slotcar.service.PiGPIO  : null
2020-11-16 11:37:17.523  INFO 23925 --- [           main] io.github.siaust.slotcar.service.PiGPIO  : /user/
2020-11-16 11:37:17.524  INFO 23925 --- [           main] io.github.siaust.slotcar.service.PiGPIO  : ExecutorSubscribableChannel[brokerChannel]

I'm just guessing here, but the default destination should not be null? Have I got a configuration problem, or should I set the default destination?

Can this be the issue of session assignment that ended up omitted? I mean messaging template requires ws session to do operations and I see no @SubscribeMapping or similar thing in second example. – im_infamous Nov 14, 2020 at 18:51 Please see my edit for additional info. Should I annotate the method I'm using SimpMessagingTemplate instance to convertAndSend() with @SubscribeMapping and subscribe to the endpoint I require? – SimonA Nov 15, 2020 at 10:03 No, you're not tied to @SubscribeMapping at all but I suppose that problem resides somewhere else. Having fresh look on your code taken I should wonder why did you have your code structured the way that controller invokes service and not vice versa? If there's no reason for it then the most convenient way is to accept this behaviour and to refactor code to working boilerplate – im_infamous Nov 16, 2020 at 9:42 My main reason, is that the service class cannot know when to send a message as the listeners are created in the other class. It's poor design by me clearly and I was in the middle of figuring it out. I will probably have to revert to how it was, working, if I can, and figure how to have the listeners notify an event in the service class, maybe with an observer pattern, I'm not sure. – SimonA Nov 16, 2020 at 10:56 Maybe you can also employ some @EventListener + event publishers to make this work properly. I can attach working configuration if needed. – im_infamous Nov 17, 2020 at 7:38

Not entirely sure what you are doing with the missing config etc. Not sure what you mean by "checking the bean" bit either.

Here is my set up for a basic "notification" channel.

  • Client hits the STOMP endpoint /swns/start to open the STOMP connection.
  • The /swns/start endpoint adds the user's principal name and STOMP session ID to a in memmory store.
  • Client stompClient.subscribe() to /notification/item which is a STOMP broker topic.
  • I can then send message to that user or all users using the class I made below.
  • The config:

    @Configuration
    @EnableWebSocketMessageBroker
    public class STOMPConfig implements WebSocketMessageBrokerConfigurer {
        @Override
        public void registerStompEndpoints(StompEndpointRegistry registry) {
            registry.addEndpoint("/notifications").setAllowedOrigins("*");
            registry.addEndpoint("/notifications").setAllowedOrigins("*").withSockJS();
        @Override
        public void configureMessageBroker(MessageBrokerRegistry registry) {
            registry.enableSimpleBroker("/notification");
            registry.setApplicationDestinationPrefixes("/swns");
    

    The Controller

    @Controller
    @Log4j2
    public class SocksController {
        @Autowired
        private ObjectMapper objectMapper;
        @Autowired
        private NotificationDispatcher notificationDispatcher;
        //Send STOMP message to /swns/start to begin a STOMP WSbased connection.
        @MessageMapping("/start")
        public void send(StompHeaderAccessor stompHeaderAccessor) {
            //Can get Spring Principal/Session user from the STOMP header (awesome!).
            final Principal user = stompHeaderAccessor.getUser();
            log.info("{} initiated a STOMP based websocket.", user != null ? user.getName() : "ANON");
            //Add the user's principal name as the key and their STOMP session Id to the static vol HashMap<String,String> in
            //the NotificationDispatcher.
            NotificationDispatcher.getPrincipalNameToSockSessionMap().put(user.getName(), stompHeaderAccessor.getSessionId());
        @PostConstruct
        void onMade() {
            log.info("////////////////////// GIMME UR SOX //////////////////////////////");
    
    @Component
    @EnableScheduling
    @Log4j2
    public class NotificationDispatcher {
        @Getter
        @Setter
        private volatile static HashMap<String, String> principalNameToSockSessionMap = new HashMap<>();
        @Autowired
        private SimpMessagingTemplate simpMessagingTemplate;
        public void sendToUser(String principalName,String destination,Notification notification) throws NotificationException {
            if(!principalNameToSockSessionMap.containsKey(principalName)){
                throw new NotificationException(String.format("Can not get session for principal name `%s` as there is no session in RAM map.",principalName));
            String sessionId = principalNameToSockSessionMap.get(principalName);
            log.info("Sending targeted notification to {} - {}", principalName, sessionId);
            SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
            headerAccessor.setSessionId(sessionId);
            headerAccessor.setLeaveMutable(true);
            simpMessagingTemplate.convertAndSendToUser(
                    sessionId,
                    destination!=null?destination:"/notification/item",
                    notification,
                    headerAccessor.getMessageHeaders());
        @EventListener
        public void sessionDissconectHandler(SessionDisconnectEvent sessionDisconnectEvent) {
            String sessionId = sessionDisconnectEvent.getSessionId();
            log.info("Disconnecting : {}", sessionId);
            principalNameToSockSessionMap.remove(sessionId);
            log.info("Current Sessions Count : {}", principalNameToSockSessionMap.size());
        @Data
        public static class Notification {
            private final String value;
            public Notification(String s) {
                this.value = s;
        public static class NotificationException extends Exception{
            public NotificationException(String s) {
                super(s);
    

    Client Code (is in React and using Redux)

    const store = configureStore({
      reducer: notificationSlice
    var sock = new SockJS('http://localhost/api/notifications');
      const stompClient = Stomp.over(sock);
          console.log('open');  
          stompClient.connect({}, function () {
            console.log(`STOMP connected : ${stompClient.connected}`)
            stompClient.send("/swns/start", {});
          stompClient.subscribe('/user/notification/item', function(menuItem){
              console.log("MESSAGE!")
              console.log(menuItem)
              store.dispatch(setMessage(menuItem.body))
            console.info('connected!')
          (error)=>{
            console.error(error)
                    Thanks for the reply, I'm trying to find some idea from your code. I've edited my main post to include the WebSocketConfig and JavaScipt.   I was checking the Spring Bean because I believe, in my limited understanding, that in this case it is not initialising or, it is on the wrong "channel", so to speak.  When it was working,I injected a class into PiGPIO class, which had a method sendMessage invoking SimpMessagingTemplate's convertAndSend. Since then I had to refactor, but I'm guessing SimpMessagingTemplate was initialised correctly in that particular class, for some reason?
    – SimonA
                    Nov 15, 2020 at 11:34
                    // constructor injected, it won't allow field injection here private final SimpMessagingTemplate template; Does indicate that the class is not being picked up by the Applcation bean context manager (forgot its name). Has it been correctly annotated with @Component and the @EnableWebSocketMessageBroker annotations?
    – Jcov
                    Nov 15, 2020 at 12:34
                    The class where SimpMessagingTemplate was working was annotated with @Service, which I have tried adding to the class it's currently called from but no effect. The current class is annotated @Component.  I had to refactor as I had a circular dependency problem going on, otherwise I would have left it as it was. I will edit in the class where the convertAndSend call worked.
    – SimonA
                    Nov 15, 2020 at 15:07
                    With SimpMessagingTemplate, it is already a defined bean with the websocket dependency added and therefore unless you need to change it for some reason then it should be able to be @Autowired in to any context aware class (I.e. @Component/Sevice/Configuration).  I'd add in an @PostConstruct on your beans to see if they are starting up? Like on mine: @PostConstruct     void onMade() {         log.info("////////////////////// GIMME UR SOX //////////////////////////////");     } 
    – Jcov
                    Nov 15, 2020 at 15:45
                    I have a feeling it may be here: this.template.convertAndSend("/topic/gpio", message); where for STOMP you should be including the headers etc. See codesandnotes.be/2020/03/31/…
    – Jcov
                    Nov 16, 2020 at 11:48
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.