Finished writing the WebSocket chapter for the second edition of my Java 24 Hour Trainer. In this blog I’ll show you one of the code samples from lesson 28.
Pretty often you need to write a program that publishes the same message to all connected clients. For example, multiple clients of the online auctions have to be notified when a new bid is placed on the product. Another example is when a new stock price quote need to be pushed from the server to all connected clients. With websockets it’s a pretty easy task.
I’ll show you a basic example when a WebSocket endpoint pushes the server’s time to all connected clients. If you can publish the server’s time to all connected clients, you can publish any application-specific data.
The following endpoint WebSocketClock schedules the task that gets and formats the server’s time every second and publishes the time to all connected clients. I schedule this timer once when the first client connects to our endpoint. The method sendTimeToAll() finds all connected clients by invoking getOpenSessions() on the Session object. Then on each session it calls getBasicRemote().sendText().
@ServerEndpoint("/clock") public class WebSocketClock { static ScheduledExecutorService timer = Executors.newSingleThreadScheduledExecutor(); private static Set<Session> allSessions; DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); @OnOpen public void showTime(Session session){ allSessions = session.getOpenSessions(); // start the scheduler on the very first connection // to call sendTimeToAll every second if (allSessions.size()==1){ timer.scheduleAtFixedRate( () -> sendTimeToAll(session),0,1,TimeUnit.SECONDS); } } private void sendTimeToAll(Session session){ allSessions = session.getOpenSessions(); for (Session sess: allSessions){ try{ sess.getBasicRemote().sendText("Local time: " + LocalTime.now().format(timeFormatter)); } catch (IOException ioe) { System.out.println(ioe.getMessage()); } } } }
The Web client is pretty simple. On the page load the JavaScript code connects to the WebSocket endpoint on the server. The callback onMessage() is invoked when the message (current time) arrives. In this callback I update the content of the span HTML element with the current time. Since my Java code will send the message every second, the span content updates with this frequency. No page refreshes, no heavy HTTP headers, no AJAX long polling hacks either.
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> <span id="messageGoesHere"></span> <script type="text/javascript"> var ws = new WebSocket("ws://localhost:8080/Lesson28/clock"); ws.onmessage = function(event) { var mySpan = document.getElementById("messageGoesHere"); mySpan.innerHTML=event.data; }; ws.onerror = function(event){ console.log("Error ", event) } </script> </body> </html>
The next screenshot shows how Eclipse internal browser, Chrome, and Firefox display the current time published by my WebSocket endpoint.
Three Web clients get current time published by a WebSocket endpoint every second.
Iterating through all open sessions works fine if the number of connected clients is small. But if you have hundreds of clients, consider grouping the Session objects into separate collections in @OnOpen message handler, and sending messages to each group in parallel from multiple threads. Important: by default, a Java EE server creates a new instance of the server endpoint class for each client’s connection, so if you’ll be creating your own session collections they must be static:
private static Set<Session> sessionsChunk1 = Collections.synchronizedSet(new HashSet<>()); private static Set<Session> sessionsChunk2 = Collections.synchronizedSet(new HashSet<>());
Thank you for this belated Christmas present!
May all your New Year dreams come true!
The well known Messaging Publish/Subscribe pattern usually implemented with JMS Topic but now the “web-way”: mixed Java and JavaScript over HTTP with WebSocket…
Perfect as a tutorial to learn how it works. Now to build a productive server, I would use a JMS implementation using STOMP over WebSocket, like ActiveMQ or HornetQ: http://jmesnil.net/stomp-websocket/doc/
Sure,
WebSocket is a raw protocol and while developing apps you’d need to consider some subprotocols.
JMS is great, but it requires an infrastructure to be in place (MOM servers).
In this blog i wanted to show the mechanism of pushing the data from the server. How to structure the data being sent over the socket is up to the developers.
BTW, Kaazing has various commercial offerings including JMS over WebSocket support.
I wish you would do the Java online training with consultations again, to include all these new subjects. Anyway, I’m looking forward to the second edition of your book!
I use Wildfly as web server. I have problem with using WebSocket and Servlet at the same time. Is it generally correct?
Hello, Pavlo.
Your problem sounds interesting.
What kind of Servlet? HttpServlet?
Could you be more specific about your problem?
Hello, Cristina
I solved my problem. It is not Websocket and Servlet using at the same time.
I show example: http://server-eacloud.rhcloud.com/EAserver/e?rid=1428301965979&t=load
This example work fine. The problem is Openshift cloud Websocket service.
Pavlo, thank you for answering.
I haven’t had the time to try Openshift yet, but I was planning to. I’ll be warned about the Websocket service now, thanks to you.
I’d be interested to see your example, if you had it on Github.
If you want your servlet to process WebSocket or other protocols, you can use the method upgrade() on the HttpServletRequest (see JSR 340).
Hi, I did it as you said , but only have the blank page. no errors in console .
Nice job, however I do not know Java. . I am using node.js with WS websockets. How does your client know the specific web browsers? I am stuck trying to figure out how to get each webpage to display the number of users of the webpage. There is only one webpage in my application. I need it to display how may people are browsing the page. Any ideas on how I can do this?
I do not know node.js, but in Java I used: sess.getBasicRemote().sendText(“Local time: ” +
LocalTime.now().format(timeFormatter) + “, Connected users: ” + allSessions.size());
Worked for me (http://mywildflyapp-fierbinteanu.rhcloud.com/).
I’m very happy I finally got this working at OpenShift. However, it looks like the browser, or the firewall, or the proxy settings can block the websockets. I was wondering if and how my page could give the user a meaningful message in this situation (e.g. ‘Please enable websockets to use this application’).
On a localhost base or one subnet this works perfectly…but how do you establish a real web socket connection through Java and Js over the internet…I am stuck at the moment…i have no idea how to build a connection to clients inside their subnets…If you have any idea I’d be really thankful.
Hello,
Could you please share the source code, as I am trying to implement the same but it is not working….
Thank you in advance
There is a reference to the book on top of this blog. The code is here: https://github.com/yfain/java24hourtrainer2ndedition
Hi, how would you solve a system that needs to send a message to all clients at the same moment in a clustered environment?