Spring Stomp Websockets Callback Not Called - javascript

Given a websocket configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/queue", "/topic");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/add").withSockJS();
}
}
and client javascript:
<script type="text/javascript">
console.log('begin javascript');
var stompClient = null;
function connect() {
var socket = new SockJS('/myapp/add');
stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
console.log('Connected to STOMP: ' + frame);
stompClient.subscribe('/user/topic/abc', function(calResult) {
console.log('*** Got it ***');
});
});
}
connect();
</script>
and sending this message from the server:
messagingTemplate.convertAndSendToUser(username, "/topic/abc", "hello");
the callback never gets fired.
The javascript console shows that the connection is made:
Connected to STOMP: CONNECTED user-name:jschmoe heart-beat:0,0
version:1.1
SUBSCRIBE id:sub-0 destination:/user/topic/abc
and the tomcat console shows:
Processing SUBSCRIBE destination=/topic/abc-useryl3ovhr2
subscriptionId=sub-0 session=yl3ovhr2 user=jschmoe payload=byte[0]
and then when the message is sent:
Processing MESSAGE destination=/topic/abc-useryl3ovhr2 session=null
payload=hello
Seems like everything works except for the callback.

In my case the problem was caused by XML configuration, once I switched to Java config with #EnableWebSocketMessageBroker annotation I received the messages on client side.

Related

How to get and send messages asynchronously from one websocket client to antoher

I need to establish connection between client websocket threw my backend on spring java to another websocket where my backend is a client, I established connection as client but can't figure out how to send it back as soon as my client send me message,
My Client Endpoint works as I need
#Service
#ClientEndpoint
public class ClientEndpoint {
Session userSession = null;
private MessageHandler messageHandler;
public WbsClientEndpoint(#Value("${url}") String url) {
try {
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
container.connectToServer(this, new URI(url));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#OnOpen
public void onOpen(Session userSession) {
System.out.println("opening web socket");
this.userSession = userSession;
}
#OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing web socket");
this.userSession = null;
}
#OnMessage
public void onMessage(String message) {
if (this.messageHandler != null) {
this.messageHandler.handleMessage(message);
}
}
public void addMessageHandler(MessageHandler msgHandler) {
this.messageHandler = msgHandler;
}
public void sendMessage(String message) {
this.userSession.getAsyncRemote().sendText(message);
}
public interface MessageHandler {
void handleMessage(String message);
}
}
and example of method when I send my message as client, it does what I need but now I only printing the message cause cannot connect it to my message handler:
#Override
public void addDevice(DeviceTransfer deviceTransfer) {
clientEndPoint.addMessageHandler(message -> {
System.out.println("Response: " + message);
});
clientEndPoint.sendMessage(JSON.toJSONString(deviceTransfer));
}
Also I wrote a websockethandler for messages that comes to my backend:
#Component
public class WebSocketHandler extends AbstractWebSocketHandler {
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
System.out.println("New Text Message Received" + message + " ___ " + session);
String clientMessage = message.getPayload();
if (clientMessage.startsWith("/addDevice")) {
//Here I don't know how to send this clientMessage and wait for result from my addDevice method to return it back
}
}
}
And I need to connect both of the realizations to establish connection in real time.
When client sends message to me I must send this message to another server as client.
My client code on JavaScript as example, when I press button it establish connection to my web socket and send my message:
const connectBtn = document.getElementById('connect');
if (connectBtn) {
connectBtn.addEventListener('click', function () {
window.socket1 = new WebSocket("ws://localhost:8081/ws");
socket1.onopen = function(e) {
console.log("[open]");
socket1.send(JSON.stringify({"command": "subscribe","identifier":"{\"channel\":\"/topic/addDevice\"}"}))
};
socket1.onmessage = function(event) {
console.log(`[message]: ${event.data}`);
};
socket1.onclose = function(event) {
if (event.wasClean) {
console.log(`[close],code=${event.code}reason=${event.reason}`);
} else {
console.log('[close]');
}
};
socket1.onerror = function(error) {
console.log(`[error] ${error.message}`);
};
});
}
It seems to me like you need to keep track of your ClientEndpoint instances:
public class ClientEndpoint {
private HashSet<ClientEndpoint> endpoints = new HashSet<>();
// or perhaps a HashMap using `userSession.id` or similar
private HashMap<string, ClientEndpoint> userToEndpoint = new HahsMap<>();
#OnOpen
public void onOpen(Session userSession) {
System.out.println("opening web socket");
this.userSession = userSession;
this.endpoints.add(this);
this.userToEndpoint.put(userSession.id, this);
}
#OnClose
public void onClose(Session userSession, CloseReason reason) {
System.out.println("closing web socket");
this.endpoints.remove(this);
this.userToEndpoint.delete(userSession.id);
this.userSession = null;
}
}
You can use endpoints/userToEndpoint to find all connected clients, perhaps filter by which rooms they're in (I assume that's what you're trying to accomplish with that subscribe command), and do whatever you want with that information. E.g. a broadcast to everyone except the sender:
for (ClientEnpoint endpoint : this.endpoints) {
if (endpoint == sender) continue; // Don't send to sender
endpoint.sendMessage("message");
}

After reconnecting to my socket.io server does not receive the event sent from my android device

I really don't see what is wrong, On an android phone, I connect to my sever node js socket.io, all the events worked. But if I cut the connection and reactivate it. The reconnection is detected by the server. But the events sent from the phone to the server are not received by the server.
I created a socket with parameters that allow reconnection :
private Socket socket;
{
try {
IO.Options opts = new IO.Options();
opts.forceNew = false;
opts.reconnection = true;
opts.reconnectionAttempts = Integer.MAX_VALUE;
opts.reconnectionDelay = 1000;
opts.reconnectionDelayMax = 5000;
socket = IO.socket(Constant.URL_SOCKET, opts);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
Obviously I didn't forget to call in onCreate :
socket.connect();
actionSocket();
Here my listerner also call in onCreate :
socket.on(Socket.EVENT_CONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
actionSocket();
}
});
socket.on(Socket.EVENT_RECONNECT, new Emitter.Listener() {
#Override
public void call(Object... args) {
actionSocket();
}
});
actionSocket corresponds to this function:
socket.on("toast", new Emitter.Listener() {
#Override
public void call(Object... args) {
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(MainActivity.this, "Toast", Toast.LENGTH_SHORT).show();
}
});
}
});
For test reconnection i got this button :
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
socket.emit("toast", new JSONObject());
}
});
Server side :
io.on('connection', function(socket){
console.log("connection");
addSocketListeners(socket);
});
And addSocketListeners()
function addSocketListeners(socket){
socket.on("toast", function(data){
socket.emit("toast", {});
});
}
Hope my question is clear. Thanks to everyone who will help me.
Try this:
off the event when disconnect the socket and on when you connect every time
socket.off("toast");
EDIT : Instead of off use removeAllListeners:
io.on('connection', function(socket){
socket.on("disconnect", function(){
socket.removeAllListeners("toast");
});
});

session is not getting timeout after specified time when adding servlet filter in java websocket

I have websocket in one of my webapplication used to populate the notification
message.
The entire application is a ear file and we have multipe war file in it and this websocket endpoint is one war file.
It contains below:
#ServerEndpoint(value = "/message", configurator = WebSocketConfigurator.class)
public class WebsocketEndpoint {
#OnOpen
public void onOpen(Session session){
}
#OnClose
public void onClose() {
}
#OnError
public void error(Session session, Throwable throwable) {
}
#OnMessage
public void handleMessage(String message, final Session session) {
synchronized (session) {
if (session != null && session.isOpen()) {
int count = 2;
session.getAsyncRemote().sendText("" + count);
session.setMaxIdleTimeout(-1);
}
}
}
}
public class WebSocketConfigurator extends ServerEndpointConfig.Configurator {
private boolean isValidHost;
#Override
public boolean checkOrigin(String originHeaderValue) {
try {
URL url = new URL(originHeaderValue);
String hostName = url.getHost();
isValidHost = Utils.isValidHostName(hostName);
} catch (Exception ex){
logger.error("Error in check checkOrigin for websocket call: "+ex.getMessage());
}
return isValidHost;
}
}
I am calling the endpoint in first login where handshake will happen and get the message and then in every 2 mins it will call to get the message only no handshake since handshake is already there
ui is as below:
var websocketUrl = new WebSocket("ws://localhost:7001/example/message");
webSocket.onopen = function() {
webSocket.send('');
}
var interval= setInterval(function() {
'pollMessage()'
}, 120*1000);
function pollMessage(){
if(wsEndPoint.readyState==1){
wsEndPoint.send('');
}
if(wsEndPoint.readyState ==2 || wsEndPoint.readyState==3){
wsEndPoint.close();
clearInterval(interval);
}
wsEndPoint.onmessage = function(message){
alert(message);
}
}
#WebServlet(urlPatterns = {"/message"})
public class MessageWebsocketServlet extends HttpServlet
{
}
The above works fine without any issue.
But I want to authenticate the call for security.
So I added webfilter
#WebFilter(urlPatterns = {"/message"}, filterName = "AuthFilter",initParams = {
#WebInitParam(name = "authorizationEnabled", value = "false")
})
#ServletSecurity(httpMethodConstraints = {#HttpMethodConstraint(value = "GET")})
public class MessageWebsocketServletFilter implements Filter{
private FilterConfig config = null;
#Override
public void init(FilterConfig config) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse res,
FilterChain chain)
throws ServletException, IOException {
//authentication logic goes here and it involved cross origin check and
}
#Override
public void destroy() {
config.getServletContext().log("Destroying SessionCheckerFilter");
}
}
We have configured 30 mins as session timeout and After adding the above filter when the user logged in and idle more than 30 mins the applicaiton is not getting session timed out.
Any pointer would be great help for me.
Short version:
From what I can see, it is because of the line session.setMaxIdleTimeout(-1);
Long version:
When opening a websocket connection, the client proceeds to a handshake. Following the websocket bible (RFC 6455 section 1.3), the handshake starts with a HTTP communication.
However, once the handshake is successful, the communication switches to another protocol as stated:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
The communication is not HTTP anymore. As far as I know, Java Servlet only handles HTTP communication. Consequently, any configuration regarding the servlet has no impact on the websocket configuration.

Java socket io unknown transport undefined

When I try to connect node.js from java socket.io client I'm getting this error:
engine intercepting request for path "/socket.io/" +0ms
engine handling "GET" http request "/socket.io/1/" +0ms
engine unknown transport "undefined" +0ms
Meantime, when I try from a javascript client, works fine:
engine intercepting request for path "/socket.io/" +0ms
engine handling "GET" http request "/socket.io/?EIO=3&transport=polling&t=1494940689150-0&b64=1" +0ms
engine handshaking client "3EFWO3PTlnvZksM8AAAA" +15ms
My Java client code:
import io.socket.IOAcknowledge;
import io.socket.IOCallback;
import io.socket.SocketIO;
import io.socket.SocketIOException;
import org.json.JSONException;
import org.json.JSONObject;
public class BasicExample implements IOCallback {
private SocketIO socket;
public static void main(String[] args) {
try {
new BasicExample();
} catch (Exception e) {
e.printStackTrace();
}
}
public BasicExample() throws Exception {
socket = new SocketIO();
socket.connect("http://localhost:9990", this);
socket.send("Hello Server");
// Sends a JSON object to the server.
socket.send(new JSONObject().put("key", "value").put("key2","another value"));
// Emits an event to the server.
socket.emit("event", "argument1", "argument2", 13.37);
}
#Override
public void onMessage(JSONObject json, IOAcknowledge ack) {
try {
System.out.println("Server said:" + json.toString(2));
} catch (JSONException e) {
e.printStackTrace();
}
}
#Override
public void onMessage(String data, IOAcknowledge ack) {
System.out.println("Server said: " + data);
}
#Override
public void onError(SocketIOException socketIOException) {
System.out.println("an Error occured");
socketIOException.printStackTrace();
}
#Override
public void onDisconnect() {
System.out.println("Connection terminated.");
}
#Override
public void onConnect() {
System.out.println("Connection established");
}
#Override
public void on(String event, IOAcknowledge ack, Object... args) {
System.out.println("Server triggered event '" + event + "'");
}
}
In Java, I'm using socketio.jar downloaded from here http://www.java2s.com/Code/Jar/s/Downloadsocketiojar.htm, wich seems was compiled from here https://github.com/Gottox/socket.io-java-client
In node.js server I'm user socket.io#1.0.6 version.
Please, can someone help me?
This client looks to be outdated about 5 years. It seems this is a newer one: https://github.com/socketio/socket.io-client-java

Java server JavaScript client WebSockets

I'm trying to do a connection between a server in Java and a JavaScript client but I'm getting this error on client side:
WebSocket connection to 'ws://127.0.0.1:4444/' failed: Connection closed before receiving a handshake response
It maybe stays on OPENNING state because the connection.onopen function is never called. The console.log('Connected!') isn't being called.
Could someone let me know what is going wrong here?
Server
import java.io.IOException;
import java.net.ServerSocket;
public class Server {
public static void main(String[] args) throws IOException {
try (ServerSocket serverSocket = new ServerSocket(4444)) {
GameProtocol gp = new GameProtocol();
ServerThread player= new ServerThread(serverSocket.accept(), gp);
player.start();
} catch (IOException e) {
System.out.println("Could not listen on port: 4444");
System.exit(-1);
}
}
}
ServerThread
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
public class ServerThread extends Thread{
private Socket socket = null;
private GameProtocol gp;
public ServerThread(Socket socket, GameProtocol gp) {
super("ServerThread");
this.socket = socket;
this.gp = gp;
}
public void run() {
try (
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
BufferedReader in = new BufferedReader(
new InputStreamReader(
socket.getInputStream()));
) {
String inputLine, outputLine;
while ((inputLine = in.readLine()) != null) {
outputLine = gp.processInput(inputLine);
System.out.println(outputLine);
}
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
GameProtocol
public class GameProtocol {
public String processInput(String theInput) {
String theOutput = null;
theOutput = theInput;
return theOutput;
}
}
Client
var connection = new WebSocket('ws://127.0.0.1:4444');
connection.onopen = function () {
console.log('Connected!');
connection.send('Ping'); // Send the message 'Ping' to the server
};
// Log errors
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
// Log messages from the server
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
To start with, both your code looks identical the Java and JavaScript one. Both work for what they are design to, but the facts is that you are trying to connect a WebSocket client to a socket server.
As I know they are two different things regarding this answer.
I have never tried it your way. That said if I have a network application that use socket than it would be pure client/server socket, and if it was a web application than I would use WebSocket on both side as well.
So far so good..
To make this work, this answer suggests to use any available WebSocket on server side and your problem is solved.
I am using WebSocket for Java and here is a sample implementation that I have tested with your client code and it works, both on client and server side.
import org.java_websocket.WebSocket;
import org.java_websocket.handshake.ClientHandshake;
import org.java_websocket.server.WebSocketServer;
import java.net.InetSocketAddress;
import java.util.HashSet;
import java.util.Set;
public class WebsocketServer extends WebSocketServer {
private static int TCP_PORT = 4444;
private Set<WebSocket> conns;
public WebsocketServer() {
super(new InetSocketAddress(TCP_PORT));
conns = new HashSet<>();
}
#Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
conns.add(conn);
System.out.println("New connection from " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
}
#Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
conns.remove(conn);
System.out.println("Closed connection to " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
}
#Override
public void onMessage(WebSocket conn, String message) {
System.out.println("Message from client: " + message);
for (WebSocket sock : conns) {
sock.send(message);
}
}
#Override
public void onError(WebSocket conn, Exception ex) {
//ex.printStackTrace();
if (conn != null) {
conns.remove(conn);
// do some thing if required
}
System.out.println("ERROR from " + conn.getRemoteSocketAddress().getAddress().getHostAddress());
}
}
On your main method just:
new WebsocketServer().start();
You might need to manipulate your code to fit it with this implementation, but that should be part of the job.
Here is the test output with 2 tests:
New connection from 127.0.0.1
Message from client: Ping
Closed connection to 127.0.0.1
New connection from 127.0.0.1
Message from client: Ping
here is WebSocket maven configuration, otherwise download the JAR file/s manually and import it in your IDE/development environment:
<!-- https://mvnrepository.com/artifact/org.java-websocket/Java-WebSocket -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.0</version>
</dependency>
Link to WebSocket.

Categories