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

1

19

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?

Morty answered 8/8, 2016 at 0:37 Comment(0)
M
1

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

Morty answered 22/10, 2019 at 16:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.