Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Connecting to Stomp over WebSockets implemented using Spring 4 from Java client

I received a Web application, implemented with Spring using STOMP over WebSockets Messaging, similar to what's described here (with RabbitMQ at the backend). It runs on Tomcat, and I can connect to the application using regular URLs (e.g. http://host/server/). I was also given a demo client - a JSPX page, which uses Modernizr WebSockets and SockJS. The connection code in the demo client looks like this:

if (Modernizr.websockets) {
    socket = new WebSocket('ws://host/server/endpointx');
}
else {
    socket = new SockJS('/server/endpointy');
}

stompClient = Stomp.over(socket);
stompClient.connect(headers, function(frame) { ... });
....

Demo client works fine (when I load JSPX page in the browser, I can connect to the server). But my goal is to connect to the same server using some Java STOMP library. Problem is, the libraries I tried, require host and port as connection parameters: For example with ActiveMQ Stomp library:

StompConnection connection = new StompConnection();
connection.open("host", port);
connection.connect(new HashMap<String, String>());

If I specify port as 61613, connection succeeds, but I hit RabbitMQ directly, not my server (this is not what I want). If I specify 8080 (or 80), on connection I get an error

java.io.EOFException at java.io.DataInputStream.readByte(Unknown Source) at org.apache.activemq.transport.stomp.StompWireFormat.unmarshal(StompWireFormat.java:137) at org.apache.activemq.transport.stomp.StompConnection.receive(StompConnection.java:77) at org.apache.activemq.transport.stomp.StompConnection.receive(StompConnection.java:68) at org.apache.activemq.transport.stomp.StompConnection.connect(StompConnection.java:139) at ....stomp.impl.activemq.ActiveMQStompDriver.connect(ActiveMQStompDriver.java:39) ... 25 more

and trace shows that this is because CONNECT frame never receives an expected CONNECTED frame (in fact, it doesn't receive anything back).

So I'm puzzled: am I using a wrong port? or dealing with some library incompatibility? or do I need to somehow indicate Tomcat that I want to upgrade HTTP connection to WebSockets?

If the above question is difficult to answer, then this one is equally useful: how do I connect to Spring application with STOMP over WebSockets messaging channel running on Tomcat using Java?

like image 751
ytrewq Avatar asked Aug 08 '16 00:08

ytrewq


People also ask

How do you use WebSockets in Spring?

In order to tell Spring to forward client requests to the endpoint , we need to register the handler. Start the application- Go to http://localhost:8080 Click on start new chat it opens the WebSocket connection. Type text in the textbox and click send. On clicking end chat, the WebSocket connection will be closed.

How do I use STOMP WebSocket?

You need to start a STOMP server with support for WebSocket (using for example HornetQ). Click on the Connect button to connect to the server and subscribe to the /queue/test/ queue. You can then type messages in the form at the bottom of the page to send STOMP messages to the queue.


1 Answers

I should have posted an answer when I found it. In order to implement Java client, connecting to Tomcat, and not to RabbitMQ directly, the Java client should not only implement STOMP Over WebSockets, but also an opening handshake:

The opening handshake is intended to be compatible with HTTP-based server-side software and intermediaries, so that a single port can be used by both HTTP clients talking to that server and WebSocket clients talking to that server. To this end, the WebSocket client's handshake is an HTTP Upgrade request

The libraries I tried to use were simply missing this capability. They tried to directly perform WebSocket connection without the handshake.

So to answer my own questions:

  • Library must support opening handshake
  • For host and port you need to specify Tomcat's host (of not the same as RabbitMQ), and web port (e.g. 8080), not RabbitMQ port

I ended up using Spring WebSocket library, which allows all that quite easily. Here's the simplest setup:

taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(N); // N = number of threads
taskScheduler.afterPropertiesSet();

stompClient = new WebSocketStompClient(new StandardWebSocketClient());
stompClient.setTaskScheduler(taskScheduler);
stompClient.setMessageConverter(new MappingJackson2MessageConverter()); 

WebSocketHttpHeaders handshakeHeaders = new WebSocketHttpHeaders(); // you can add additional headers here for Tomcat

// Here's the main piece other libraries were missing:
// The following request will perform both, handshake and connection
// hence it gets both sets of headers (for Tomcat and for Stomp)
ListenableFuture<StompSession> future = stompClient.connect(
            url,
            handshakeHeaders,
            new StompHeaders(),
            new CustomStompSessionHandlerAdapter(this)); // called from class that also was implementing CustomStompSessionHandlerAdapter, but this could be separate class as well.

Also see WebSocket Support section in Spring reference

like image 79
ytrewq Avatar answered Oct 19 '22 18:10

ytrewq