简体   繁体   English

Spring Websocket STOMP:发送RECEIPT帧

[英]Spring Websocket STOMP: send RECEIPT frames

I have a Websocket-stomp server based on Spring and its SimpleBroker implementation (not utilizing an external broker). 我有一个基于Spring的Websocket-stomp服务器及其SimpleBroker实现(不使用外部代理)。

I would like to enable STOMP RECEIPT messages. 我想启用STOMP RECEIPT消息。

How I could configure my code to send these automatically? 如何配置我的代码自动发送这些?

In Spring Integration test for the STOMP protocol we have this code: 在针对STOMP协议的Spring Integration测试中,我们有以下代码:

    //SimpleBrokerMessageHandler doesn't support RECEIPT frame, hence we emulate it this way
    @Bean
    public ApplicationListener<SessionSubscribeEvent> webSocketEventListener(
            final AbstractSubscribableChannel clientOutboundChannel) {
        return event -> {
            Message<byte[]> message = event.getMessage();
            StompHeaderAccessor stompHeaderAccessor = StompHeaderAccessor.wrap(message);
            if (stompHeaderAccessor.getReceipt() != null) {
                stompHeaderAccessor.setHeader("stompCommand", StompCommand.RECEIPT);
                stompHeaderAccessor.setReceiptId(stompHeaderAccessor.getReceipt());
                clientOutboundChannel.send(
                        MessageBuilder.createMessage(new byte[0], stompHeaderAccessor.getMessageHeaders()));
            }
        };
    }

https://github.com/spring-projects/spring-integration/blob/master/spring-integration-stomp/src/test/java/org/springframework/integration/stomp/inbound/StompInboundChannelAdapterWebSocketIntegrationTests.java https://github.com/spring-projects/spring-integration/blob/master/spring-integration-stomp/src/test/java/org/springframework/integration/stomp/inbound/StompInboundChannelAdapterWebSocketIntegrationTests.java

A solution similar to the post of Artem Bilan using a separated class to implement the listener. 类似于Artem Bilan的帖子的解决方案,使用分离的类来实现监听器。

@Component
public class SubscribeListener implements ApplicationListener<SessionSubscribeEvent> {
    @Autowired
    AbstractSubscribableChannel clientOutboundChannel;

    @Override
    public void onApplicationEvent(SessionSubscribeEvent event) {
        Message<byte[]> message = event.getMessage();
        StompHeaderAccessor stompHeaderAccessor = StompHeaderAccessor.wrap(message);

        if (stompHeaderAccessor.getReceipt() != null) {
            StompHeaderAccessor receipt = StompHeaderAccessor.create(StompCommand.RECEIPT);
            receipt.setReceiptId(stompHeaderAccessor.getReceipt());    
            receipt.setSessionId(stompHeaderAccessor.getSessionId());
            clientOutboundChannel.send(MessageBuilder.createMessage(new byte[0], receipt.getMessageHeaders()));
        }
    }
}

The answer provided by Artem Bilan only works for SUBSCRIBE frames. Artem Bilan提供的答案仅适用于SUBSCRIBE帧。 Here is another one that captures any incoming frame with receipt header. 这是另一个用收据标题捕获任何传入帧。 Only the class with the @EnableWebSocketMessageBroker annotation needs to be extended, other classes (like the ones with @Controller annotation) remain the same. 只需要扩展带有@EnableWebSocketMessageBroker注释的类,其他类(如带有@Controller注释的类)保持不变。

 import java.util.logging.Level; import java.util.logging.Logger; import java.util.Map; import java.util.List; import java.util.HashMap; import java.util.ArrayList; import org.springframework.context.annotation.Configuration; import org.springframework.messaging.simp.config.MessageBrokerRegistry; import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer; import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; import org.springframework.web.socket.config.annotation.StompEndpointRegistry; import org.springframework.messaging.simp.config.SimpleBrokerRegistration; import org.springframework.messaging.Message; import org.springframework.messaging.MessageHeaders; import org.springframework.messaging.support.ChannelInterceptorAdapter; import org.springframework.messaging.support.GenericMessage; import org.springframework.messaging.simp.config.ChannelRegistration; import org.springframework.messaging.simp.SimpMessageType; import org.springframework.messaging.simp.stomp.StompCommand; import org.springframework.messaging.MessageChannel; import org.springframework.beans.factory.annotation.Autowired; @Configuration @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { private static final Logger LOGGER = Logger.getLogger( WebSocketConfig.class.getName() ); private MessageChannel outChannel; @Override public void configureClientInboundChannel(ChannelRegistration registration) { registration.interceptors( new InboundMessageInterceptor() ); } @Override public void configureClientOutboundChannel(ChannelRegistration registration) { registration.interceptors( new OutboundMessageInterceptor() ); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { // prefixes are application-dependent config.enableSimpleBroker("/topic"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/note"); } class InboundMessageInterceptor extends ChannelInterceptorAdapter { @SuppressWarnings("unchecked") public Message preSend(Message message, MessageChannel channel) { LOGGER.log( Level.SEVERE, "preSend: "+message ); GenericMessage genericMessage = (GenericMessage)message; MessageHeaders headers = genericMessage.getHeaders(); String simpSessionId = (String)headers.get( "simpSessionId" ); if( ( SimpMessageType.MESSAGE.equals( headers.get( "simpMessageType" ) ) && StompCommand.SEND.equals( headers.get( "stompCommand" ) ) ) || ( SimpMessageType.SUBSCRIBE.equals( headers.get( "simpMessageType" ) ) && StompCommand.SUBSCRIBE.equals( headers.get( "stompCommand" ) ) ) && ( simpSessionId != null ) ) { Map<String,List<String>> nativeHeaders = (Map<String,List<String>>)headers.get( "nativeHeaders" ); if( nativeHeaders != null ) { List<String> receiptList = nativeHeaders.get( "receipt" ); if( receiptList != null ) { String rid = (String)receiptList.get(0); LOGGER.log( Level.SEVERE, "receipt requested: "+rid ); sendReceipt( rid, simpSessionId ); } } } return message; } private void sendReceipt( String rid, String simpSessionId ) { if( outChannel != null ) { HashMap<String,Object> rcptHeaders = new HashMap<String,Object>(); rcptHeaders.put( "simpMessageType", SimpMessageType.OTHER ); rcptHeaders.put( "stompCommand", StompCommand.RECEIPT ); rcptHeaders.put( "simpSessionId", simpSessionId ); HashMap<String,List<String>> nativeHeaders = new HashMap<String,List<String>>(); ArrayList<String> receiptElements = new ArrayList<String>(); receiptElements.add( rid ); nativeHeaders.put( "receipt-id", receiptElements ); rcptHeaders.put( "nativeHeaders",nativeHeaders ); GenericMessage<byte[]> rcptMsg = new GenericMessage<byte[]>( new byte[0],new MessageHeaders( rcptHeaders ) ); outChannel.send( rcptMsg ); } else LOGGER.log( Level.SEVERE, "receipt NOT sent" ); } } class OutboundMessageInterceptor extends ChannelInterceptorAdapter { public void postSend(Message message, MessageChannel channel, boolean sent) { LOGGER.log( Level.SEVERE, "postSend: "+message ); outChannel = channel; } } } 

Indeed, it is much more complicated than it should be and obtaining the outChannel is not very elegant. 实际上,它要比它应该复杂得多,并且获得outChannel不是很优雅。 But it works. 但它的确有效。 :-) :-)

all of the above send receipt frames too early. 所有上述过早发送收据框架。 the following gets you what you need. 以下内容可以满足您的需求。

ref: https://github.com/spring-projects/spring-framework/issues/21848 ref: https//github.com/spring-projects/spring-framework/issues/21848

@Configuration
static class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    private MessageChannel outChannel;

    @Autowired
    public WebSocketConfig(MessageChannel clientOutboundChannel) {
        this.outChannel = clientOutboundChannel;
    }

    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {

        registration.interceptors(new ExecutorChannelInterceptor() {

            @Override
            public void afterMessageHandled(Message<?> inMessage,
                    MessageChannel inChannel, MessageHandler handler, Exception ex) {

                StompHeaderAccessor inAccessor = StompHeaderAccessor.wrap(inMessage);
                String receipt = inAccessor.getReceipt();
                if (StringUtils.isEmpty(receipt)) {
                    return;
                }

                StompHeaderAccessor outAccessor = StompHeaderAccessor.create(StompCommand.RECEIPT);
                outAccessor.setSessionId(inAccessor.getSessionId());
                outAccessor.setReceiptId(receipt);
                outAccessor.setLeaveMutable(true);

                Message<byte[]> outMessage =
                        MessageBuilder.createMessage(new byte[0], outAccessor.getMessageHeaders());

                outChannel.send(outMessage);
            }
        });
    }
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM