Here is my Java endpoint
#ServerEndpoint("/tweets")
public class TweetStreamServer {
private static final Logger LOGGER = LoggerFactory.getLogger(TweetStreamServer.class);
#OnMessage
public void tweets(final String message, final Session session) throws IOException, InterruptedException {
System.out.println("session id:" + session.getId() + ", search term: " + message);
final Client twitterClient = TwitterHoseBird.getInstance(message);
while (!session.getOpenSessions().isEmpty()) {
for (final Session s : session.getOpenSessions()) {
if (twitterClient.isDone()) {
System.out.println("Twitter Client Done, waiting ...");
}
s.getBasicRemote().sendText(TwitterHoseBird.getMsgQueue().take());
}
}
}
#OnClose
public void onClose(Session session, CloseReason reason) throws IOException {
LOGGER.warn("closing session: {}, remaining session: {}", session.getId(), session.getOpenSessions().size());
}
On Chrome, I do the following in 4 different tabs
var connection = new WebSocket('ws://127.0.0.1:8080/tweetstream-1.0-SNAPSHOT/tweets');
connection.onopen = function () {
connection.send('germany');
};
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
connection.onclose = function (e) {
console.log('closing session');
};
All the tabs then receive data from Twitter.
If I do connection.close(); on one of the tab, only that tab closes connection, but other 3 still receive the data.
However, if I close the tab, it closes connections in all other tabs by logging closing session
What is the issue here?
On server, when I close tab, I see log as
[0m[33m09:24:53,342 WARN [com.self.tweetstream.TweetStreamServer] (default task-16) closing session: tulDjtq4Lx8s1Zo3mA2mwX8B, remaining session: 1
How can I make other connections live, even if any tab is closed?
Related
Following code works fine in the development environment but not in production. I got stuck due to this. I have tried to deploy with tomcat v9.0.24 and Tomcat v8.5.32. In the development environment, tomcat v9.0.24 is working but the issue is with production.
org.apache.catalina.core.StandardContext.startInternal Error during ServletContainerInitializer processing javax.servlet.ServletException: java.lang.NoSuchMethodException:org.glassfish.tyrus.server.TyrusServerConfiguration.<init>()
grails-3.3.4 Gradle:
//*********** Websoket *************//
compile 'org.glassfish.tyrus:tyrus-server:1.13.1'
compile 'javax.websocket:javax.websocket-api:1.1'
compile 'org.glassfish.tyrus:tyrus-container-grizzly-server:1.13.1'
Websocket class
import grails.gorm.transactions.Transactional
import grails.util.Environment
import grails.util.Holders
import pawnshoprest.staff.User
import javax.websocket.*
import javax.websocket.server.ServerEndpoint
#ServerEndpoint(value = "/broadcast/{user_id}/{username}")
class WebSocketsService {
//**************** declare client ***************
private static Set<Session> clients = Collections.synchronizedSet(new HashSet<Session>());
#OnOpen
static void onOpen(Session session) {
System.out.println("Connected ... " + session.getId());
clients.add(session);
}
#OnMessage
static String onMessage(String message, Session session){
println("\n")
System.out.println("my Message: " + message);
synchronized(clients){
// Iterate over the connected sessions
// and broadcast the received message
for(Session client : clients){
//if (!client.equals(session)){
System.out.println("************** Session ID *******************");
System.out.println("sender: " + session.getId());
System.out.println("Reciever: " + client.getId());
//************ broadcast message **********************
if(!session.pathParameters.user_id.equals(client.pathParameters.user_id)){
println('message sent to ' + client.pathParameters.username)
client.getBasicRemote().sendText(message);
}
//}
}
}
//return message;
}
#OnClose
static void onClose(Session session, CloseReason closeReason) {
//******** Client Disconnect ****************
System.out.println("Disconnected ... " + session.getId());
clients.remove(session)
}
}
Bootstrap.groovy
class Bootstrap{
def init = { servletContext ->
Server server = new Server("${InetAddress.getLocalHost().getHostAddress()}", 8025, "/websockets", null, WebSocketsService);
try {
server.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
println('\n')
println('**************************** Socket running ****************************')
println("Please press a key to stop the server.");
println('\n')
reader.readLine();
} catch (Exception e) {
e.printStackTrace();
} finally {
println('\n')
println('**************************** Socket Has Been Stopped ****************************')
println('\n')
}
}
}
I created a new verticle that should response for HTTP requests and SockJS bridged events. Based on this question https://stackoverflow.com/questions/41516328 and vert.x manual https://vertx.io/docs/vertx-web/java/#_sockjs I created this piece of code:
Java:
#Override
public void start(Future<Void> startFuture) throws Exception {
startHttpServer(startFuture);
startSockJSHandler();
}
private void startHttpServer(Future<Void> startFuture) {
HttpServer server = vertx.createHttpServer(new HttpServerOptions());
server.requestHandler(req -> {
System.out.println("[" + new Date().toString() + "] Request #" + ++requestCount);
if (req.path().contains("http")) {
req.response().putHeader("Access-Control-Allow-Origin", "*").end("req_num: " + requestCount);
}
}).listen(8080, ar -> startFuture.handle(ar.mapEmpty()));
}
private void startSockJSHandler() {
Router router = Router.router(vertx);
SockJSHandlerOptions sockJSOptions = new SockJSHandlerOptions().setHeartbeatInterval(2000);
SockJSHandler sockJSHandler = SockJSHandler.create(vertx, sockJSOptions);
BridgeOptions bridgeOptions = new BridgeOptions();
bridgeOptions.addInboundPermitted(new PermittedOptions().setAddressRegex(".*")).addOutboundPermitted(new PermittedOptions().setAddressRegex(".*"));
sockJSHandler.bridge(bridgeOptions, be -> {
System.out.println("BRIDGE EVENT: " + be.type().toString());
});
router.route("/eventbus/*").handler(sockJSHandler);
}
JavaScript eventbus client:
var sock = new SockJS('http://localhost:8080/eventbus/');
sock.onopen = function() {
console.log('open');
sock.send('test');
};
sock.onmessage = function(e) {
console.log('message', e.data);
sock.close();
};
sock.onclose = function() {
console.log('close');
};
HTTP request/response works fine, but SockJS events not. In web browser 'Network' module I see only one SockJS request (http://localhost:8080/eventbus/info). 8 seconds in 'pending' status, and after this time the status is changed to 'closed' (method onclose() is called at the end).
Did I do something wrong?
The HttpServer must delegate requests to the Router. Otherwise nothing happens. Usually, it is configured to delegate all requests to the Router.
server.requestHandler(router::accept).listen(8080);
See Basic Vert.x-Web concepts in the docs.
I'm newbie to the web-socket programming...
I have the following JavaScript client code:
var connection = new WebSocket('ws://localhost:8080/OmegaThings/registerdevice');
connection.onopen = function () {
console.log("Socket has been opened state = " + connection.readyState);
connection.send('Ping'); // Send the message 'Ping' to the server
connection.send('Websocket client');
};
console.log("Socket has been opened state = " + connection.readyState);
connection.send('finish');
// 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);
};
Java endpoint:
#ServerEndpoint("/registerdevice")
public class RegisterDeviceEndPoint
{
private static final Logger LOG = Logger.getLogger(RegisterDeviceEndPoint.class.getName());
#OnOpen
public void connectionOpened()
{
LOG.log(Level.INFO, "******************connection opened**************");
}
#OnMessage
public synchronized void processMessage(Session session, String message)
{
LOG.log(Level.INFO, "received message: {0}", message);
}
#OnClose
public void connectionClosed()
{
LOG.log(Level.INFO, "connection closed");
}
}
on the firefox console I got the following output:
"Socket has been opened state = 1"
InvalidStateError: An attempt was made to use an object that is not, or is no longer, usable
"Socket has been opened state = 0"
on the GlassFish server log I got "ping" and "Websocket client", but the connection closed after onopen event exit(not sure), thus, the last word "finish" doesn't appear on the log and the error occurs.
I want to know if my code is correct?
What causes the error? javascript code, GlassFish server configuration or the java endpoint code?
Try to change the glassfish 8080 port, eg: 8887, or make sure Your antivirus/other application are not using port 80, I previously had experience where my server websocket was blocked by antivirus which using port 80.
I use websockets and Glassfish. I call start() funcion on load page. When start function contains alert it sends the message to the server but when I don't put the alert it doesn't work. I can't figure out why.
java script
function start() {
alert('a'); //this alert
webSocket.send('start_server');
}
function onMessage(event) {
document.getElementById('messages').innerHTML
+= event.data;
}
server side
#OnMessage
public void onMessage(String message, Session session)
throws IOException, InterruptedException {
System.out.println("Message recieved");
session.getBasicRemote().sendText(message);
}
#OnOpen
public void onOpen() {
System.out.println("Client connected");
}
Where do you initialize the web-socket on the client side?
The alert suspends execution until you click ok - so i guess your order of execution is wrong (initialize the websocket before webSocket.send)
You may send data after the WebSocket#open event happend.
var connection = new WebSocket(...)
connection.onopen = function () {
connection.send('Ping');
};
Source: html5rocks
I have a websocket endpoint as
#ServerEndpoint("/tweets")
public class TweetStreamServer {
private static final Logger LOGGER = LoggerFactory.getLogger(TweetStreamServer.class);
#OnMessage
public void tweets(final String message, final Session session) throws IOException, InterruptedException {
System.out.println("session id:" + session.getId() + ", search term: " + message);
final Client twitterClient = TwitterHoseBird.getInstance(message);
while (!session.getOpenSessions().isEmpty()) {
for (final Session s : session.getOpenSessions()) {
if (twitterClient.isDone()) {
System.out.println("Twitter Client Done, waiting ...");
}
s.getBasicRemote().sendText(TwitterHoseBird.getMsgQueue().take());
}
}
}
}
I deploy this on WildFly 8.1.0 Final. Then I open multiple tabs on Chrome, Safari and run the following
var connection = new WebSocket('ws://127.0.0.1:8080/tweetstream-1.0-SNAPSHOT/tweets');
connection.onopen = function () {
connection.send('germany');
};
connection.onerror = function (error) {
console.log('WebSocket Error ' + error);
};
connection.onmessage = function (e) {
console.log('Server: ' + e.data);
};
connection.onclose = function (e) {
console.log('closing session');
};
Then all the tabs start receiving data from server.
Then when I do connection.close(); on one of the tabs, only that connection breaks while all the other tabs are still receiving the data
But if I close one of the tabs (in any browser), all the sessions that were open in all the other tabs close session with closing session message
Question
- Is it not a valid use case that if user closes a tab in one browser, all the other tabs should still receive the data?
- Do you see any bug/issue with what I am doing?
- How can I fix this issue?
Thanks
Instead of using
s.getBasicRemote().sendText(TwitterHoseBird.getMsgQueue().take());
change it to
s.getAsyncRemote().sendText(TwitterHoseBird.getMsgQueue().take());
and everything else would just workout fine