提交 01c4e458 编写于 作者: R Rossen Stoyanchev

Add support for "system" STOMP session

The "system" STOMP session is established at startup and can be used
to send messages without a client session, e.g. to support broadcasting
from a REST/HTTP handler method.
上级 44db0f81
...@@ -67,6 +67,10 @@ public class StompHeaderAccessor extends PubSubHeaderAccesssor { ...@@ -67,6 +67,10 @@ public class StompHeaderAccessor extends PubSubHeaderAccesssor {
public static final String NACK = "nack"; public static final String NACK = "nack";
public static final String LOGIN = "login";
public static final String PASSCODE = "passcode";
public static final String DESTINATION = "destination"; public static final String DESTINATION = "destination";
public static final String CONTENT_TYPE = "content-type"; public static final String CONTENT_TYPE = "content-type";
...@@ -297,6 +301,23 @@ public class StompHeaderAccessor extends PubSubHeaderAccesssor { ...@@ -297,6 +301,23 @@ public class StompHeaderAccessor extends PubSubHeaderAccesssor {
return getHeaderValue(NACK); return getHeaderValue(NACK);
} }
public void setLogin(String login) {
this.headers.put(LOGIN, login);
}
public String getLogin() {
return getHeaderValue(LOGIN);
}
public void setPasscode(String passcode) {
this.headers.put(PASSCODE, passcode);
}
public String getPasscode() {
return getHeaderValue(PASSCODE);
}
public void setReceiptId(String receiptId) { public void setReceiptId(String receiptId) {
this.headers.put(RECEIPT_ID, receiptId); this.headers.put(RECEIPT_ID, receiptId);
} }
......
...@@ -25,6 +25,7 @@ import java.util.concurrent.BlockingQueue; ...@@ -25,6 +25,7 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
import org.springframework.context.SmartLifecycle;
import org.springframework.http.MediaType; import org.springframework.http.MediaType;
import org.springframework.messaging.Message; import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel; import org.springframework.messaging.MessageChannel;
...@@ -54,7 +55,10 @@ import reactor.tcp.netty.NettyTcpClient; ...@@ -54,7 +55,10 @@ import reactor.tcp.netty.NettyTcpClient;
* @since 4.0 * @since 4.0
*/ */
@SuppressWarnings("rawtypes") @SuppressWarnings("rawtypes")
public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractPubSubMessageHandler<M> { public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractPubSubMessageHandler<M>
implements SmartLifecycle {
private static final String STOMP_RELAY_SYSTEM_SESSION_ID = "stompRelaySystemSessionId";
private MessageChannel<M> clientChannel; private MessageChannel<M> clientChannel;
...@@ -62,26 +66,22 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -62,26 +66,22 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
private MessageConverter payloadConverter; private MessageConverter payloadConverter;
private final TcpClient<String, String> tcpClient; private TcpClient<String, String> tcpClient;
private final Map<String, RelaySession> relaySessions = new ConcurrentHashMap<String, RelaySession>(); private final Map<String, RelaySession> relaySessions = new ConcurrentHashMap<String, RelaySession>();
private Object lifecycleMonitor = new Object();
private boolean running = false;
/** /**
* @param clientChannel a channel for sending messages from the remote message broker * @param clientChannel a channel for sending messages from the remote message broker
* back to clients * back to clients
*/ */
public StompRelayPubSubMessageHandler(PubSubChannelRegistry<M, ?> registry) { public StompRelayPubSubMessageHandler(PubSubChannelRegistry<M, ?> registry) {
Assert.notNull(registry, "registry is required"); Assert.notNull(registry, "registry is required");
this.clientChannel = registry.getClientOutputChannel(); this.clientChannel = registry.getClientOutputChannel();
this.tcpClient = new TcpClient.Spec<String, String>(NettyTcpClient.class)
.using(new Environment())
.codec(new DelimitedCodec<String, String>((byte) 0, true, StandardCodecs.STRING_CODEC))
.connect("127.0.0.1", 61613)
.get();
this.payloadConverter = new CompositeMessageConverter(null); this.payloadConverter = new CompositeMessageConverter(null);
} }
...@@ -94,6 +94,71 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -94,6 +94,71 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
return null; return null;
} }
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
@Override
public boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return this.running;
}
}
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
// TODO: make this configurable
this.tcpClient = new TcpClient.Spec<String, String>(NettyTcpClient.class)
.using(new Environment())
.codec(new DelimitedCodec<String, String>((byte) 0, true, StandardCodecs.STRING_CODEC))
.connect("127.0.0.1", 61613)
.get();
StompHeaderAccessor headers = StompHeaderAccessor.create(StompCommand.CONNECT);
headers.setAcceptVersion("1.1,1.2");
headers.setLogin("guest");
headers.setPasscode("guest");
headers.setHeartbeat(0, 0);
@SuppressWarnings("unchecked")
M message = (M) MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toStompMessageHeaders()).build();
RelaySession session = new RelaySession(message, headers) {
@Override
protected void sendMessageToClient(M message) {
// TODO: check for ERROR frame (reconnect?)
}
};
this.relaySessions.put(STOMP_RELAY_SYSTEM_SESSION_ID, session);
this.running = true;
}
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.running = false;
this.tcpClient.close();
}
}
@Override
public void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
@Override @Override
public void handleConnect(M message) { public void handleConnect(M message) {
StompHeaderAccessor stompHeaders = StompHeaderAccessor.wrap(message); StompHeaderAccessor stompHeaders = StompHeaderAccessor.wrap(message);
...@@ -146,10 +211,19 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -146,10 +211,19 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
StompHeaderAccessor headers = StompHeaderAccessor.wrap(message); StompHeaderAccessor headers = StompHeaderAccessor.wrap(message);
headers.setStompCommandIfNotSet(command); headers.setStompCommandIfNotSet(command);
if (headers.getSessionId() == null && (StompCommand.SEND.equals(command))) {
}
String sessionId = headers.getSessionId(); String sessionId = headers.getSessionId();
if (sessionId == null) { if (sessionId == null) {
logger.error("No sessionId in message " + message); if (StompCommand.SEND.equals(command)) {
return; sessionId = STOMP_RELAY_SYSTEM_SESSION_ID;
}
else {
logger.error("No sessionId in message " + message);
return;
}
} }
RelaySession session = this.relaySessions.get(sessionId); RelaySession session = this.relaySessions.get(sessionId);
...@@ -163,7 +237,7 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -163,7 +237,7 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
} }
private final class RelaySession { private class RelaySession {
private final String sessionId; private final String sessionId;
...@@ -236,6 +310,10 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -236,6 +310,10 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
} }
relaySessions.remove(this.sessionId); relaySessions.remove(this.sessionId);
} }
sendMessageToClient(message);
}
protected void sendMessageToClient(M message) {
clientChannel.send(message); clientChannel.send(message);
} }
...@@ -245,7 +323,7 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -245,7 +323,7 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
headers.setMessage(errorText); headers.setMessage(errorText);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
M errorMessage = (M) MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toHeaders()).build(); M errorMessage = (M) MessageBuilder.withPayload(new byte[0]).copyHeaders(headers.toHeaders()).build();
clientChannel.send(errorMessage); sendMessageToClient(errorMessage);
} }
public void forward(M message, StompHeaderAccessor headers) { public void forward(M message, StompHeaderAccessor headers) {
...@@ -309,4 +387,5 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP ...@@ -309,4 +387,5 @@ public class StompRelayPubSubMessageHandler<M extends Message> extends AbstractP
return true; return true;
} }
} }
} }
...@@ -171,7 +171,7 @@ public class PubSubHeaderAccesssor { ...@@ -171,7 +171,7 @@ public class PubSubHeaderAccesssor {
if (this.headers.get(headerName) != null) { if (this.headers.get(headerName) != null) {
return this.headers.get(headerName); return this.headers.get(headerName);
} }
else if (this.originalHeaders.get(headerName) != null) { else if ((this.originalHeaders != null) && (this.originalHeaders.get(headerName) != null)) {
return this.originalHeaders.get(headerName); return this.originalHeaders.get(headerName);
} }
return null; return null;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册