When I started to learn how to do web sockets with Spring, I was able to get lot of examples. However, almost all of them were using STOMP protocol. STOMP is important that it gives structure to web sockets. However, I still wanted to understand how the basic web socket works before moving on to STOMP.
In particular, this post will showcase an example web socket using Spring and sending messages to targeted logged-in user(s).
Most of the code for this example has been taken from http://www.devglan.com/spring-boot/spring-websocket-integration-example-without-stomp.
We add the targeted user messaging on top of this.
We first configure the Spring web security allowing users to login to the application using form login. We use in memory authentication with sample users for demo purposes. Plus, we use the standard technique by having an endpoint to get the login user (and on 401 it redirects to login).
Run the application as a Spring boot app and go to the url: http://localhost:8080/app.html. I have configured 2 users - user1/pass1 and user2/pass2. Login using one of these and click the connect button.
Once logged in, we have a landing page where we have a button which allows us to connect to the web socket (this would be done from plain Javascript and the script is put inside app.html itself). This script is simple, uses plain Javascript to connect to web socket. And, if a message is sent by the server to the connected client, we simply receive the message and add it to the '#greetings' div which is defined on the landing.html. The message is simply appended to the this div, so, we can see all the message as it is sent by the server.
On the server side, we use the Spring @EnableWebSocket annotation and register a web socket handler for the endpoint '/' (this is not usually the case, as a practice, people would have something like, 'directws' or something to indicate this separately - like we usually use the 'rest' for rest endpoints).
The web socket handler extends the Spring TextWebSocketHandler class and overrides the afterConnectionEstablished method. We create a BroadcastService and pass on the session. The BroadcastService stores the session in a list.
The scenario we have used is where a server needs to 'push' messages to connected clients (about a new event, or about an email which has arrived and so on). So, I have added a REST endpoint to help us to simulate this. I call it PushController which has endpoints like 'push/broadcast/message' which is for broadcast and 'push/targeted/{user}/{message}' which is for specific user. So, we can deploy the server and simulate the push by making a REST call to this endpoint from say Postman or ARC. These end points in turn call the methods of broadcast service that we talked about above.
Let's go back to the broadcast service and examine the methods. We have a broadcastMessage method which just loops over all the available web socket sessions (which was created when a client connected) and calls sendMessage on those - which will in turn sends this message to call connected clients. This is quite simple and works well for broadcast.
The other method is what we are interested about. The targetedMessage method takes the message as well as the user name - to which the message is targeted. So, now how do we send the message to the specific user?
The WebSocketSession class has a method getPrincipal which returns the principal i.e the user who has logged in. This actually returns the same old java.security.Principal instance - which is really cool as it neatly ties with the Principal object that we are acustomed to in Java Enterprise Applications. So, I now loop over the available web socket sessions and if the principal's name (user name) matches with that of user name passed into the method, we just call the sendMessage on that session - which ensures that the message is pushed only to the user.
The demo is not very efficient, but, I hope it conveys the basic idea. We can expand on this to other situations - like pushing messages to users of a specific role for example.
The complete code is available in my github repo.
In particular, this post will showcase an example web socket using Spring and sending messages to targeted logged-in user(s).
Most of the code for this example has been taken from http://www.devglan.com/spring-boot/spring-websocket-integration-example-without-stomp.
We add the targeted user messaging on top of this.
We first configure the Spring web security allowing users to login to the application using form login. We use in memory authentication with sample users for demo purposes. Plus, we use the standard technique by having an endpoint to get the login user (and on 401 it redirects to login).
Run the application as a Spring boot app and go to the url: http://localhost:8080/app.html. I have configured 2 users - user1/pass1 and user2/pass2. Login using one of these and click the connect button.
Once logged in, we have a landing page where we have a button which allows us to connect to the web socket (this would be done from plain Javascript and the script is put inside app.html itself). This script is simple, uses plain Javascript to connect to web socket. And, if a message is sent by the server to the connected client, we simply receive the message and add it to the '#greetings' div which is defined on the landing.html. The message is simply appended to the this div, so, we can see all the message as it is sent by the server.
On the server side, we use the Spring @EnableWebSocket annotation and register a web socket handler for the endpoint '/' (this is not usually the case, as a practice, people would have something like, 'directws' or something to indicate this separately - like we usually use the 'rest' for rest endpoints).
The web socket handler extends the Spring TextWebSocketHandler class and overrides the afterConnectionEstablished method. We create a BroadcastService and pass on the session. The BroadcastService stores the session in a list.
The scenario we have used is where a server needs to 'push' messages to connected clients (about a new event, or about an email which has arrived and so on). So, I have added a REST endpoint to help us to simulate this. I call it PushController which has endpoints like 'push/broadcast/message' which is for broadcast and 'push/targeted/{user}/{message}' which is for specific user. So, we can deploy the server and simulate the push by making a REST call to this endpoint from say Postman or ARC. These end points in turn call the methods of broadcast service that we talked about above.
Let's go back to the broadcast service and examine the methods. We have a broadcastMessage method which just loops over all the available web socket sessions (which was created when a client connected) and calls sendMessage on those - which will in turn sends this message to call connected clients. This is quite simple and works well for broadcast.
The other method is what we are interested about. The targetedMessage method takes the message as well as the user name - to which the message is targeted. So, now how do we send the message to the specific user?
The WebSocketSession class has a method getPrincipal which returns the principal i.e the user who has logged in. This actually returns the same old java.security.Principal instance - which is really cool as it neatly ties with the Principal object that we are acustomed to in Java Enterprise Applications. So, I now loop over the available web socket sessions and if the principal's name (user name) matches with that of user name passed into the method, we just call the sendMessage on that session - which ensures that the message is pushed only to the user.
The demo is not very efficient, but, I hope it conveys the basic idea. We can expand on this to other situations - like pushing messages to users of a specific role for example.
The complete code is available in my github repo.
No comments:
Post a Comment