I want to send an alerts to client if anything goes wrong in server side coding using STOMP Client. I have searched in google and didn't find any examples but I did find STOMP client documentation.
I have written following code in controller
WebSocketClient transport = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(transport);
stompClient.setMessageConverter(new StringMessageConverter());
String url = "ws://localhost:8080/SpringMVCStompClient/alert";
StompSessionHandler handler = new MySessionHandler();
stompClient.connect(url, handler);
and MyHandler code is:
public class MySessionHandler extends StompSessionHandlerAdapter {
#Override
public void afterConnected(StompSession session, StompHeaders connectedHeaders) {
Alert alert= new Alert();
alert.setName("Server");
alert.setMessage("Message from server");
session.send("/topic/alert", alert);
}
}
when i am trying to execute this execution is not going to MyHandler class. Is this is the right way to do?. Help he me how can i send a alert to client.
Websocket XML Configuration
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/alert">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
Controller
#Autowired
public SpringMVCController(SimpMessagingTemplate template) {
this.template = template;
}
#Autowired
private SimpMessagingTemplate template;
#MessageMapping("/alert")
#SendTo("/topic/alert")
public Alerts alerts(Alerts alerts) throws Exception {
Alert alert= new Alert();
alert.setName("Server");
alert.setMessage("Message from server");
this.template.convertAndSend("/topic/alert", alert);
return alerts;
}
Client js code service
(function(angular, SockJS, Stomp) {
angular.module("alertApp.services").service("AlertService", function($q, $timeout) {
var service = {}, listener = $q.defer(), socket = {
client: null,
stomp: null
};
service.SOCKET_URL = "/SpringMVCStompClient/alert";
service.ALERT_TOPIC = "/topic/alert";
service.ALERT_BROKER = "/app/alert";
service.receive = function() {
return listener.promise;
};
service.send = function(alert) {
socket.stomp.send(service.ALERT_BROKER, { priority: 9}, JSON.stringify({
message: alert.message,
name: alert.name
}));
};
service.disconnect = function() {
socket.stomp.disconnect();
};
var startListener = function() {
socket.stomp.subscribe(service.ALERT_TOPIC, function(data) {
listener.notify(JSON.parse(data.body));
});
};
service.initialize = function() {
socket.client = new SockJS(service.SOCKET_URL);
socket.stomp = Stomp.over(socket.client);
socket.stomp.connect({}, startListener);
};
return service;
});
})(angular, SockJS, Stomp);
Related
i want to send event to socket io server that ran on java spring boot application,
the weird point is that my angular app can connect to server perfectly and i can get connect and disconnect events in java.but when it comes to emit an event to server or get any event from server it is not working
Java Class:
public class SocketIOServerImpl {
public static void main(String[] args) throws InterruptedException {
Configuration config = new Configuration();
config.setHostname("localhost");
config.setPort(8090);
websocket
final SocketIOServer server = new SocketIOServer(config);
server.addConnectListener(socketIOClient -> {
System.out.println("User Connected");
server.getBroadcastOperations().sendEvent("daniyal", "You Connected to Server Successfully");
});
server.addDisconnectListener(client -> {
server.getBroadcastOperations().sendEvent("daniyal", "You Connected to Server Successfully");
});
server.addEventListener("daniyal", String.class, new DataListener<String>() {
#Override
public void onData(SocketIOClient socketIOClient, String s, AckRequest ackRequest) throws Exception {
System.out.println("User Connected");
server.getBroadcastOperations().sendEvent("daniyal", "You Emited STH to Server Successfully");
}
});
server.start();
Thread.sleep(Integer.MAX_VALUE);
server.stop();
}
}
angular servise.ts:
#Injectable({
providedIn: 'root'
})
export class SocketServiceService {
readonly uri: string = 'ws://localhost:8090';
socket: any;
constructor() {
this.socket = io.connect(this.uri,{ transports: [ 'websocket' ] });
}
listen(eventName: string) {
return new Observable((subscriber) => {
this.socket.on(eventName, (data: any) => {
subscriber.next(data);
});
});
}
emit(eventName: string, data: any) {
this.socket.emit(eventName, data);
}
}
app.component.ts:
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
constructor(private socketService: SocketServiceService) {
}
ngOnInit() {
this.socketService.listen('daniyal').subscribe(
(res) => {
console.log('Server Response: ',res);
});
}
emit(){
this.socketService.emit('daniyal','HI SERVER');
}
}
i use angular version 14 and java 11
i expected server can get the event and when send event to client,client get that too
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 have a websocket server file called server.js
const express = require('express');
const http = require('http');
const WebSocket = require('ws');
const port = 1234;
const server = http.createServer(express);
const wss = new WebSocket.Server({ server });
server.listen(port, function() {
console.log(`Server is listening on port ${port}`);
})
wss.on('connection', function connection(ws, req) {
console.log("connected");
ws.on('message', function incoming(data) {
wss.clients.forEach(function each(client) {
if(client !== ws && client.readyState == WebSocket.OPEN) {
client.send(data.toString());
console.log(client.id + " => " + data.toString());
}
})
})
})
In java i wanted to connect to the websocket with initiateWebSocket() function
private void instantiateWebSocket() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(WEBSOCKET_URL).build();
SocketListener socketListener = new SocketListener(this);
webSocket = client.newWebSocket(request, socketListener);
}
public class SocketListener extends WebSocketListener {
public MainActivity activity;
public SocketListener (MainActivity activity) {
this.activity = activity;
}
#Override
public void onOpen(#NonNull WebSocket webSocket, #NonNull Response response) {
super.onOpen(webSocket, response);
activity.runOnUiThread(() -> Toast.makeText(activity, "Connection starts", Toast.LENGTH_LONG).show());
webSocket.send("Hello..."); // send message here Hello
}
}
So I wanted to send the word "Hello..." when the connection is connected to the websocket.
On my server.js, it showed connected, which means ws.on("connection") is executed...
But the weird part is ws.on("message") is not executed and Hello... is not received
Also note that I didn't received any errors, everything is working fine, only Hello... is not received.
Any advice? Thank you.
I'm new in Spring and I trying log in to chat application with spring security username.
But controller or stomp don't recognize input. And don't allows to enter chat
Where is I should to write in stomp or controller?
Thank you.
Chat Controller
#Controller
public class ChatController {
#GetMapping("/chat")
public String chat() {
return "chat";
}
#MessageMapping("/chat.sendMessage")
#SendTo("/topic/public")
public ChatMessagePojo sendMessage(#Payload ChatMessagePojo chatMessagePojo) {
return chatMessagePojo;
}
#MessageMapping("/chat.addUser")
#SendTo("/topic/public")
public ChatMessagePojo addUser(#Payload ChatMessagePojo chatMessagePojo, SimpMessageHeaderAccessor headerAccessor) {
// Add username in web socket session
headerAccessor.getSessionAttributes().put("email", chatMessagePojo.getSender());
return chatMessagePojo;
}
ChatMessage class
public class ChatMessagePojo {
private MessageType type;
private String content;
private String sender;
public enum MessageType {
CHAT,
JOIN,
LEAVE
}
}
Stomp
const connect = () => {
const Stomp = require("stompjs");
var SockJS = require("sockjs-client");
SockJS = new SockJS("http://localhost:8080/ws");
stompClient = Stomp.over(SockJS);
stompClient.connect({}, onConnected, onError);
};
const onConnected = () => {
console.log("connected");
stompClient.subscribe(
"/user/" + currentUser.id + "/queue/messages",
onMessageReceived
);
};
const sendMessage = (msg) => {
if (msg.trim() !== "") {
const message = {
senderId: currentUser.id,
recipientId: activeContact.id,
senderName: currentUser.name,
recipientName: activeContact.name,
content: msg,
timestamp: new Date(),
};
stompClient.send("/app/chat", {}, JSON.stringify(message));
}
};
Example login screen to chat:
I tried but failed, and then I solved it with my own solution. Maybe you can refer to it.
Make a socket endpoint for authentication and send the ID and password for authentication.
Save id and session after successful authentication.
Then get the user id through the session map SESSION_USERID.
Frontend
stompClient.send("/app/chat/login", {}, "{type: AUTH, data: {id: 'mock', pwd: '12345678'}}");
Backend
public static final Map<String, Long> SESSION_USERID = new ConcurrentHashMap<>();
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message)
// parse message to object
if (message.type == AUTH) {
// do auth
// if auth succeed, cache the user
SESSION_USERID.put(userId, session.getId());
} else if (message.type == CHAT) {
Long userId = SESSION_USERID.get(session.getId());
if (userId == null) {
// Send error or close session
} else {
// Send messages to other peoples
}
}
}
//----
My goal is to send one request from frontend to backend and receive multiple responses. I'm using WebSocket because responses are very frequent and WebSocket seems to be best protocol to do it and SseEmitter send multiple responses from backend.
Here is my request controller:
#MessageMapping("/emitter")
#SendTo("/topic/response")
public SseEmitter output(RunData runData) throws Exception {
SseEmitter emitter = new SseEmitter();
new Thread(new Runnable() {
#Override
public void run() {
try {
RemoteHostController rhc = new RemoteHostController(runData);
rhc.execute();
while (rhc.getActiveCount() > 0) {
emitter.send(rhc.getAllOutput());
Thread.sleep(2000);
}
emitter.complete();
} catch (Exception ee) {
ee.printStackTrace();
emitter.completeWithError(ee);
}
}
}).start();
return emitter;
}
RemoteHostController is managing connections and getAllOutput returns output from the hosts.
Frontend application is running quite simple index.html that is connecting to websocket using Stomp and SockJS, sends data to server and generate tag with data from response:
function connect() {
var socket = new SockJS('http://localhost:8080/emitter');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/response', function(greeting){
showOutput(greeting.body);
});
});
}
function sendData() {
var hostname = document.getElementById('hostname').value;
var username = document.getElementById('username').value;
var password = document.getElementById('password').value;
var command = document.getElementById('command').value;
stompClient.send("/app/emitter", {}, JSON.stringify({ 'hostname': hostname,
'username': username,
'password': password,
'command': command}));
}
function showOutput(message) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
response.appendChild(p);
}
When I send data to backend only response I get is:
{"timeout":null}
It's SseEmitter timeout field, when I change timeout it will return {"timeout":<timeout_value>}.
I can see in logs that RemoteHostController is connecting to hosts and executing commands properly.
Am I doing something wrong? Or WebSocket only supports one request one response communication?
Here is an example of both WebSocket and SSE. As noted above SSE is not supported by IE browsers. Adding as much as I can for completeness. Make sure you are not using a RestController when you use SeeEmitter because that will return the object and that is my guess from the description above.
pom.xml
<dependencies>
<!-- Spring boot framework -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
</dependencies>
Web socket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class ApplicationWebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
super.configureMessageBroker(registry);
registry.enableSimpleBroker("/topic");
}
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/socketrequest").withSockJS();
}
}
Request Data:
public class RequestData {
private String string1;
private String string2;
// excluding getters and setters
}
Web socket controller:
#Controller
public class WebSocketController {
#Autowired
SimpMessagingTemplate simpMessagingTemplate;
#MessageMapping("/processrequest")
void runWebSocket( RequestData requestData ) {
new Thread(new RunProcess(requestData)).start();
}
private class RunProcess implements Runnable {
private RequestData requestData;
RunProcess(RequestData requestData) {
this.requestData = requestData;
}
public void run() {
simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString1());
simpMessagingTemplate.convertAndSend("/topic/response", requestData.getString2());
simpMessagingTemplate.convertAndSend("/topic/response", "A third response via websocket");
}
}
}
Sse Controller:
#Controller
public class SseController {
#RequestMapping("/emitter")
public SseEmitter runEmitter(#RequestParam(value = "string1") String string1,
#RequestParam(value = "string2") String string2)
{
SseEmitter sseEmitter = new SseEmitter();
RequestData requestData = new RequestData();
requestData.setString1(string1);
requestData.setString2(string2);
new Thread(new RunProcess(requestData,sseEmitter)).start();
return sseEmitter;
}
private class RunProcess implements Runnable {
private RequestData requestData;
private SseEmitter sseEmitter;
RunProcess(RequestData requestData, SseEmitter sseEmitter) {
this.requestData = requestData;
this.sseEmitter = sseEmitter;
}
public void run() {
try {
sseEmitter.send(requestData.getString1());
sseEmitter.send(requestData.getString2());
sseEmitter.send("A third response from SseEmitter");
sseEmitter.complete();
} catch (IOException e) {
e.printStackTrace();
sseEmitter.completeWithError(e);
}
}
}
}
Html code:
<script src="/javascript/sockjs-0.3.4.js"></script>
<script src="/javascript/stomp.js"></script>
<script type="text/javascript">
var stompClient = null;
function connect() {
var socket = new SockJS('http://localhost:8085/socketrequest');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/response', function(message){
showOutput(message.body);
});
});
}
function doWebsocket() {
stompClient.send("/processrequest", {}, JSON.stringify({ 'string1': 'The first string', 'string2' : 'The second string' }));
}
function doSse() {
console.log("doSse");
var rtUrl= '/emitter?string1=first string sse&string2=second string sse';
var source = new EventSource(rtUrl);
source.onmessage=function(event){
showOutput(event.data)
};
}
function showOutput(message) {
var response = document.getElementById('response');
var p = document.createElement('p');
p.style.wordWrap = 'break-word';
p.appendChild(document.createTextNode(message));
response.appendChild(p);
}
connect();
</script>
</head>
<div>
Starting page
</div>
<div>
<button id="websocket" onclick="doWebsocket();">WebSocket</button>
<button id="sse" onclick="doSse();">Server Side Events</button>
</div>
<div >
Response:
<p id="response"></p>
</div>
</html>