You can find many examples available on the web for spring websocket integration but most of them are using some messaging protocol like STOMP. Though Spring provides STOMP support out of the box but use of any messaging protocol to connect to websocket is not mandatory. You can use raw websocket by defining your own messaging protocol to connect to spring websocket using TextWebSocketHandler. In this post we will be creating a websocket connection using Spring but without STOMP and SockJs.
If you are looking for spring websocket example with STOMP and Spring Security, then visit here spring websocket example with STOMP or else here is an example of Spring boot websocket angular example
What is STOMP
STOMP stands for Streaming Text Oriented Messaging Protocol. As per wiki, STOMP is a simple text-based protocol, designed for working with message-oriented middleware (MOM). It provides an interoperable wire format that allows STOMP clients to talk with any message broker supporting the protocol.
This means when we do not have STOMP as a client, the message sent lacks of information to make Spring route it to a specific message handler method. So, if we could create a mechanism that can make Spring to route to a specific message handler then probably we can make websocket connection without STOMP.
Custom WebSocketHandler
We concluded that if we do not want to use STOMP, then we have to define our custom message protocol handler that should handle all the messages coming from any websocket client. Before defining any custom message handler class, we need to identify what format of message our handler class expect from the client. It can be either XML or Json or of any other format.
In this example we assume that the websocket client will be communicating with the server in JSON. Hence our handler will be a JSON message handler and we can define this in Spring by extending TextWebSocketHandler.java
TextWebSocketHandler
TextWebSocketHandler is a class in Spring library that extends AbstractWebSockethandler class and primarily meant for handling only text messages. So, if you are expecting any binary messages from your websocket client then defining your custom handler class by extending TextWebSocketHandler is of no use as binary messages will be rejected with CloseStatus.NOT_ACCEPTABLE.
Implementation of Spring Websocket without STOMP
While coming to the code implementation, we will be creating a simple web app using Spring websocket where message transfer between the client and server will happen via websocket without STOMP. Let us start by defining our project structure.
Project Structure
Maven Dependency
pom.xml<properties> <tomcat.version>8.0.3</tomcat.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.3.3.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> </dependency> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </dependencies>
Server Side Implementation
Let us start by defining Applicaton.java
.
@SpringBootApplication: This is a convenience annotation that is equivalent to declaring @Configuration
,@EnableAutoConfiguration
and @ComponentScan
.
package com.devglan.config; import org.springframework.boot.SpringApplication; import org.springframework.boot.context.web.SpringBootServletInitializer; @SpringBootApplication public class Application extends SpringBootServletInitializer { private static ClassapplicationClass = Application.class; public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Overriding TextWebSocketHandler
Now let us define our custom handler class by extending TextWebSocketHandler.java
that will override the protocol to handle the text message. Our handler will be capable of handling Json message. This class records all the session once any websocket connection is established and broadcasts the message to all the sessions once any message is received.
Instead of broadcasting message to all the clients, you can also configure to send message to a particular session. This configuration is also explained in coming section.
SocketHandler.classpackage com.devglan.config; import org.springframework.stereotype.Component; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; @Component public class SocketHandler extends TextWebSocketHandler { Listsessions = new CopyOnWriteArrayList<>(); @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException { for(WebSocketSession webSocketSession : sessions) { Map value = new Gson().fromJson(message.getPayload(), Map.class); webSocketSession.sendMessage(new TextMessage("Hello " + value.get("name") + " !")); } } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { //the messages will be broadcasted to all users. sessions.add(session); } }
Other Interesting Posts Maintaining Spring Session during Websocket Connection Spring Boot Websocket Example with STOMP Spring Boot JMS ActiveMQ Example Spring Boot Actuator Spring Boot Spring MVC Example Spring Boot Security Password Encoding using Bcrypt Encoder
Spring Web Socket Configurations
Now let us define our websocket configurations. The below configuration will register websocket handler at /name
and define our custom handler class that will handle all the messages from the websocket client.
package com.devglan.config; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new SocketHandler(), "/name"); } }
This is it for server side. Now let's jump to client side code.
app.jsHere we have defined 3 methods each for creating websocket connection when user clicks on connect button, sending message to the server once send button is clicked and another for listening the server response that will come from the server.
function connect() { ws = new WebSocket('ws://localhost:8080/name'); ws.onmessage = function(data){ showGreeting(data.data); } setConnected(true); } function disconnect() { if (ws != null) { ws.close(); } setConnected(false); console.log("Disconnected"); } function sendName() { ws.send($("#name").val()); } function showGreeting(message) { $("#greetings").append(""); } " + message + "
Run Application
1. Run Application.java as a java application and the main method will execute.
2. In the browser make request at http:localhost:8080.
3. Create websocket connection by clicking connect button and enter the name and click on send.
4. The handler class at server side will handle the message and return the greeting message as a response which will be shown in the browser as below:
Sending Message to Single Session
Above implementation will broadcast message to all the sessions and hence all the client will receive the messaage at a time. To send message to a single client we need to tweak our Sockethandler.java
as below.
SocketHandler.java
package com.devglan.config; import org.springframework.stereotype.Component; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; @Component public class SocketHandler extends TextWebSocketHandler { Listsessions = new CopyOnWriteArrayList<>(); @Override public void handleTextMessage(WebSocketSession session, TextMessage message) throws InterruptedException, IOException { Map value = new Gson().fromJson(message.getPayload(), Map.class); /*for(WebSocketSession webSocketSession : sessions) { Map value = new Gson().fromJson(message.getPayload(), Map.class); webSocketSession.sendMessage(new TextMessage("Hello " + value.get("name") + " !")); }*/ session.sendMessage(new TextMessage("Hello " + value.get("name") + " !")); } @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { //the messages will be broadcasted to all users. sessions.add(session); } }
This change in SocketHandler.java will ensure that the message is sent to a single session.
Conclusion
I hope this article served you that you were looking for. If you have anything that you want to add or share then please share it below in the comment section.