我有一个基于Spring及其SimpleBroker实现的Websocket-stomp服务器(不使用外部代理)。
我想启用STOMP RECEIPT消息。
如何配置我的代码以自动发送这些代码?
答案 0 :(得分:5)
在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()));
}
};
}
答案 1 :(得分:1)
类似于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()));
}
}
}
答案 2 :(得分:0)
Artem Bilan提供的答案仅适用于SUBSCRIBE帧。这是另一个用收据标题捕获任何传入帧。只需要扩展带有@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;
}
}
}
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;
}
}
}
实际上,它要比它应该复杂得多,并且获得outChannel并不是很优雅。但它的确有效。 : - )
答案 3 :(得分:0)
以上所有内容都过早发送收据框架。 以下内容可以满足您的需求。
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);
}
});
}
}