Scenario:
Client(angular js 1.4) will call Rest endpoint to get data, the server(spring boot) will process the list of files and will return accurate data. To process the list of files, the server will take time depending on the number of files. so I have implements STOMP notification as to send a notification to the client saying "List of files have been processed and here is the bunch of files(result)".
Issue:
Stomp connection is established successfully and client also gets subscribed, but when the server publishes the events, client is not able to receive.
Below is my code snippet:
WebSocketConfig.java
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic"); // Enables a simple in-memory broker
}
}
SocketController.java
#Controller
public class SocketController {
#SendTo("/topic/public")
public String sendMessage() {
LOGGER.info("====> chatMessage()");
return "List updated successfully";
}
}
main.js
connect() {
var socket = new SockJS('/ws');
var stompClient = Stomp.over(socket);
event.preventDefault();
console.log("socket: ", socket);
console.log("stompClient: ", stompClient);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/public', function (payload) {
console.log("payload: ", payload);
var message = JSON.parse(payload.body);
console.log("message: ", message);
});
}, function (error) {
console.log("onError() called");
console.log("error: ",error);
});
};
Scripts used:
<script src="https://cdnjs.cloudflare.com/ajax/libs/sockjs-client/1.1.4/sockjs.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.min.js"></script>
connect() method of main.js is called on button click. Probably there is an issue regarding scope I guess.
Use SimpMessagingTemplate to convert and send messages to the specific topic.
#Controller
public class SocketController {
private static final Logger LOGGER = LoggerFactory.getLogger(SocketController.class);
#Autowired private SimpMessagingTemplate template;
public void sendMessage(String message) {
LOGGER.info("====> sendMessage:");
this.template.convertAndSend("/topic/public", message);
}
}
Related
I'm struggling how to send message to all users in a private channel in Spring. Here is my WebSocket config class:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/all", "/message","/topic");
config.setUserDestinationPrefix("/secured/user");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws");
registry.addEndpoint("/ws")
.setHandshakeHandler(new UserHandshakeHandler())
.withSockJS();
}
}
Then I have a list of all my usernames, so I loop my list to send each of them a private message, like this:
#Service
#Slf4j
public class MessageService {
private final SimpMessagingTemplate simpMessagingTemplate;
public MessageService(SimpMessagingTemplate simpMessagingTemplate) {
this.simpMessagingTemplate = simpMessagingTemplate;
}
#Async
#Scheduled(fixedDelay = 1000, initialDelay = 1000)
public void sendMessageToUser() {
List<String> nameList = Lists.newArrayList("admin", "user");
for(String username : nameList){
simpMessagingTemplate.convertAndSendToUser(username,"/message", "Hello "+username);
}
}
}
Then in my web frontend, I setup a websocket connection like this:
function connect() {
const socket = new SockJS('/ws');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/secured/user/message',
function (message) {
console.log(message);
});
});
}
I got confirmation that frontend successfully establish the websocket connection:
Opening Web Socket...
stomp.min.js:8 Web Socket Opened...
stomp.min.js:8 >>> CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000
stomp.min.js:8 <<< CONNECTED
version:1.1
heart-beat:0,0
user-name:admin
stomp.min.js:8 connected to server undefined
message.js:18 Connected: CONNECTED
user-name:admin
heart-beat:0,0
version:1.1
stomp.min.js:8 >>> SUBSCRIBE
id:sub-0
destination:/secured/user/message
The problem is the message is indeed sent from server side, but simply not able to be received in the frontend.
Hugely appreciate if anyone could help spot the issue in my code. Thanks a lot!
I want to initiate a trigger(maybe a notification) from backend(based in spring boot) to a particular user whose userId is xyz.
the one way i have found is:
initially i connect to a websocket end point and subscribe to channel "/user/Notifications/xyz"
following is the relevant code in my angular typescript
connectToUserWebSocket(userId) {
let socket = new SockJS('http://localhost:5000/fellowGenius');
this.ws = Stomp.over(socket);
let that = this;
this.ws.connect(
{},
(frame) => {
that.ws.subscribe('/user/Notifications/' +userId, (message) => {
console.log("user subscribed");
});
},
(error) => {
alert('STOMP error ' + error);
}
);
}
Now once i have subscribed to my channel . I want to send a trigger to client which is initiated by backend itself so i run a code in my java service.
My relevant java code is:
#SendTo("/user/Notifications/{userId}")
public String sendMeetingNotificationWebSocket(#DestinationVariable String userId) {
return "hello";
}
my websocket configurations are:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer{
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/fellowGenius").setAllowedOrigins("*").addInterceptors(new HttpSessionHandshakeInterceptor()).withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/inbox/","/user/Notifications/");
}
}
But the problem is that even i can see one web socket connected in my spring boot console.
But i don't get a response from the function on the client side.
Please help me with this problem.
I tried to implement this example https://spring.io/guides/gs/messaging-stomp-websocket/ and it all worked fine. So i moved forward and tried to make it work with a standalone HornetQ.
So, i defined a topic in HornetQ config - /topic/requests
Here are the changes i've made
In index.html i got rid of sockJS
function connect() {
var ws = 'ws://127.0.0.1:61613/stomp';
stompClient = Stomp.client(ws);
stompClient.connect("guest", "guest", function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('jms.topic.requests', function(greeting){
showGreeting(JSON.parse(greeting.body).content);
});
});
}
Sending message from browser
function sendName() {
var name = document.getElementById('name').value;
stompClient.send("jms.topic.requests", {}, JSON.stringify({ 'name': name }));
}
ShowGreeting
function showGreeting(message) {
var response = document.getElementById('response');
console.log('response: ' + response);
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
response.appendChild(p);
}
Configured Spring to work with a standalone broker
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/stomp");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic", "/queue");
//registry.setApplicationDestinationPrefixes("/jms");
}
Controller
#MessageMapping("/stomp")
#SendTo("/topic/requests")
public Greeting greeting(HelloMessage message) throws Exception {
System.out.println("Controller called!");
Thread.sleep(3000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
As a result, it can connect to a running instance of hornetQ and send messages to a topic. However, instead of printing specified string back it just prints "undefined". I know that message is reaching the queue, as i have another browser-based subscriber.
That println in controller is never called, so i suspect i've failed to properly configure it, but i have no idea what would it be.
Greeting
public class Greeting {
private String content;
public Greeting(String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
Is there a way to use WebSockets with SockJS client and Spring 4 server but not using STOMP?
Based on this tutorial from Spring's website, I know how to set up a WebSocket based application using Stomp and Spring 4. On the client side, we have:
var socket = new SockJS('/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 on the server side, we have the following in the controller:
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(3000); // simulated delay
return new Greeting("Hello, " + message.getName() + "!");
}
Now, I understand that #MessageMapping("/hello") ensures that if a message is sent to a destination "/hello", then the greeting() method will be called. And since the stompClient is subscribed to "/topic/greetings", the #SendTo("/topic/greetings") will send the message back to the stompClient.
But the problem with the above is that stompClient is a Stomp object. And I want to simply use sock.send('test'); and have it delivered to my server's destination. And I want to do #SendTo("myownclientdestinationmap"), I can receive it by
sock.onmessage = function(e) {
console.log('message', e.data);
};
So, any way to do this with Spring 4, SockJS and without Stomp? Or does Spring 4 WebSocket only supports Stomp?
Spring supports STOMP over WebSocket but the use of a subprotocol is not mandatory, you can deal with the raw websocket. When using a raw websocket, the message sent lacks of information to make Spring route it to a specific message handler method (we don't have any messaging protocol), so instead of annotating your controller, you'll have to implement a WebSocketHandler:
public class GreetingHandler extends TextWebSocketHandler {
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) {
Thread.sleep(3000); // simulated delay
TextMessage msg = new TextMessage("Hello, " + message.getPayload() + "!");
session.sendMessage(msg);
}
}
And then add your handler to the registry in the configuration (you can add more than one handler and use SockJS for fallback options):
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(greetingHandler(), "/greeting").withSockJS();
}
#Bean
public WebSocketHandler greetingHandler() {
return new GreetingHandler();
}
}
The client side will be something like this:
var sock = new SockJS('http://localhost:8080/greeting');
sock.onmessage = function(e) {
console.log('message', e.data);
}
I have an JMS listener, and I have to take the message, manipulate it and then redirect it to an page using websocket.
Well, I’m just confused about the configuration, I have configured the WebSocketConfig:
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/queue");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws/firstep/").withSockJS();
registry.addEndpoint("/ws/secondep/").withSockJS();
}
And this it should be correct, Then my webpage is:
var socket = new SockJS("/myapp-web/api/ws/secondep/",undefined,options);
var stompClient = Stomp.over(socket);
stompClient.connect({
company : "xxx"
}, function(frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/register', function(message){
console.log('message: ' + message);
});
stompClient.subscribe('/topic/update', function(message){
console.log('message: ' + message);
});
And the connection works.
Now On my jms listener I tried to send a message in this way:
public class ImporterListener implements MessageListener {
Logger logger = LoggerFactory.getLogger(ImporterListner.class);
#SendTo("/topic/register")
private String TestMessage() {
return "TestMessage";
}
#Override
public void onMessage(Message message) {
logger.info("Request on message");
if (message instanceof MapMessage) {
MapMessage t = (MapMessage) message;
TestMessage(); //<--- have to send the message here
But it doesn’t work.
The questions are:
How to send a message?
Where do I have to specify the end point (secondep) when I send a message?
Thank you! any help is appreciated!
TestMessage(); //<--- have to send the message here
No, it has't to send, because you use method from the same class, but #SendTo makes your ImporterListener as proxy and Advice will work on method only from another component.
You should inject this:
#Autowired
#Qualifier("brokerMessagingTemplate")
private MessageSendingOperations brokerMessagingTemplate;
And send a message using that:
brokerMessagingTemplate.convertAndSend("/topic/register", "TestMessage");
Where do I have to specify the end point (secondep) when I send a message?
It is for the #MessageMapping("/ws/secondep") on some POJO method to receive message from clients. It isn't for the sending part.