5
votes

With the Stomp broker relay for web socket messaging, I can subscribe to a destination /topic/mydest. This creates a broker subscription and receives all messages that something in the system triggers for this broker destination, which happens when some event in the system occurs.

I can subscribe to a destination /app/mydest, and a controller method with @SubscribeMapping("mydest") will be called, and the return value is sent back only on this socket as a message. As far as I can tell, this is the only message that will ever be sent for this subscription.

Is there a way to combine this in a single subscription, i.e. create a broker subscription for a certain /topic destination, and trigger some code that directly sends a message back to the subscriber?

The use case: when an error occurs in the system, a message with a current error count is sent to /topic/mydest. When a new client subscribes, I want to send him only the last known error count. Others are not interested at this moment, as the count has not changed.

My current solution is to subscribe to both /app/mydest and /topic/mydest and use the same message handler on the client. But it really is one logical subscription, and it is a bit error prone as a client needs to remember to subscribe to both.

My questions in this context: will there ever be a further message for the /app/ subscription? Is there anything to call to trigger one? How else can I send initial information to a subscriber for a topic, without sending redundant messages to the existing subscribers?

As requested, here's my Websocket configuration class.

@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws").setAllowedOrigins("*").withSockJS();
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableStompBrokerRelay("/queue/", "/topic/", "/exchange/");
        registry.setApplicationDestinationPrefixes("/app");
    }
}
2
Perhaps I should clarify: the current solution that I describe is fine enough from a practical standpoint. My main motivation for the question is to fully grasp the intent of subscriptions to user destinations, and ways to act on subscriptions to broker destinations. - rainerfrey

2 Answers

1
votes

You can use ApplicationListener and SessionSubscribeEvent. Example:

@Component
public class SubscribeListener implements ApplicationListener<SessionSubscribeEvent> {

    private final SimpMessagingTemplate messagingTemplate;

    @Autowired
    public SubscribeListener(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }

    @Override
    public void onApplicationEvent(SessionSubscribeEvent event) {
        messagingTemplate.convertAndSendToUser(event.getUser().getName(), "/topic/mydest", "Last known error count");
    }
}
-1
votes

You can listen to the session subscribe event and send initial message

@Component
@RequiredArgsConstructor
public class WebSocketEventListener {

private static final Logger logger = LoggerFactory.getLogger(WebSocketEventListener.class);
private final SimpMessagingTemplate simpMessagingTemplate;


@EventListener
public void handleSessionSubscribeEvent(SessionSubscribeEvent event) {
    logger.info("Subscribed to session: " + event);
    Principal user = event.getUser();
    if (user instanceof UsernamePasswordAuthenticationToken) {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) user;
        if (token.getPrincipal() instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) token.getPrincipal();
            simpMessagingTemplate.convertAndSendToUser(userDetails.getUsername(), "/queue/notify", "Hello");
        }
    }
}

}