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?
–
–
–
–
–
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 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.