Giter Site home page Giter Site logo

nielsutrecht / spring-boot-websocket-client Goto Github PK

View Code? Open in Web Editor NEW
96.0 8.0 58.0 20 KB

Demonstrates a Spring Boot Websocket + Stomp service with both a JavaScript and Java client

License: MIT License

Java 71.73% HTML 11.82% JavaScript 16.45%
spring spring-boot websockets spring-boot-websocket spring-websockets-stomp blogs

spring-boot-websocket-client's Introduction

Spring Boot Websocket Example

In our current project we want to add a service that uses websockets to push messages to our mobile applications. While the documentation on Spring Websockets + STOMP is excellent when it comes to implementing a service that is consumed by a simple web application, the example on how to use the STOMP client doesn't really align very well with the short getting started guide. Since I hit a few snags in the implementation I'm creating this example so you don't have to.

Introduction

For the spike I'm working on now we want to have our mobile client communicate with our back end via websockets. On top of that we want to use the STOMP messaging protocol / broker since we are likely going to need a pub/sub mechanism anyway. The main difference from the Getting Started is that we don't need the SockJS compatibility layer since we control both the server and the client.

The service

Relative to the Getting Started there are a few key changes. First of all I prefer to use Lombok in my projects. You'll notice that the POJO's have generated getters and setters and the @Slf4j annotation creates a Logger for us.

Secondly I have removed the SockJS layer from the service. This is a really minor change in the endpoint configuration. Instead of this:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/hello").withSockJS();
}

You do this:

@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
    registry.addEndpoint("/hello");
}

I also added a TimeSender component that, unsurprisingly, broadcasts time updates every 5 seconds. It does this broadcast on the same '/topic/greeting' topic as the GreetingController responds on to keep the client simple. It's a Spring @Scheduled task so don't forget to enable task scheduling with @EnableScheduling in your configuration!

The rest of the service is more or less the same as the one in the Getting Started: Application is the main entry point, WebSocketConfig contains the configuration and GreetingController handled the receiving of the client's name and greeting them.

If you build the project using maven (mvn clean install) you can run the service standalone using java -jar target/spring-boot-websocket-1.0.jar. You can also run it from your IDE by simply running Application.

The JavaScript client

The only real change made on the JS client side was removing the SockJS compatibility layer. I removed the script tag loading the sockjs.js source and changed the bit where you create the client from this:

var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);

To this:

stompClient = Stomp.client('ws://localhost:8080/hello');

The Java client

The Java STOMP client was a bit less straightforward. It had me stumped (stomped?) for a while. Although I could connect to the service and send messages it would not allow me to receive any responses. Not just that; I wasn't getting any errors either. Why? Well what the documentation unfortunately does not make very clear is that the StompSessionHandlerAdapter you tend to extend has an empty implementation of handleException(). So whenever something goes wrong it just swallows the exception. That only took me like 2 hours to figure out :D. So the first order of business was to add 'proper' exception handling to MySessionHandler:

@Override
public void handleException(StompSession session, StompCommand command, StompHeaders headers, byte[] payload, Throwable exception) {
    exception.printStackTrace();
}

In my opinion this should be the default behavior. Note: you can't simply rethrow the exception here; it will disappear.

So now that we are actually seeing exceptions I found out I was doing something wrong:

org.springframework.messaging.converter.MessageConversionException: No suitable converter, payloadType=class com.nibado.example.websocket.service.Greeting, handlerType=class com.nibado.example.websocket.client.MySessionHandler
at org.springframework.messaging.simp.stomp.DefaultStompSession.invokeHandler(DefaultStompSession.java:443)

I went through different permutations of removing the StringMessageConverter, returning the Type of String inside session handler .getPayloadType(), etc. The problem was both a lack of understanding on my part and (again) unclear documentation. First of all; the STOMP 'frames' you receive will have a mime type. It's not really easy to spot (I found out by dumping all the headers to std out) but the service implementation, because we are returning Greeting objects from the controller, is using Jackson to serialize these objects to JSON. This will also result in those frames getting the application/json mimetype. The StringMessageConverter will only work on text/plain messages.

The second part that was confusing was the documentation. It claims that if Jackson is on the classpath it will use a Jackson message convertor. I don't know how it tries to figure this out but it didn't happen automatically, this is why I needed to add it myself:

stompClient.setMessageConverter(new MappingJackson2MessageConverter());

That got my basic client working: the ServiceClient configures the connection and adds a handler, MySessionHandler implements a handler that sends a message to the WS service and then subscribes to the 'topic/greeting' topic. It now will receive the same messages as the JS client does.

Conclusion

Although I do think the Spring documentation has some gaps here it was a fun journey of discovery! I hope this example is useful and save you some time. If you have any questions or feedback feel free to raise an issue in this repository!

spring-boot-websocket-client's People

Contributors

nielsutrecht avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spring-boot-websocket-client's Issues

Websocket Connection to an external Url Server

Hi dear Niels,

As I understood, you are aware of the routing path in Server side.
(session.subscribe("/topic/greetings", this))
I want to establish a websocket connection to an external websocket server url (I don't have access to the server). I have already implemented a javax.websocket ClientEndpoint which can correctly connect to the server, but I want to handle it via Spring (the persistent connection in web).

I have visited different pages like you, which simply created a client-server connection, with the specific server routes and subscriptions . But the problem is I want to just make a persistent websocket via Spring, without the knowledge from the server paths.

Automatic Reconnects

It's good to see I wasn't the only one having some real issues with Spring's STOMP client.

Have you been able to figure out how to do automatic reconnects?

I had hoped Spring would have something for this, since it's a mess to re-implement this every time I need a persistent connection to my server. But since the entire JMS based Spring "magic" (MessageListenerContainer et al) is incompatible with STOMP, I fear I'll have to do it.

Unless ofcourse, you already found an easier way :)

Need c++ client

Hi,
What c++ client would you recommend to connect to your server.

Thanks,
Samir R

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.