User Destinations

An application can send messages that target a specific user, and Spring’s STOMP support recognizes destinations prefixed with /user/ for this purpose. For example, a client might subscribe to the /user/queue/position-updates destination. UserDestinationMessageHandler handles this destination and transforms it into a destination unique to the user session (such as /queue/position-updates-user123). This provides the convenience of subscribing to a generically named destination while, at the same time, ensuring no collisions with other users who subscribe to the same destination so that each user can receive unique stock position updates.

When working with user destinations, it is important to configure broker and application destination prefixes as shown in Enable STOMP, or otherwise the broker would handle "/user" prefixed messages that should only be handled by UserDestinationMessageHandler.

On the sending side, messages can be sent to a destination such as /user/{username}/queue/position-updates, which in turn is translated by the UserDestinationMessageHandler into one or more destinations, one for each session associated with the user. This lets any component within the application send messages that target a specific user without necessarily knowing anything more than their name and the generic destination. This is also supported through an annotation and a messaging template.

A message-handling method can send messages to the user associated with the message being handled through the @SendToUser annotation (also supported on the class-level to share a common destination), as the following example shows:

@Controller
public class PortfolioController {

	@MessageMapping("/trade")
	@SendToUser("/queue/position-updates")
	public TradeResult executeTrade(Trade trade, Principal principal) {
		// ...
		return tradeResult;
	}
}

If the user has more than one session, by default, all of the sessions subscribed to the given destination are targeted. However, sometimes, it may be necessary to target only the session that sent the message being handled. You can do so by setting the broadcast attribute to false, as the following example shows:

@Controller
public class MyController {

	@MessageMapping("/action")
	public void handleAction() throws Exception{
		// raise MyBusinessException here
	}

	@MessageExceptionHandler
	@SendToUser(destinations="/queue/errors", broadcast=false)
	public ApplicationError handleException(MyBusinessException exception) {
		// ...
		return appError;
	}
}
While user destinations generally imply an authenticated user, it is not strictly required. A WebSocket session that is not associated with an authenticated user can subscribe to a user destination. In such cases, the @SendToUser annotation behaves exactly the same as with broadcast=false (that is, targeting only the session that sent the message being handled).

You can send a message to user destinations from any application component by, for example, injecting the SimpMessagingTemplate created by the Java configuration or the XML namespace. (The bean name is brokerMessagingTemplate if required for qualification with @Qualifier.) The following example shows how to do so:

@Service
public class TradeServiceImpl implements TradeService {

	private final SimpMessagingTemplate messagingTemplate;

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

	// ...

	public void afterTradeExecuted(Trade trade) {
		this.messagingTemplate.convertAndSendToUser(
				trade.getUserName(), "/queue/position-updates", trade.getResult());
	}
}
When you use user destinations with an external message broker, you should check the broker documentation on how to manage inactive queues, so that, when the user session is over, all unique user queues are removed. For example, RabbitMQ creates auto-delete queues when you use destinations such as /exchange/amq.direct/position-updates. So, in that case, the client could subscribe to /user/exchange/amq.direct/position-updates. Similarly, ActiveMQ has configuration options for purging inactive destinations.

In a multi-application server scenario, a user destination may remain unresolved because the user is connected to a different server. In such cases, you can configure a destination to broadcast unresolved messages so that other servers have a chance to try. This can be done through the userDestinationBroadcast property of the MessageBrokerRegistry in Java configuration and the user-destination-broadcast attribute of the message-broker element in XML.