Sending multiple messages to the same topic with Stomp - spring

so I'm trying to send messages back to my browser from the server and have two methods that are supposed to be wired to send the messages to the STOMP topic.
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public static Greeting greeting(HelloMessage message) throws Exception {
System.out.println("Sending message...");
Application.startBody(message.getName());
return new Greeting("Hello, " + message.getName() + "!");
}
#SendTo("/topic/greetings")
public static Greeting security(String message) {
System.out.println("entered the informer");
return new Greeting("bye, " + message + "!");
}
The first one is also mapped to receive messages and send one back. The first funciton works and the message makes it back to the browser and displays on the webpage. The second once however, does not work. It never displays a received message in the console of the web page. Can I only send to the same topic with one method? I tried changing the topic and adding a subscription to my Stomp client but that didn't work either. Does it have anything to do with the second method being static? (I need to call it from a separate class.)
This is my subscription in the html file:
function connect() {
var socket = new SockJS("http://localhost:8080/hello");
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function(greeting){
showGreeting(JSON.parse(greeting.body).content);
});
});
}
And here is my WebSocketConfig.java:
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app").enableSimpleBroker("/queue","/topic");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
}
}

I have the same problem and reading the WebShocket Support of Spring it's said:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
STOMP servers can use the MESSAGE command to broadcast messages to all subscribers.
It’s important to know that a server cannot send unsolicited messages.*
All messages from a server must be in response to a specific client subscription and the "subscription-id" header of the server message must match the "id" header of the client subscription.
So I guess that the first method works because it's a response form the previous mapping.
Ok, I solved this problem with The SimpMessageSendingOperations
#Controller
public class PrincipalController {
private static SimpMessageSendingOperations messagingTemplate;
#Autowired
public PrincipalController(SimpMessageSendingOperations messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
...
public static void security(String message){
System.out.println("entered the informer");
PrincipalController.messagingTemplate.convertAndSend("/topic/greetings", new Greeting("bye, " + message + "!"));
}
}
...

#Theo - from another class
just autowire this service
#Service
public class MessageService {
#Autowired
public SimpMessageSendingOperations messagingTemplate;
public void sendMessage( String message ) {
messagingTemplate.convertAndSend( "/topic/greetings", new Greeting( "bye, " + message + "!" ) );
}
}

Related

Capturing a message from unity using WebSocketSharp on a Spring server

I can't solve the problem. It is necessary to send messages to the server through the socket and process them. I can intercept the subscription, unsubscribe, connection and disconnection events on the server. But I can’t understand how to send a message to a specific destonation and intercept it on the server. On the client I use the library https://github.com/sta/websocket-sharp
Server code
#Configuration
#EnableWebSocket
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/multiplayer");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/multiplayer").setAllowedOrigins("*").withSockJS();
}
#Autowired
MessageHandler messageHandler;
// message interception option
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(messageHandler, "/result-game");
}
}
In the registerWebSocketHandlers method, I register a listener, it intercepts messages only if you specify "/multiplayer/websocket" in the "/result-game" place, the only trouble is that it intercepts everything in general and the #EventListaner listeners that intercepted my connection events stop working , shutdowns, etc. And when you try to send data to the client on the socket, the client does not receive them.
Client code
var DOMAIN = "1.1.0.1:8080";
_webSocket = new WebSocket($"ws://{DOMAIN}/multiplayer/websocket");
_webSocket.OnOpen += (sender, e) =>
{
// Sending a connection message
StompMessageSerializer serializer = new StompMessageSerializer();
var connect = new StompMessage("CONNECT");
connect["accept-version"] = "1.1";
connect["heart-beat"] = "10000,10000";
connect["playerId"] = _id;
var s = serializer.Serialize(connect);
SubscribeSocket(clientId.ToString(), $"/multiplayer/connect/{_id}", ConnectServer);
_webSocket.Send(s);
// Attempt to send messages to a specific destination
var content = new { Subject = "Stomp client", Message = "Hello World!!" };
var broad = new StompMessage("SEND", JsonConvert.SerializeObject(content));
broad["content-type"] = "application/json";
broad["destination"] = "/result-game";
_webSocket.Send(serializer.Serialize(broad));
Debug.LogError(TAG + "Connect open");
};
_webSocket.ConnectAsync();
On the server, in addition to the option above, there are two more attempts to intercept
First
#Slf4j
#Controller
#RequiredArgsConstructor
public class MessageController{
#MessageMapping("/result-game")
public void say(String message) throws InterruptedException {
Thread.sleep(30);
}
}
Second
#ServerEndpoint("/result-game")
public class MessageHandle {
#OnMessage
public void handleMessage(Session session, String message) {
// Do something with the message
System.out.println("Received message: " + message);
}
#OnMessage
public void processGreeting(String message, Session session) {
System.out.println("Greeting received:" + message);
}
}
Could someone tell me what I'm doing wrong with spring and working with sockets for the first time

Spring Boot - WebSocket - Doesn't show subscribers [duplicate]

This question already has answers here:
Principal is null for every Spring websocket event
(2 answers)
Closed last month.
I was going over the basic Spring Boot WebSocket Tutorial: https://spring.io/guides/gs/messaging-stomp-websocket/
I decided to modify it to print out how many users are subscribed to a channel in the console but couldn't figure it out for hours. I've seen a few StackOverflow posts but they don't help. The last one I check was this: https://stackoverflow.com/a/51113021/11200149 which says to add try this:
#Autowired private SimpUserRegistry simpUserRegistry;
public Set<SimpUser> getUsers() {
return simpUserRegistry.getUsers();
}
So, I added the above to my controller, and here is the change:
#Controller
public class GreetingController {
#Autowired
private SimpUserRegistry userRegistry;
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Set<SimpUser> subscribedUsers = userRegistry.getUsers();
System.out.println("User amount: " + subscribedUsers.size()); // always prints: 0
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
}
This always prints 0:
System.out.println("User amount: " + subscribedUsers.size());
I'm coming from Socket.IO so maybe things work a bit differently because I've seen people implement their own manual Subscription Service classes. In socket.io this would be a piece of cake so I would assume Spring Boot would have this, but I just can't seem to find it.
Edit: This post does a great explanation for this problem.
Principal is null for every Spring websocket event
Maybe you can try to add custom HandshakeHandler class into registry and override the determineUser method to return the Principal object that containing subscriber name so that the SimpUserRegistry can work properly.
If you would like to see the effect, the below is what I'm trying.
app.js (sending out a user name through request parameter)
function connect() {
var socket = new SockJS('/gs-guide-websocket?name=' + $('#name').val());
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}
custom class extends DefaultHandshakeHandler.class
#Component
public class WebSocketHandShakeHandler extends DefaultHandshakeHandler {
#Override
protected Principal determineUser(ServerHttpRequest request, WebSocketHandler wsHandler, Map<String, Object> attributes) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpServletRequest httpServletRequest = servletRequest.getServletRequest();
String name = httpServletRequest.getParameter("name");
return new MyPrincipal(name);
}
}
custom object implement Principal.class
#Data
#NoArgsConstructor
#AllArgsConstructor
public class MyPrincipal implements Principal {
private String name;
}
WebSocketConfig.class
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Autowired
private WebSocketHandShakeHandler webSocketHandShakeHandler;
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/gs-guide-websocket")
.setHandshakeHandler(webSocketHandShakeHandler)
.withSockJS();
}
}
Show all subscribers
#RestController
public class ApiController {
#Autowired
private SimpUserRegistry simpUserRegistry;
#GetMapping("/users")
public List<String> connectedEquipments() {
return this.simpUserRegistry
.getUsers()
.stream()
.map(SimpUser::getName).toList();
}
}
Result
By the way, you can check the DefaultSimpUserRegistry.class to observe the process of putting name into subscribers user map.

Is there a way to send websocket message from regular method in spring

I am building web application. There are admin and user roles provided. When user making some action admin is recieving a message that something happened. Websocket connection establishing when user logged. Is there a way to not create ws connection for user and use only HHTP protocol to sending message and send WS message from controller method only?
Now i have theese settings:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
}
}
#Controller
public class NotificationController {
#MessageMapping("/notification")
#SendTo("/topic/test")
public Notification getNotification(Notification notification) {
return notification;
}
}
Yes it is possible.
You have to inject SimpleMessagintTemplate, with #Autowire or with constructor.
private final SimpMessagingTemplate simpMessagingTemplate;
public ConstructorName(SimpMessagingTemplate simpMessagingTemplate){
this.simpMessagingTemplate = simpMessagingTemplate;
}
In your controller, or function where you want to send the message to the client use the convertAndSendToUser function.
simpMessagingTemplate.convertAndSendToUser("userId","/private", messageData);
On javascript client side.
var Sock = new SockJS('http://localhost:8080/ws');
stompClient = over(Sock);
stompClient.connect({}, onConnected, onError);
stompClient.subscribe('/topic/' + userId + '/private', onMessageReceived);

How to pass message to controller by #MessageMapping with specified user url?

I have such problem. When i try to send message from client side to server, it doesn't match with my #MessageMapping methods. I don't know how to intercept messages on controller layer.
Client side
sends message (it's react-stomp that uses sockjs):
move = (move) => {
this.clientRef.sendMessage("/user/${this.state.opponentId}/queue/move", JSON.stringify(move))
};
Server side. WebSocketConfig:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/handler")
.setHandshakeHandler(new CustomHandshakeHandler())
.setAllowedOrigins("http://localhost:3000")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry brokerRegistry) {
brokerRegistry.setApplicationDestinationPrefixes("/app");
brokerRegistry.enableSimpleBroker("/topic", "/queue", "/user");
}
#EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new MyChannelInterceptor());
}
}
I also added interceptor class to check path of incomming message:
public class MyChannelInterceptor implements ChannelInterceptor {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
return message;
}
}
On debugging of MyChannelInterceptor i see message with payload and headers. There is simpDestination header with such value:
/user/baedde36-0f9e-4fa5-b8d7-687db1dbcd67/queue/move
What #MessageMapping value should i write to handle messages from specified users? This message succesfully gets to frontside by subscription on this topic but doesn't stay on any controller:
`/user/${message}/queue/move`
I just want to handle messages on server side but i can't catch it there.
Okay. As i understood there is 3 ways to handle websocket messages:
/app - handles with controller
/user - handles with broker, sends messages to specific users
/topic - broadcast to topic for all subscribers
In my situation i just need to create json object with userId, receiverId and message. On server side add DTO class and get it as attribute in my controller method.
Solution:
move = (move) => {
let moveDto = {move: move, userId: this.state.userId, opponentId: this.state.opponentId}
this.clientRef.sendMessage(`/app/move`, JSON.stringify(moveDto))
};
Server side:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class MoveDto {
private String userId;
private String opponentId;
private int move;
}
Controller class:
#RestController
public class GameController {
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
...//some code here
#MessageMapping("/move")
public void message(MoveDto moveDto) {
String userMessage= "foo";
String opponentMessage = "bar";
simpMessagingTemplate.convertAndSendToUser(
moveDto.getUserId(), "/queue/message", userMessage);
simpMessagingTemplate.convertAndSendToUser(
moveDto.getOpponentId(), "/queue/message", opponentMessage );
}

How can configure activemq ex. queue time to live , or keep the message in queue until period of time or action?

I am building notification system using spring boot and websocket, I used ActiveMQ to keep Queues for offlines users, it's working perfect.
I need to edit some configuration like queue time to live, keep message in queue until user read it, I don't know how can configure it?
The below is its implementation:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
/*config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");*/
config
.setApplicationDestinationPrefixes("/app")
.setUserDestinationPrefix("/user")
.enableStompBrokerRelay("/topic","/queue","/user")
.setRelayHost("localhost")
.setRelayPort(61613)
.setClientLogin("guest")
.setClientPasscode("guest");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
And:
#Service
public class NotificationWebSocketService {
#Autowired
private SimpMessagingTemplate messagingTemplate;
public void initiateNotification(WebSocketNotification notificationData) throws InterruptedException {
messagingTemplate.convertAndSendToUser(notificationData.getUserID(), "/reply", notificationData.getMessage());
}
}
After invoke NotificationWebSocketService it will create queue "/user/Johon/reply" in activemq contains message when user subscribe in this queue message will received.
How can configure queue time to live , keep message in queue until user read it?
Unit-test to illustrate how to set up expiration of message in user queue.
Required tomcat-embedded, spring-messaging, and active-mq
import org.apache.catalina.Context;
import org.apache.catalina.Wrapper;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.startup.Tomcat;
import org.apache.coyote.http11.Http11NioProtocol;
import org.apache.tomcat.util.descriptor.web.ApplicationListener;
import org.apache.tomcat.websocket.server.WsContextListener;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.messaging.simp.config.ChannelRegistration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.messaging.simp.stomp.*;
import org.springframework.messaging.support.ChannelInterceptorAdapter;
import org.springframework.web.SpringServletContainerInitializer;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import org.springframework.web.socket.WebSocketHttpHeaders;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import static java.util.concurrent.TimeUnit.SECONDS;
public class Test48402361 {
private static final Logger logger = LoggerFactory.getLogger(Test48402361.class);
private static TomcatWebSocketTestServer server = new TomcatWebSocketTestServer(33333);
#BeforeClass
public static void beforeClass() throws Exception {
server.deployConfig(Config.class);
server.start();
}
#AfterClass
public static void afterClass() throws Exception {
server.stop();
}
#Test
public void testUser() throws Exception {
WebSocketStompClient stompClient = new WebSocketStompClient(new SockJsClient(Collections.singletonList(new WebSocketTransport(new StandardWebSocketClient()))));
BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();
StompSession session = stompClient
.connect("ws://localhost:" + server.getPort() + "/test", new WebSocketHttpHeaders(), new StompSessionHandlerAdapter() {
})
.get();
// waiting until message 2 expired
Thread.sleep(3000);
session.subscribe("/user/john/reply", new StompFrameHandler() {
#Override
public Type getPayloadType(StompHeaders headers) {
return byte[].class;
}
#Override
public void handleFrame(StompHeaders headers, Object payload) {
String message = new String((byte[]) payload);
logger.debug("message: {}, headers: {}", message, headers);
blockingQueue.add(message);
}
});
String message = blockingQueue.poll(1, SECONDS);
Assert.assertEquals("1", message);
message = blockingQueue.poll(1, SECONDS);
Assert.assertEquals("3", message);
}
public static class Config extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { };
}
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class[] { Mvc.class };
}
#Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
#Configuration
#EnableWebSocketMessageBroker
public static class Mvc extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/test")
.withSockJS()
.setWebSocketEnabled(true);
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/user").setRelayHost("localhost").setRelayPort(61614);
}
#Autowired
private SimpMessagingTemplate template;
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.setInterceptors(new ChannelInterceptorAdapter() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(message);
switch (sha.getCommand()) {
case CONNECT:
// after connect we send 3 messages to user john, one will purged after 2 seconds.
template.convertAndSendToUser("john", "/reply", "1");
Map<String, Object> headers = new HashMap<>();
headers.put("expires", System.currentTimeMillis() + 2000);
template.convertAndSendToUser("john", "/reply", "2", headers);
template.convertAndSendToUser("john", "/reply", "3");
break;
}
return super.preSend(message, channel);
}
});
}
}
public static class TomcatWebSocketTestServer {
private static final ApplicationListener WS_APPLICATION_LISTENER =
new ApplicationListener(WsContextListener.class.getName(), false);
private final Tomcat tomcatServer;
private final int port;
private Context context;
public TomcatWebSocketTestServer(int port) {
this.port = port;
Connector connector = new Connector(Http11NioProtocol.class.getName());
connector.setPort(this.port);
File baseDir = createTempDir("tomcat");
String baseDirPath = baseDir.getAbsolutePath();
this.tomcatServer = new Tomcat();
this.tomcatServer.setBaseDir(baseDirPath);
this.tomcatServer.setPort(this.port);
this.tomcatServer.getService().addConnector(connector);
this.tomcatServer.setConnector(connector);
}
private File createTempDir(String prefix) {
try {
File tempFolder = File.createTempFile(prefix + '.', "." + getPort());
tempFolder.delete();
tempFolder.mkdir();
tempFolder.deleteOnExit();
return tempFolder;
} catch (IOException ex) {
throw new RuntimeException("Unable to create temp directory", ex);
}
}
public int getPort() {
return this.port;
}
#SafeVarargs
public final void deployConfig(Class<? extends WebApplicationInitializer>... initializers) {
this.context = this.tomcatServer.addContext("", System.getProperty("java.io.tmpdir"));
// Add Tomcat's DefaultServlet
Wrapper defaultServlet = this.context.createWrapper();
defaultServlet.setName("default");
defaultServlet.setServletClass("org.apache.catalina.servlets.DefaultServlet");
this.context.addChild(defaultServlet);
// Ensure WebSocket support
this.context.addApplicationListener(WS_APPLICATION_LISTENER);
this.context.addServletContainerInitializer(
new SpringServletContainerInitializer(), new HashSet<>(Arrays.asList(initializers)));
}
public void start() throws Exception {
this.tomcatServer.start();
}
public void stop() throws Exception {
this.tomcatServer.stop();
}
}
}
"stompClient.subscribe('/user/Johon/reply' --> '/user/Johon/reply' is a topic and not a queue.
If your Stomp client is not connected to the topic '/user/Johon/reply' he will loose every message sent to that topic.
So your solutions are :
convert your topic '/user/Johon/reply' to a queue, so the message remains on the queue indefinitely or until the server end-processes the message.
use Retroactive Consumer & Subscription Recovery Policy
A retroactive consumer is just a regular JMS Topic consumer who
indicates that at the start of a subscription every attempt should be
used to go back in time and send any old messages (or the last message
sent on that topic) that the consumer may have missed.
http://activemq.apache.org/retroactive-consumer.html
The subscription recovery policy allows you to go back in time when
you subscribe to a topic.
http://activemq.apache.org/subscription-recovery-policy.html
Use Durable Subscribers
Durable topic subscribers that are offline for a long period of time
are usually not desired in the system. The reason for that is that
broker needs to keep all the messages sent to those topics for the
said subscribers. And this message piling can over time exhaust broker
store limits for example and lead to the overall slowdown of the
system.
http://activemq.apache.org/manage-durable-subscribers.html
Durable Subscribers with Stomp :
http://activemq.apache.org/stomp.html#Stomp-ActiveMQExtensionstoSTOMP
CONNECT client-id string Specifies the JMS clientID which is used in
combination with the activemq.subcriptionName to denote a durable
subscriber.
some explanations about TTL
A client can specify a time-to-live value in milliseconds for each
message it sends. This value defines a message expiration time that is
the sum of the message's time-to-live and the GMT when it is sent (for
transacted sends, this is the time the client sends the message, not
the time the transaction is committed).
the default time to live is 0, so the message remains on the queue
indefinitely or until the server end-processes the message
UPDATE
if you want to use external ActiveMQ Broker
remove #EnableWebSocketMessageBroker and add to your activemq.xml below connector and restart the broker.
<transportConnector name="stomp" uri="stomp://localhost:61613"/>
if you want to embedd ActiveMQ Broker, add bean to you WebSocketConfig :
#Bean(initMethod = "start", destroyMethod = "stop")
public BrokerService broker() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("stomp://localhost:61613");
return broker;
}
and required dependencies
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-activemq</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-stomp</artifactId>
</dependency>
full examples
Spring Boot WebSocket with embedded ActiveMQ Broker
http://www.devglan.com/spring-boot/spring-boot-websocket-integration-example

Resources