Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

STOMP client support [SPR-11588] #16212

Closed
spring-issuemaster opened this issue Mar 22, 2014 · 16 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Mar 22, 2014

Iyad Elian opened SPR-11588 and commented

so I am trying to send a STOMP message over WebSockets using Spring and I am having problems with the code. I followed the samples and it suggests a StompMessageHandler which has a StompSession. What is that? I understand a WebSocketSession and over that I encode a StompMessage. So I do something like this instead:
WebSocketSession session = stompClient.connect(handler);
session.sendMessage(new StompMessage(StompCommand.SEND, "topic/test", "Hi"));

My StompMessage extends WebSocketMessage<String> but I can't just use that because AbstractWebSocketSessions.sendMessage checks on the types of the WebSocketMessage and it does not support a StompMessage. I can't extend TextMessage because it is final so I wrap a StompMessage with a TextMessage
session.sendMessage(new TextMessage( new StompMessage(StompCommand.SEND, "topic/test", "Hi").getBytes()));

But I get session closed because AbstractWebSocketSession says
public final void sendMessage(WebSocketMessage<?> message) throws IOException {
Assert.isTrue(isOpen(), "Cannot send message after connection closed.");

STOMP is a text protocol the js guys have couple of lines stomp.js that encodes your message and send it over websocket. Why the java mess.


Affects: 4.0.3

Sub-tasks:

  • #17411 [doc] Update reference for STOMP client

Issue Links:

  • INT-3685 Add Stomp Client Support ("is depended on by")
  • #15423 Add SockJS client support

Referenced from: commits d30b3ea

6 votes, 11 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Sébastien Deleuze commented

Hi, you should use Spring builtin STOMP support instead of trying to send your own StompMessage. Please have a look to the STOMP messaging section of the Spring reference documentation. More specifically, the Sending Messages From Anywhere section may be useful for what you try to achieve.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Iyad Elian commented

Thanks for pointing that out. still what I am trying to do is valid. my rant was about the lack of extensibility of the code. For example having TextMessage final when STOMP is text. At least that assert is a bug
Assert.isTrue(isOpen(), "Cannot send message after connection closed.");

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Rossen Stoyanchev commented

Re-opening for some further comments.

Are you looking for a Java STOMP client, i.e. one that can connect to a server in a separate process? Or are you looking to send STOMP messages from within your WebSocket server application connected clients? Is this for production code or for test code?

We don't provide a Java STOMP client currently. There is a ticket for SockJS client support (#15423), which goes along with having STOMP client support but we don't provide that. The code you're looking at is in the tests of the Stock Portfolio sample. There are no claims there that it is pretty nor that it works well. It just demonstrates how to send and receive STOMP messages from the client side. A StompSession by the way is something that allows you to send STOMP messages. If you want examples you can see them here and here.

If you're looking to send messages to connected clients from your STOMP/WebSocket server application, as Sebastien pointed out it's very simple. See the reference he provided.

Please confirm what you meant and I will update the title of this ticket accordingly.

For future reference I suggest reviewing some guidelines on how to write helpful bug reports. JIRA is not for rants.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Iyad Elian commented

yes this is for a Java STOMP client, The server is an Apollo broker between clients. if you follow the osi model session exists just above your transport which is ws or tcp. I hope StompSession does not make it into code. I am simply looking to send messages from clients as I mentioned and I was writing my own client. if you have a better way please let me know. Thanks.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Iyad Elian commented

specifically My code is doing
WebSocketSession session = stompClient.connect(handler);
if (session.isOpen()) {
session.sendMessage(new TextMessage( new StompMessage(StompCommand.SEND, "topic/test", "Hi").getBytes()));
}

The handler itself sends a message on that ws successfully afterConnected. Surely the session is not closed right after I connect, still I double check that session.isOpen() and I get:
java.lang.IllegalArgumentException: Cannot send message after connection closed.
at org.springframework.util.Assert.isTrue(Assert.java:65)
at org.springframework.web.socket.adapter.AbstractWebSocketSession.sendMessage(AbstractWebSocketSession.java:97)

since StandardWebSocketSession thinks it is.
@Override
public boolean isOpen() {
return (getNativeSession() != null && getNativeSession().isOpen());
}

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Rossen Stoyanchev commented

I wouldn't use this test code frankly. I would start with a clean slate and maybe re-use some more basic pieces.

Probably the most usable are the StompEncoder and StompDecoder which decode to and from a byte stream. We use them to send and receive STOMP messages from an external STOMP broker (like Apollo) and also to send and receive STOMP over WebSocket messages from clients. But whether you want to convert to and from our Spring Message<?> type is another question. Our support isn't necessarily focused on exposing STOMP explicitly through the API. Instead you write controllers with annotated message handling methods like here. It is a kind of Spring Integration light type architecture focused on passing a (generic) Spring Message<?> through message channels within the application. Or if an application wants to send a message to the broker, it can do so through a MessagingTemplate like here.

If interested you can see how we process STOMP over WebSocket messages in this package in both directions.

Also keep in mind there are some changes coming very soon that will allow mutliple STOMP frames in one WebSocket messages as well as one STOMP message split over multiple WebSocket messages. See this commit. This change is getting ready for the 4.0.3 release on Tuesday.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 23, 2014

Iyad Elian commented

Thanks, I will check and get back to you.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jul 19, 2014

Alex commented

Is there any news on that?

Having client side stomp support in Spring would be really, really useful

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 5, 2015

Igor Kolomiets commented

I asked Rossen if there are plans to have Java STOMP client as part of Spring Websocket here:

rstoyanchev/spring-websocket-portfolio#53

and was surprised to learn there's an open issue just for that. I really look foward to Spring 4.2 to have great Java STOMP client that we can use in our team for load testing.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 6, 2015

Anton Bessonov commented

The STOMP client from Tests is working well, but I experience following issues with it:

If I use WebSocketTransport:

transports.add(new WebSocketTransport(new StandardWebSocketClient()));

then the client is not notified about disconnects (it's crucial for my use case to reconnect, may be should be a feature for SockJS client).

With

RestTemplateXhrTransport xhrTransport = new RestTemplateXhrTransport(new RestTemplate());
xhrTransport.setRequestHeaders(headers); // <---- Header with session-cookie
transports.add(xhrTransport);

my app is notified, but I lost my authentification. This happen after Check the user of a SockJS request because (I think) of cleanup in org.springframework.security.messaging.context.SecurityContextChannelInterceptor.afterSendCompletion(Message<?>, MessageChannel, boolean, Exception). Relevant parts from log are:

00:24:16 [http-nio-8080-exec-4] XhrStreamingTransportHandler[DEBUG] - POST http://127.0.0.1:8080/portfolio/697/7d96a831a46e07f9dcc154a4634cfa37/xhr_streaming
[2x ...]
00:24:16 [http-nio-8080-exec-5] DefaultSockJsService[DEBUG] - The user for the session does not match the user for the request.
00:24:16 [SimpleAsyncTaskExecutor-1] RestTemplate[WARN] - POST request for "http://127.0.0.1:8080/portfolio/697/7d96a831a46e07f9dcc154a4634cfa37/xhr_send" resulted in 404 (Not Found); invoking error handler
        SecurityContext contextBeforeChainExecution = repo.loadContext(holder); // <----- Auth is here

        try {
            SecurityContextHolder.setContext(contextBeforeChainExecution);

            chain.doFilter(holder.getRequest(), holder.getResponse()); // <-------- Cleanup in SecurityContextChannelInterceptor.afterSendCompletion

        } finally {
            SecurityContext contextAfterChainExecution = SecurityContextHolder.getContext(); // <---- No auth anymore
            // Crucial removal of SecurityContextHolder contents - do this before anything else.
            SecurityContextHolder.clearContext();
            repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse()); // <---------- Remove session from redis
[...]

And relevant parts of session removal:

            final Authentication authentication = context.getAuthentication();
            HttpSession httpSession = request.getSession(false);

            // See SEC-776
            if (authentication == null || trustResolver.isAnonymous(authentication)) {
                if (logger.isDebugEnabled()) {
                    logger.debug("SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.");
                }

                if (httpSession != null && !contextObject.equals(contextBeforeExecution)) {
                    // SEC-1587 A non-anonymous context may still be in the session
                    // SEC-1735 remove if the contextBeforeExecution was not anonymous
                    httpSession.removeAttribute(springSecurityContextKey);      // <---------- Here!!!
                }
                return;
            }

I have tried to prepare a sample (run mvn clean spring-boot:run), but I'm not sure wether it's accuracy enough (I get 403 here).

EDIT: BTW: SecurityContextChannelInterceptor come from org.springframework.security.config.annotation.web.socket.AbstractSecurityWebSocketMessageBrokerConfigurer.securityContextChannelInterceptor() and it can't be (cleanly) overriden, because SecurityContextChannelInterceptor is final and Bean is used on other place.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 6, 2015

Rossen Stoyanchev commented

Bessonov, I fixed something recently that sounds very related. Have you seen this change? By the way if necessary we should continue the discussion under the issue tracking of the spring-websocket-portfolio project. The upcoming STOMP client will be written from scratch, rather than copying what's in the sample project tests.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Jan 31, 2015

Anton Bessonov commented

Yes, I've seen it. As I wrote, notification is depend on chosen transport implementation.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 12, 2015

Rossen Stoyanchev commented

There is a STOMP client now available in master (see commit d30b3e.)

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 26, 2015

Rossen Stoyanchev commented

Documentation added.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 27, 2015

Iyad Elian commented

Great thanks. can you please link to the docs?

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Mar 28, 2015

Rossen Stoyanchev commented

This is a (temporary) link to the 4.2 snapshot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.