I need to manage a single WebSocket connection in a Android application. For this I implemented a web application where set up a WebSocket Message Broker using Spring, as its quick start.
The problem is that I could not make a connection in my Android application. I'm using Autobahn Android, but I can not connect to subscribe and publish on topics (like SockJS with STOMP).
Server (Spring):
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/ws"></websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic"/>
</websocket:message-broker>
#Controller
public class MessageController {
#MessageMapping("/ws")
#SendTo("/topic/poc")
public MyEntity proofOfConcept(String message) throws Exception {
return new MyEntity(message);
}
}
Client (Autobahn Android):
final String wsuri = "ws://" + HOSTNAME + ":" + PORT + "/myapp/ws";
mConnection.connect(wsuri, new Wamp.ConnectionHandler() {
#Override
public void onOpen() {
mConnection.subscribe("/myapp/ws/topic/poc", MyEntity.class, new Wamp.EventHandler() {
#Override
public void onEvent(String topicUri, Object event) { }
});
}
#Override
public void onClose(int code, String reason) {
// ERROR: Could not connect to /HOSTNAME...
}
});
I managed to connect using simple handlers of spring instead of message broker, but that limits me to "listen" only one endpoint per connection... Could anyone help me please?
AutobahnAndroid implements WebSocket and WAMP, not STOMP. Different from STOMP, WAMP provides both Publish & Subcribe and Remote Procedure Calls.
For using WAMP, you will need a WAMP Router. You can find client and router implementations for WAMP here.
Related
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.
What I am trying to do
I have a lobby with players and when someone leaves the lobby I want to update it for every client so the actual list of players is displayed.
What I have done
To avoid cyclical requests being sent from frontend to backend I decided to use web sockets. When someone leaves the lobby then request is sent to REST api and then backend, upon receiving this request, does all the business logic and afterwards "pokes" this lobby using socket in order to update all clients in the lobby.
My problem
Everything works fine except the case when user closes the browser or the tab because I can't send a request in this scenario. (as far as I know this is impossible to do using javascript and beforeunload event, onDestroy() methods, etc..)
My question
Is it possible to check on the server side whether any socket disconnected and if yes then how can I do this? I also tried to use heartbeat which is being sent from frontend to backend but I don't know how to handle this heartbeat message on the server side.
Server side (Spring boot)
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguartion implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/api/socket")
.setAllowedOrigins("*")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
ThreadPoolTaskScheduler te = new ThreadPoolTaskScheduler();
te.setPoolSize(1);
te.setThreadNamePrefix("wss-heartbeat-thread-");
te.initialize();
config.enableSimpleBroker("/lobby")
.setHeartbeatValue(new long[]{0, 1000})
.setTaskScheduler(te);
}
}
#Controller
public class WebSocketController {
private final SimpMessagingTemplate template;
WebSocketController(SimpMessagingTemplate template) {
this.template = template;
}
public void pokeLobby(#DestinationVariable String lobbyName, SocketMessage message) {
this.template.convertAndSend("/lobby/"+lobbyName.toLowerCase(), message);
}
}
Client side
connectToLobbyWebSocket(lobbyName: string): void {
const ws = new SockJS(this.addressStorage.apiAddress + '/socket');
this.stompClient = Stomp.over(ws);
// this.stompClient.debug = null;
const that = this;
this.stompClient.connect({}, function () {
that.stompClient.subscribe('/lobby/' + lobbyName, (message) => {
if (message.body) {
that.socketMessage.next(message.body); // do client logic
}
});
});
}
You can listen for SessionDisconnectEvent in your application and send messages to other clients when you receive such an event.
Event raised when the session of a WebSocket client using a Simple Messaging Protocol (e.g. STOMP) as the WebSocket sub-protocol is closed.
Note that this event may be raised more than once for a single session and therefore event consumers should be idempotent and ignore a duplicate event.
There are other types of events also.
I am currently developing a Web Socket for internal and external requests. However, while trying to test the bidirectional connection via a Chrome command I received the following error:
"Origin 'null' is therefore not allowed access."
Web Socket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class SocketConfig implements WebSocketMessageBrokerConfigurer{
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/bidirectional");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
//register a new endpoint
registry.addEndpoint("/blueframe").setAllowedOrigins("/*");
registry.addEndpoint("/blueframe").setAllowedOrigins("/*").withSockJS();
}
}
JavaScript connection:
var socket = new SockJS('http://192.168.137.23:8080/blueframe');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
});
I already tried to see other posts with no results.
First i quitted using Webservices, instead i start using RabbitMQ for a bidirectional communication.
Regarding Web Socket Configuration, i am not the only one having this problem so i found a parcial solution: Downgrade the Spring version or use Socket handlers instead of StompEndpointRegistry interface.
I have this websocket server developed using Spring Boot. The server is working fine with a js based client.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(final MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/chat").withSockJS();
registry.addEndpoint("/chat");
}
}
The controller:
#Controller
public class ChatController {
#MessageMapping("/chat")
#SendTo("/topic/messages")
public OutputMessage send(final Message message) throws Exception {
final String time = new SimpleDateFormat("HH:mm").format(new Date());
return new OutputMessage(message.getFrom(), message.getText(), time);
}
}
This is the server side. Now, for the client, I have created a #ClientEndpoint and when I connect to the URI "ws://localhost:8080/spring-mvc-java/chat", I am able to establish a connection and I can see that the #OnOpen callback of #ClientEndpoint is triggered.
However, it seems that the userSession.getAsyncRemote().sendText(message) does not have any effect. I don't see the response from the server.
I can see the js client is:
Connecting to the server var socket = new SockJS('/spring-mvc-java/chat')
Subscribing stompClient.subscribe('/topic/messages',...
Send the message stompClient.send("/app/chat",...
I am able to achieve the first step. How to achieve the 2nd and the 3rd step in a Java based client?
Thanks
First of all you need a websocketclient and websocketstompclient
WebSocketClient client = new StandardWebSocketClient();
WebSocketStompClient stompClient = new WebSocketStompClient(client);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());
You have to custom handler from StompSessionHandler for
getPayloadType
handleFrame
afterConnected
handleException
handleTransportError
methods
StompSessionHandler sessionHandler = new CustomStompSessionHandler();
You can connect your sockets like this. You can accomplish sending and receiving messages
StompSession stompSession=stompClient.connect("ws://localhost:8080/chat",sessionHandler).get();
This two trigger your websocket topic/messages give you messages through the sockets
/app/chat sending Message to sockets
stompSession.subscribe("/topic/messages", sessionHandler);
stompSession.send("/app/chat", new Message("Hi", "user"));
do you want like this?
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);
}