Here are the examples of the java api org.springframework.messaging.SubscribableChannel taken from open source projects. By voting up you can indicate which examples are most useful and appropriate.
84 Examples
19
View Source File : WebMvcStompEndpointRegistryTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Before
public void setup() {
SubscribableChannel inChannel = mock(SubscribableChannel.clreplaced);
SubscribableChannel outChannel = mock(SubscribableChannel.clreplaced);
this.webSocketHandler = new SubProtocolWebSocketHandler(inChannel, outChannel);
WebSocketTransportRegistration transport = new WebSocketTransportRegistration();
TaskScheduler scheduler = mock(TaskScheduler.clreplaced);
this.endpointRegistry = new WebMvcStompEndpointRegistry(this.webSocketHandler, transport, scheduler);
}
19
View Source File : UserDestinationMessageHandlerTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* Unit tests for
* {@link org.springframework.messaging.simp.user.UserDestinationMessageHandler}.
*/
public clreplaced UserDestinationMessageHandlerTests {
private static final String SESSION_ID = "123";
private UserDestinationMessageHandler handler;
private SimpUserRegistry registry;
private SubscribableChannel brokerChannel;
@Before
public void setup() {
this.registry = mock(SimpUserRegistry.clreplaced);
this.brokerChannel = mock(SubscribableChannel.clreplaced);
UserDestinationResolver resolver = new DefaultUserDestinationResolver(this.registry);
this.handler = new UserDestinationMessageHandler(new StubMessageChannel(), this.brokerChannel, resolver);
}
@Test
public void handleSubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", SESSION_ID, "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertEquals("/queue/foo-user123", SimpMessageHeaderAccessor.getDestination(message.getHeaders()));
}
@Test
public void handleUnsubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.UNSUBSCRIBE, "joe", "123", "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertEquals("/queue/foo-user123", SimpMessageHeaderAccessor.getDestination(message.getHeaders()));
}
@Test
public void handleMessage() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
given(this.registry.getUser("joe")).willReturn(simpUser);
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertEquals("/queue/foo-user123", accessor.getDestination());
replacedertEquals("/user/queue/foo", accessor.getFirstNativeHeader(ORIGINAL_DESTINATION));
}
@Test
public void handleMessageWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
replacedertEquals("/topic/unresolved", accessor.getDestination());
replacedertEquals("/user/joe/queue/foo", accessor.getFirstNativeHeader(ORIGINAL_DESTINATION));
// Should ignore our own broadcast to brokerChannel
this.handler.handleMessage(message);
Mockito.verifyNoMoreInteractions(this.brokerChannel);
}
@Test
public void handleMessageFromBrokerWithActiveSession() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
given(this.registry.getUser("joe")).willReturn(simpUser);
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setNativeHeader("customHeader", "customHeaderValue");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
replacedertNotNull(captor.getValue());
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertEquals("/queue/foo-user123", headers.getDestination());
replacedertEquals("/user/queue/foo", headers.getFirstNativeHeader(ORIGINAL_DESTINATION));
replacedertEquals("customHeaderValue", headers.getFirstNativeHeader("customHeader"));
replacedertArrayEquals(payload, (byte[]) captor.getValue().getPayload());
}
@Test
public void handleMessageFromBrokerWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
// No re-broadcast
verifyNoMoreInteractions(this.brokerChannel);
}
@Test
public void ignoreMessage() {
// no destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", null));
Mockito.verifyZeroInteractions(this.brokerChannel);
// not a user destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
// subscribe + not a user destination
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", "123", "/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
// no match on message type
this.handler.handleMessage(createWith(SimpMessageType.CONNECT, "joe", "123", "user/joe/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
}
private Message<?> createWith(SimpMessageType type, String user, String sessionId, String destination) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);
if (destination != null) {
headers.setDestination(destination);
}
if (user != null) {
headers.setUser(new TestPrincipal(user));
}
if (sessionId != null) {
headers.setSessionId(sessionId);
}
return MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
}
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Test
public void sendAndReceiveVariableTimeoutCustomHeaders() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setSendTimeout(20_000);
this.template.setReceiveTimeout(10_000);
this.template.setThrowExceptionOnLateReply(true);
this.template.setSendTimeoutHeader("sto");
this.template.setReceiveTimeoutHeader("rto");
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
willAnswer(invocation -> {
this.executor.execute(() -> handler.handleMessage(invocation.getArgument(0)));
return true;
}).given(channel).send(any(Message.clreplaced), anyLong());
Message<?> message = MessageBuilder.withPayload("request").setHeader("sto", 30_000L).setHeader("rto", 1L).build();
replacedertNull(this.template.sendAndReceive(channel, message));
replacedertTrue(latch.await(10_000, TimeUnit.MILLISECONDS));
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Test
public void sendWithTimeoutMutable() {
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
final AtomicReference<Message<?>> sent = new AtomicReference<>();
willAnswer(invocation -> {
sent.set(invocation.getArgument(0));
return true;
}).given(channel).send(any(Message.clreplaced), eq(30_000L));
MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setLeaveMutable(true);
Message<?> message = new GenericMessage<>("request", accessor.getMessageHeaders());
accessor.setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L);
this.template.send(channel, message);
verify(channel).send(any(Message.clreplaced), eq(30_000L));
replacedertNotNull(sent.get());
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER));
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Test
public void sendAndReceiveTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setReceiveTimeout(1);
this.template.setSendTimeout(30_000L);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
willAnswer(invocation -> {
this.executor.execute(() -> handler.handleMessage(invocation.getArgument(0)));
return true;
}).given(channel).send(any(Message.clreplaced), anyLong());
replacedertNull(this.template.convertSendAndReceive(channel, "request", String.clreplaced));
replacedertTrue(latch.await(10_000, TimeUnit.MILLISECONDS));
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Test
public void sendAndReceiveVariableTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setSendTimeout(20_000);
this.template.setReceiveTimeout(10_000);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
willAnswer(invocation -> {
this.executor.execute(() -> handler.handleMessage(invocation.getArgument(0)));
return true;
}).given(channel).send(any(Message.clreplaced), anyLong());
Message<?> message = MessageBuilder.withPayload("request").setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L).setHeader(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER, 1L).build();
replacedertNull(this.template.sendAndReceive(channel, message));
replacedertTrue(latch.await(10_000, TimeUnit.MILLISECONDS));
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Test
public void sendAndReceive() {
SubscribableChannel channel = new ExecutorSubscribableChannel(this.executor);
channel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
replyChannel.send(new GenericMessage<>("response"));
}
});
String actual = this.template.convertSendAndReceive(channel, "request", String.clreplaced);
replacedertEquals("response", actual);
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Test
public void sendWithTimeout() {
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
final AtomicReference<Message<?>> sent = new AtomicReference<>();
willAnswer(invocation -> {
sent.set(invocation.getArgument(0));
return true;
}).given(channel).send(any(Message.clreplaced), eq(30_000L));
Message<?> message = MessageBuilder.withPayload("request").setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L).setHeader(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER, 1L).build();
this.template.send(channel, message);
verify(channel).send(any(Message.clreplaced), eq(30_000L));
replacedertNotNull(sent.get());
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER));
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER));
}
19
View Source File : UserDestinationMessageHandler.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* {@code MessageHandler} with support for "user" destinations.
*
* <p>Listens for messages with "user" destinations, translates their destination
* to actual target destinations unique to the active session(s) of a user, and
* then sends the resolved messages to the broker channel to be delivered.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public clreplaced UserDestinationMessageHandler implements MessageHandler, SmartLifecycle {
private static final Log logger = SimpLogging.forLogName(UserDestinationMessageHandler.clreplaced);
private final SubscribableChannel clientInboundChannel;
private final SubscribableChannel brokerChannel;
private final UserDestinationResolver destinationResolver;
private final MessageSendingOperations<String> messagingTemplate;
@Nullable
private BroadcastHandler broadcastHandler;
@Nullable
private MessageHeaderInitializer headerInitializer;
private volatile boolean running = false;
private final Object lifecycleMonitor = new Object();
/**
* Create an instance with the given client and broker channels subscribing
* to handle messages from each and then sending any resolved messages to the
* broker channel.
* @param clientInboundChannel messages received from clients.
* @param brokerChannel messages sent to the broker.
* @param resolver the resolver for "user" destinations.
*/
public UserDestinationMessageHandler(SubscribableChannel clientInboundChannel, SubscribableChannel brokerChannel, UserDestinationResolver resolver) {
replacedert.notNull(clientInboundChannel, "'clientInChannel' must not be null");
replacedert.notNull(brokerChannel, "'brokerChannel' must not be null");
replacedert.notNull(resolver, "resolver must not be null");
this.clientInboundChannel = clientInboundChannel;
this.brokerChannel = brokerChannel;
this.messagingTemplate = new SimpMessagingTemplate(brokerChannel);
this.destinationResolver = resolver;
}
/**
* Return the configured {@link UserDestinationResolver}.
*/
public UserDestinationResolver getUserDestinationResolver() {
return this.destinationResolver;
}
/**
* Set a destination to broadcast messages to that remain unresolved because
* the user is not connected. In a multi-application server scenario this
* gives other application servers a chance to try.
* <p>By default this is not set.
* @param destination the target destination.
*/
public void setBroadcastDestination(@Nullable String destination) {
this.broadcastHandler = (StringUtils.hasText(destination) ? new BroadcastHandler(this.messagingTemplate, destination) : null);
}
/**
* Return the configured destination for unresolved messages.
*/
@Nullable
public String getBroadcastDestination() {
return (this.broadcastHandler != null ? this.broadcastHandler.getBroadcastDestination() : null);
}
/**
* Return the messaging template used to send resolved messages to the
* broker channel.
*/
public MessageSendingOperations<String> getBrokerMessagingTemplate() {
return this.messagingTemplate;
}
/**
* Configure a custom {@link MessageHeaderInitializer} to initialize the
* headers of resolved target messages.
* <p>By default this is not set.
*/
public void setHeaderInitializer(@Nullable MessageHeaderInitializer headerInitializer) {
this.headerInitializer = headerInitializer;
}
/**
* Return the configured header initializer.
*/
@Nullable
public MessageHeaderInitializer getHeaderInitializer() {
return this.headerInitializer;
}
@Override
public final void start() {
synchronized (this.lifecycleMonitor) {
this.clientInboundChannel.subscribe(this);
this.brokerChannel.subscribe(this);
this.running = true;
}
}
@Override
public final void stop() {
synchronized (this.lifecycleMonitor) {
this.running = false;
this.clientInboundChannel.unsubscribe(this);
this.brokerChannel.unsubscribe(this);
}
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
@Override
public final boolean isRunning() {
return this.running;
}
@Override
public void handleMessage(Message<?> message) throws MessagingException {
Message<?> messageToUse = message;
if (this.broadcastHandler != null) {
messageToUse = this.broadcastHandler.preHandle(message);
if (messageToUse == null) {
return;
}
}
UserDestinationResult result = this.destinationResolver.resolveDestination(messageToUse);
if (result == null) {
return;
}
if (result.getTargetDestinations().isEmpty()) {
if (logger.isTraceEnabled()) {
logger.trace("No active sessions for user destination: " + result.getSourceDestination());
}
if (this.broadcastHandler != null) {
this.broadcastHandler.handleUnresolved(messageToUse);
}
return;
}
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(messageToUse);
initHeaders(accessor);
accessor.setNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION, result.getSubscribeDestination());
accessor.setLeaveMutable(true);
messageToUse = MessageBuilder.createMessage(messageToUse.getPayload(), accessor.getMessageHeaders());
if (logger.isTraceEnabled()) {
logger.trace("Translated " + result.getSourceDestination() + " -> " + result.getTargetDestinations());
}
for (String target : result.getTargetDestinations()) {
this.messagingTemplate.send(target, messageToUse);
}
}
private void initHeaders(SimpMessageHeaderAccessor headerAccessor) {
if (getHeaderInitializer() != null) {
getHeaderInitializer().initHeaders(headerAccessor);
}
}
@Override
public String toString() {
return "UserDestinationMessageHandler[" + this.destinationResolver + "]";
}
/**
* A handler that broadcasts locally unresolved messages to the broker and
* also handles similar broadcasts received from the broker.
*/
private static clreplaced BroadcastHandler {
private static final List<String> NO_COPY_LIST = Arrays.asList("subscription", "message-id");
private final MessageSendingOperations<String> messagingTemplate;
private final String broadcastDestination;
public BroadcastHandler(MessageSendingOperations<String> template, String destination) {
this.messagingTemplate = template;
this.broadcastDestination = destination;
}
public String getBroadcastDestination() {
return this.broadcastDestination;
}
@Nullable
public Message<?> preHandle(Message<?> message) throws MessagingException {
String destination = SimpMessageHeaderAccessor.getDestination(message.getHeaders());
if (!getBroadcastDestination().equals(destination)) {
return message;
}
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.getAccessor(message, SimpMessageHeaderAccessor.clreplaced);
replacedert.state(accessor != null, "No SimpMessageHeaderAccessor");
if (accessor.getSessionId() == null) {
// Our own broadcast
return null;
}
destination = accessor.getFirstNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION);
if (logger.isTraceEnabled()) {
logger.trace("Checking unresolved user destination: " + destination);
}
SimpMessageHeaderAccessor newAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
for (String name : accessor.toNativeHeaderMap().keySet()) {
if (NO_COPY_LIST.contains(name)) {
continue;
}
newAccessor.setNativeHeader(name, accessor.getFirstNativeHeader(name));
}
if (destination != null) {
newAccessor.setDestination(destination);
}
// ensure send doesn't block
newAccessor.setHeader(SimpMessageHeaderAccessor.IGNORE_ERROR, true);
return MessageBuilder.createMessage(message.getPayload(), newAccessor.getMessageHeaders());
}
public void handleUnresolved(Message<?> message) {
MessageHeaders headers = message.getHeaders();
if (SimpMessageHeaderAccessor.getFirstNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION, headers) != null) {
// Re-broadcast
return;
}
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
String destination = accessor.getDestination();
accessor.setNativeHeader(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION, destination);
accessor.setLeaveMutable(true);
message = MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
if (logger.isTraceEnabled()) {
logger.trace("Translated " + destination + " -> " + getBroadcastDestination());
}
this.messagingTemplate.send(getBroadcastDestination(), message);
}
}
}
19
View Source File : MessageBrokerRegistry.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Nullable
protected SimpleBrokerMessageHandler getSimpleBroker(SubscribableChannel brokerChannel) {
if (this.simpleBrokerRegistration == null && this.brokerRelayRegistration == null) {
enableSimpleBroker();
}
if (this.simpleBrokerRegistration != null) {
SimpleBrokerMessageHandler handler = this.simpleBrokerRegistration.getMessageHandler(brokerChannel);
handler.setPathMatcher(this.pathMatcher);
handler.setCacheLimit(this.cacheLimit);
handler.setPreservePublishOrder(this.preservePublishOrder);
return handler;
}
return null;
}
19
View Source File : MessageBrokerRegistry.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Nullable
protected StompBrokerRelayMessageHandler getStompBrokerRelay(SubscribableChannel brokerChannel) {
if (this.brokerRelayRegistration != null) {
StompBrokerRelayMessageHandler relay = this.brokerRelayRegistration.getMessageHandler(brokerChannel);
relay.setPreservePublishOrder(this.preservePublishOrder);
return relay;
}
return null;
}
19
View Source File : PubSubChannelAdaptersIntegrationTests.java
License : Apache License 2.0
Project Creator : spring-cloud
License : Apache License 2.0
Project Creator : spring-cloud
// If this test flakes, delete it.
// It verifies that in AUTO_ACK mode, the message is neither acked nor nacked, and that
// redelivery happens after subscription's ackDeadline preplacedes.
// There is also a client library bug (https://github.com/googleapis/java-pubsub/issues/141) that
// results in ackDeadline being extended by 60 seconds even when maxAckExtensionPeriod is zero,
// making minimum redelivery time is ackDeadline + 60.
@Test
public void sendAndReceiveMessageAutoAckWithFailure() {
this.contextRunner.withUserConfiguration(SubscribableConfiguration.clreplaced, CommonConfiguration.clreplaced).withPropertyValues("spring.cloud.gcp.pubsub.subscriber.max-ack-extension-period=0").run((context) -> {
context.getBean(PubSubInboundChannelAdapter.clreplaced).setAckMode(AckMode.AUTO_ACK);
context.getBean("inputChannel", MessageChannel.clreplaced).send(MessageBuilder.withPayload("This message is in trouble.".getBytes()).build());
SubscribableChannel channel = context.getBean("outputChannel", SubscribableChannel.clreplaced);
AtomicInteger numReceivedMessages = new AtomicInteger(0);
channel.subscribe(msg -> {
if (numReceivedMessages.incrementAndGet() == 1) {
throw new RuntimeException("BOOM!");
}
});
// wait for initial delivery
Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> numReceivedMessages.get() > 0);
replacedertThat(numReceivedMessages.get()).isEqualTo(1);
// Expect redelivery after at least 10 seconds but within 1.5 minutes:
// 10 seconds subscription ackDeadline
// + 60 seconds https://github.com/googleapis/java-pubsub/issues/141
// + 20 seconds anti-flake buffer
Awaitility.await().atLeast(9, TimeUnit.SECONDS).atMost(90, TimeUnit.SECONDS).until(() -> numReceivedMessages.get() > 1);
replacedertThat(numReceivedMessages.get()).isEqualTo(2);
});
}
19
View Source File : WebMvcStompEndpointRegistryTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@BeforeEach
public void setup() {
SubscribableChannel inChannel = mock(SubscribableChannel.clreplaced);
SubscribableChannel outChannel = mock(SubscribableChannel.clreplaced);
this.webSocketHandler = new SubProtocolWebSocketHandler(inChannel, outChannel);
WebSocketTransportRegistration transport = new WebSocketTransportRegistration();
TaskScheduler scheduler = mock(TaskScheduler.clreplaced);
this.endpointRegistry = new WebMvcStompEndpointRegistry(this.webSocketHandler, transport, scheduler);
}
19
View Source File : UserDestinationMessageHandlerTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
/**
* Unit tests for {@link UserDestinationMessageHandler}.
*/
clreplaced UserDestinationMessageHandlerTests {
private static final String SESSION_ID = "123";
private final SimpUserRegistry registry = mock(SimpUserRegistry.clreplaced);
private final SubscribableChannel brokerChannel = mock(SubscribableChannel.clreplaced);
private final UserDestinationMessageHandler handler = new UserDestinationMessageHandler(new StubMessageChannel(), this.brokerChannel, new DefaultUserDestinationResolver(this.registry));
@Test
@SuppressWarnings("rawtypes")
void handleSubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", SESSION_ID, "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertThat(SimpMessageHeaderAccessor.getDestination(message.getHeaders())).isEqualTo("/queue/foo-user123");
}
@Test
@SuppressWarnings("rawtypes")
void handleUnsubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.UNSUBSCRIBE, "joe", "123", "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertThat(SimpMessageHeaderAccessor.getDestination(message.getHeaders())).isEqualTo("/queue/foo-user123");
}
@Test
@SuppressWarnings("rawtypes")
void handleMessage() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
given(this.registry.getUser("joe")).willReturn(simpUser);
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertThat(accessor.getDestination()).isEqualTo("/queue/foo-user123");
replacedertThat(accessor.getFirstNativeHeader(ORIGINAL_DESTINATION)).isEqualTo("/user/queue/foo");
}
@Test
@SuppressWarnings("rawtypes")
void handleMessageWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
replacedertThat(accessor.getDestination()).isEqualTo("/topic/unresolved");
replacedertThat(accessor.getFirstNativeHeader(ORIGINAL_DESTINATION)).isEqualTo("/user/joe/queue/foo");
// Should ignore our own broadcast to brokerChannel
this.handler.handleMessage(message);
Mockito.verifyNoMoreInteractions(this.brokerChannel);
}
@Test
@SuppressWarnings("rawtypes")
void handleMessageFromBrokerWithActiveSession() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
given(this.registry.getUser("joe")).willReturn(simpUser);
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setNativeHeader("customHeader", "customHeaderValue");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
replacedertThat(captor.getValue()).isNotNull();
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertThat(headers.getDestination()).isEqualTo("/queue/foo-user123");
replacedertThat(headers.getFirstNativeHeader(ORIGINAL_DESTINATION)).isEqualTo("/user/queue/foo");
replacedertThat(headers.getFirstNativeHeader("customHeader")).isEqualTo("customHeaderValue");
replacedertThat((byte[]) captor.getValue().getPayload()).isEqualTo(payload);
}
@Test
void handleMessageFromBrokerWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
// No re-broadcast
verifyNoMoreInteractions(this.brokerChannel);
}
@Test
void ignoreMessage() {
// no destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", null));
verifyNoInteractions(this.brokerChannel);
// not a user destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/queue/foo"));
verifyNoInteractions(this.brokerChannel);
// subscribe + not a user destination
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", "123", "/queue/foo"));
verifyNoInteractions(this.brokerChannel);
// no match on message type
this.handler.handleMessage(createWith(SimpMessageType.CONNECT, "joe", "123", "user/joe/queue/foo"));
verifyNoInteractions(this.brokerChannel);
}
private Message<?> createWith(SimpMessageType type, String user, String sessionId, String destination) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);
if (destination != null) {
headers.setDestination(destination);
}
if (user != null) {
headers.setUser(new TestPrincipal(user));
}
if (sessionId != null) {
headers.setSessionId(sessionId);
}
return MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
}
}
19
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@Test
public void sendAndReceive() {
SubscribableChannel channel = new ExecutorSubscribableChannel(this.executor);
channel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
replyChannel.send(new GenericMessage<>("response"));
}
});
String actual = this.template.convertSendAndReceive(channel, "request", String.clreplaced);
replacedertThat(actual).isEqualTo("response");
}
19
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@Test
public void sendWithTimeoutMutable() {
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
final AtomicReference<Message<?>> sent = new AtomicReference<>();
willAnswer(invocation -> {
sent.set(invocation.getArgument(0));
return true;
}).given(channel).send(any(Message.clreplaced), eq(30_000L));
MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setLeaveMutable(true);
Message<?> message = new GenericMessage<>("request", accessor.getMessageHeaders());
accessor.setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L);
this.template.send(channel, message);
verify(channel).send(any(Message.clreplaced), eq(30_000L));
replacedertThat(sent.get()).isNotNull();
replacedertThat(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER)).isFalse();
replacedertThat(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER)).isFalse();
}
19
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@Test
public void sendWithTimeout() {
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
final AtomicReference<Message<?>> sent = new AtomicReference<>();
willAnswer(invocation -> {
sent.set(invocation.getArgument(0));
return true;
}).given(channel).send(any(Message.clreplaced), eq(30_000L));
Message<?> message = MessageBuilder.withPayload("request").setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L).setHeader(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER, 1L).build();
this.template.send(channel, message);
verify(channel).send(any(Message.clreplaced), eq(30_000L));
replacedertThat(sent.get()).isNotNull();
replacedertThat(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER)).isFalse();
replacedertThat(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER)).isFalse();
}
19
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@Test
public void sendAndReceiveVariableTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setSendTimeout(20_000);
this.template.setReceiveTimeout(10_000);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
willAnswer(invocation -> {
this.executor.execute(() -> handler.handleMessage(invocation.getArgument(0)));
return true;
}).given(channel).send(any(Message.clreplaced), anyLong());
Message<?> message = MessageBuilder.withPayload("request").setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L).setHeader(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER, 1L).build();
replacedertThat(this.template.sendAndReceive(channel, message)).isNull();
replacedertThat(latch.await(10_000, TimeUnit.MILLISECONDS)).isTrue();
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@Test
public void sendAndReceiveVariableTimeoutCustomHeaders() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setSendTimeout(20_000);
this.template.setReceiveTimeout(10_000);
this.template.setThrowExceptionOnLateReply(true);
this.template.setSendTimeoutHeader("sto");
this.template.setReceiveTimeoutHeader("rto");
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
willAnswer(invocation -> {
this.executor.execute(() -> handler.handleMessage(invocation.getArgument(0)));
return true;
}).given(channel).send(any(Message.clreplaced), anyLong());
Message<?> message = MessageBuilder.withPayload("request").setHeader("sto", 30_000L).setHeader("rto", 1L).build();
replacedertThat(this.template.sendAndReceive(channel, message)).isNull();
replacedertThat(latch.await(10_000, TimeUnit.MILLISECONDS)).isTrue();
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@Test
public void sendAndReceiveTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setReceiveTimeout(1);
this.template.setSendTimeout(30_000L);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
willAnswer(invocation -> {
this.executor.execute(() -> handler.handleMessage(invocation.getArgument(0)));
return true;
}).given(channel).send(any(Message.clreplaced), anyLong());
replacedertThat(this.template.convertSendAndReceive(channel, "request", String.clreplaced)).isNull();
replacedertThat(latch.await(10_000, TimeUnit.MILLISECONDS)).isTrue();
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : MessageChannelReactiveUtils.java
License : Apache License 2.0
Project Creator : ralscha
License : Apache License 2.0
Project Creator : ralscha
private static <T> Publisher<Message<T>> adaptSubscribableChannelToPublisher(SubscribableChannel inputChannel) {
return new SubscribableChannelPublisherAdapter<>(inputChannel);
}
19
View Source File : PubSubMessageHandler.java
License : Apache License 2.0
Project Creator : ralscha
License : Apache License 2.0
Project Creator : ralscha
public clreplaced PubSubMessageHandler implements MessageHandler, SmartLifecycle, InitializingBean, ApplicationContextAware {
protected final Log logger = LogFactory.getLog(getClreplaced());
private final SubscribableChannel clientInboundChannel;
private final SubscribableChannel brokerChannel;
private final MessageChannel clientOutboundChannel;
private final SubscriptionRegistry subscriptionRegistry;
private boolean autoStartup = true;
private volatile boolean running = false;
private final Object lifecycleMonitor = new Object();
private ApplicationContext applicationContext;
private final HandlerMethodService handlerMethodService;
private final Features features;
private final EventStore eventStore;
public PubSubMessageHandler(SubscribableChannel clientInboundChannel, SubscribableChannel brokerChannel, MessageChannel clientOutboundChannel, SubscriptionRegistry subscriptionRegistry, HandlerMethodService handlerMethodService, Features features, EventStore eventStore) {
this.clientInboundChannel = clientInboundChannel;
this.brokerChannel = brokerChannel;
this.clientOutboundChannel = clientOutboundChannel;
this.subscriptionRegistry = subscriptionRegistry;
this.handlerMethodService = handlerMethodService;
this.features = features;
this.eventStore = eventStore;
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.clientInboundChannel.subscribe(this);
this.brokerChannel.subscribe(this);
this.running = true;
}
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.clientInboundChannel.unsubscribe(this);
this.brokerChannel.unsubscribe(this);
this.running = false;
}
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
@Override
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return this.running;
}
}
@Override
public void handleMessage(Message<?> message) {
if (!this.running) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this + " not running yet. Ignoring " + message);
}
return;
}
if (message instanceof SubscribeMessage) {
SubscribeMessage subscribeMessage = (SubscribeMessage) message;
if (this.features.isDisabled(Feature.BROKER_PATTERN_BASED_SUBSCRIPTION) && subscribeMessage.getMatchPolicy() != MatchPolicy.EXACT) {
sendMessageToClient(new ErrorMessage(subscribeMessage, WampError.OPTION_NOT_ALLOWED));
return;
}
SubscribeResult result = this.subscriptionRegistry.subscribe(subscribeMessage);
sendMessageToClient(new SubscribedMessage(subscribeMessage, result.getSubscription().getSubscriptionId()));
sendSubscriptionEvents(result, subscribeMessage);
if (subscribeMessage.isGetRetained()) {
handleRetentionRequest(subscribeMessage, result.getSubscription());
}
} else if (message instanceof UnsubscribeMessage) {
UnsubscribeMessage unsubscribeMessage = (UnsubscribeMessage) message;
UnsubscribeResult result = this.subscriptionRegistry.unsubscribe(unsubscribeMessage);
if (result.getError() == null) {
sendMessageToClient(new UnsubscribedMessage(unsubscribeMessage));
sendSubscriptionEvents(result, unsubscribeMessage);
} else {
sendMessageToClient(new ErrorMessage(unsubscribeMessage, result.getError()));
}
} else if (message instanceof PublishMessage) {
PublishMessage publishMessage = (PublishMessage) message;
if (publishMessage.isDiscloseMe() && this.features.isDisabled(Feature.BROKER_PUBLISHER_IDENTIFICATION)) {
if (publishMessage.getWebSocketSessionId() != null) {
sendMessageToClient(new ErrorMessage(publishMessage, WampError.DISCLOSE_ME_DISALLOWED));
}
return;
}
long publicationId = IdGenerator.newRandomId(null);
handlePublishMessage(publishMessage, publicationId);
if (publishMessage.isAcknowledge()) {
sendMessageToClient(new PublishedMessage(publishMessage, publicationId));
}
if (this.features.isEnabled(Feature.BROKER_EVENT_RETENTION) && publishMessage.isRetain()) {
this.eventStore.retain(publishMessage);
}
}
}
private void handleRetentionRequest(SubscribeMessage subscribeMessage, Subscription subscription) {
List<PublishMessage> retainedMessages = this.eventStore.getRetained(subscription.getTopicMatch());
if (!retainedMessages.isEmpty()) {
Subscriber subscriber = new Subscriber(subscribeMessage.getWebSocketSessionId(), subscribeMessage.getWampSessionId());
for (PublishMessage retainedMessage : retainedMessages) {
publishRetentionEvent(subscription, subscriber, retainedMessage);
}
}
}
private void publishRetentionEvent(Subscription subscription, Subscriber subscriber, PublishMessage publishMessage) {
String topic = null;
Long publisher = null;
if (subscription.getMatchPolicy() != MatchPolicy.EXACT) {
topic = publishMessage.getTopic();
}
if (publishMessage.isDiscloseMe()) {
publisher = publishMessage.getWampSessionId();
}
if (isEligible(publishMessage, subscriber)) {
EventMessage eventMessage = new EventMessage(subscriber.getWebSocketSessionId(), subscription.getSubscriptionId(), IdGenerator.newRandomId(null), topic, publisher, true, publishMessage);
sendMessageToClient(eventMessage);
}
}
@EventListener
void handleDisconnectEvent(WampDisconnectEvent event) {
List<UnsubscribeResult> results = this.subscriptionRegistry.removeWebSocketSessionId(event.getWebSocketSessionId(), event.getWampSessionId());
for (UnsubscribeResult result : results) {
sendSubscriptionEvents(result, event);
}
}
private void sendSubscriptionEvents(SubscribeResult result, SubscribeMessage subscribeMessage) {
SubscriptionDetail detail = new SubscriptionDetail(result.getSubscription());
if (result.isCreated()) {
this.applicationContext.publishEvent(new WampSubscriptionCreatedEvent(subscribeMessage, detail));
}
this.applicationContext.publishEvent(new WampSubscriptionSubscribedEvent(subscribeMessage, detail));
}
private void sendSubscriptionEvents(UnsubscribeResult result, UnsubscribeMessage unsubscribeMessage) {
SubscriptionDetail detail = new SubscriptionDetail(result.getSubscription());
this.applicationContext.publishEvent(new WampSubscriptionUnsubscribedEvent(unsubscribeMessage, detail));
if (result.isDeleted()) {
this.applicationContext.publishEvent(new WampSubscriptionDeletedEvent(unsubscribeMessage, detail));
}
}
private void sendSubscriptionEvents(UnsubscribeResult result, WampDisconnectEvent event) {
SubscriptionDetail detail = new SubscriptionDetail(result.getSubscription());
this.applicationContext.publishEvent(new WampSubscriptionUnsubscribedEvent(event, detail));
if (result.isDeleted()) {
this.applicationContext.publishEvent(new WampSubscriptionDeletedEvent(event, detail));
}
}
private void handlePublishMessage(PublishMessage publishMessage, long publicationId) {
Set<Subscription> subscriptions = this.subscriptionRegistry.findSubscriptions(publishMessage.getTopic());
if (subscriptions.size() > 0) {
Long publisher = null;
for (Subscription subscription : subscriptions) {
String topic = null;
if (subscription.getMatchPolicy() != MatchPolicy.EXACT) {
topic = publishMessage.getTopic();
}
if (publishMessage.isDiscloseMe()) {
publisher = publishMessage.getWampSessionId();
}
for (Subscriber subscriber : subscription.getSubscribers()) {
if (isEligible(publishMessage, subscriber)) {
EventMessage eventMessage = new EventMessage(subscriber.getWebSocketSessionId(), subscription.getSubscriptionId(), publicationId, topic, publisher, false, publishMessage);
sendMessageToClient(eventMessage);
}
}
// do not send event messages to annotated methods when the publish
// message was created from the WampPublisher and exclude me is set
// to true
if (publishMessage.getWebSocketSessionId() == null && (publishMessage.isExcludeMe() || this.features.isDisabled(Feature.BROKER_PUBLISHER_EXCLUSION))) {
continue;
}
List<InvocableHandlerMethod> eventListenerHandlerMethods = subscription.getEventListenerHandlerMethods();
if (eventListenerHandlerMethods != null) {
EventMessage eventMessage = new EventMessage(null, -1, publicationId, topic, publisher, false, publishMessage);
for (InvocableHandlerMethod handlerMethod : eventListenerHandlerMethods) {
try {
this.handlerMethodService.invoke(eventMessage, handlerMethod);
} catch (Exception e) {
if (this.logger.isErrorEnabled()) {
this.logger.error("Error while invoking event message handler method " + handlerMethod, e);
}
}
}
}
}
}
}
private boolean isEligible(PublishMessage publishMessage, Subscriber subscriber) {
String myWebSocketSessionId = publishMessage.getWebSocketSessionId();
if ((publishMessage.isExcludeMe() || this.features.isDisabled(Feature.BROKER_PUBLISHER_EXCLUSION)) && myWebSocketSessionId != null && myWebSocketSessionId.equals(subscriber.getWebSocketSessionId())) {
return false;
}
if (this.features.isEnabled(Feature.BROKER_SUBSCRIBER_BLACKWHITE_LISTING)) {
if (publishMessage.getEligible() != null && !publishMessage.getEligible().contains(subscriber.getWampSessionId())) {
return false;
}
if (publishMessage.getExclude() != null && publishMessage.getExclude().contains(subscriber.getWampSessionId())) {
return false;
}
}
return true;
}
protected void sendMessageToClient(Message<?> message) {
try {
this.clientOutboundChannel.send(message);
} catch (Throwable ex) {
this.logger.error("Failed to send " + message, ex);
}
}
@Override
public void afterPropertiesSet() throws Exception {
for (String beanName : this.applicationContext.getBeanNamesForType(Object.clreplaced)) {
detectAnnotatedMethods(beanName);
}
}
private void detectAnnotatedMethods(String beanName) {
Clreplaced<?> handlerType = this.applicationContext.getType(beanName);
final Clreplaced<?> userType = ClreplacedUtils.getUserClreplaced(handlerType);
List<EventListenerInfo> eventListeners = detectEventListeners(beanName, userType);
this.subscriptionRegistry.subscribeEventHandlers(eventListeners);
}
private List<EventListenerInfo> detectEventListeners(String beanName, Clreplaced<?> userType) {
List<EventListenerInfo> registry = new ArrayList<>();
Set<Method> methods = MethodIntrospector.selectMethods(userType, (MethodFilter) method -> AnnotationUtils.findAnnotation(method, WampListener.clreplaced) != null);
for (Method method : methods) {
WampListener wampEventListenerAnnotation = AnnotationUtils.findAnnotation(method, WampListener.clreplaced);
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(new HandlerMethod(this.applicationContext.getBean(beanName), method));
String[] topics = (String[]) AnnotationUtils.getValue(wampEventListenerAnnotation);
if (topics.length == 0) {
// by default use beanName.methodName as topic
topics = new String[] { beanName + "." + method.getName() };
}
MatchPolicy match = (MatchPolicy) AnnotationUtils.getValue(wampEventListenerAnnotation, "match");
EventListenerInfo info = new EventListenerInfo(handlerMethod, topics, match);
registry.add(info);
}
return registry;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
19
View Source File : ReceiverHandler.java
License : Apache License 2.0
Project Creator : q920447939
License : Apache License 2.0
Project Creator : q920447939
@PostConstruct
public void init() {
SubscribableChannel channel = receiverServer.receiverMes();
channel.subscribe(message -> {
byte[] payload = (byte[]) message.getPayload();
System.out.println(new String(payload));
});
}
19
View Source File : UserDestinationMessageHandlerTests.java
License : MIT License
Project Creator : mindcarver
License : MIT License
Project Creator : mindcarver
/**
* Unit tests for
* {@link org.springframework.messaging.simp.user.UserDestinationMessageHandler}.
*/
public clreplaced UserDestinationMessageHandlerTests {
private static final String SESSION_ID = "123";
private UserDestinationMessageHandler handler;
private SimpUserRegistry registry;
private SubscribableChannel brokerChannel;
@Before
public void setup() {
this.registry = mock(SimpUserRegistry.clreplaced);
this.brokerChannel = mock(SubscribableChannel.clreplaced);
UserDestinationResolver resolver = new DefaultUserDestinationResolver(this.registry);
this.handler = new UserDestinationMessageHandler(new StubMessageChannel(), this.brokerChannel, resolver);
}
@Test
public void handleSubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", SESSION_ID, "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertEquals("/queue/foo-user123", SimpMessageHeaderAccessor.getDestination(message.getHeaders()));
}
@Test
public void handleUnsubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.UNSUBSCRIBE, "joe", "123", "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertEquals("/queue/foo-user123", SimpMessageHeaderAccessor.getDestination(message.getHeaders()));
}
@Test
public void handleMessage() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
when(this.registry.getUser("joe")).thenReturn(simpUser);
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertEquals("/queue/foo-user123", accessor.getDestination());
replacedertEquals("/user/queue/foo", accessor.getFirstNativeHeader(ORIGINAL_DESTINATION));
}
@Test
public void handleMessageWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
replacedertEquals("/topic/unresolved", accessor.getDestination());
replacedertEquals("/user/joe/queue/foo", accessor.getFirstNativeHeader(ORIGINAL_DESTINATION));
// Should ignore our own broadcast to brokerChannel
this.handler.handleMessage(message);
Mockito.verifyNoMoreInteractions(this.brokerChannel);
}
@Test
public void handleMessageFromBrokerWithActiveSession() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
when(this.registry.getUser("joe")).thenReturn(simpUser);
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setNativeHeader("customHeader", "customHeaderValue");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
replacedertNotNull(captor.getValue());
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertEquals("/queue/foo-user123", headers.getDestination());
replacedertEquals("/user/queue/foo", headers.getFirstNativeHeader(ORIGINAL_DESTINATION));
replacedertEquals("customHeaderValue", headers.getFirstNativeHeader("customHeader"));
replacedertArrayEquals(payload, (byte[]) captor.getValue().getPayload());
}
@Test
public void handleMessageFromBrokerWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(StandardCharsets.UTF_8);
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
// No re-broadcast
verifyNoMoreInteractions(this.brokerChannel);
}
@Test
public void ignoreMessage() {
// no destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", null));
Mockito.verifyZeroInteractions(this.brokerChannel);
// not a user destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
// subscribe + not a user destination
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", "123", "/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
// no match on message type
this.handler.handleMessage(createWith(SimpMessageType.CONNECT, "joe", "123", "user/joe/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
}
private Message<?> createWith(SimpMessageType type, String user, String sessionId, String destination) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);
if (destination != null) {
headers.setDestination(destination);
}
if (user != null) {
headers.setUser(new TestPrincipal(user));
}
if (sessionId != null) {
headers.setSessionId(sessionId);
}
return MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
}
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : mindcarver
License : MIT License
Project Creator : mindcarver
@Test
public void sendAndReceiveTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setReceiveTimeout(1);
this.template.setSendTimeout(30_000L);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
doAnswer(invocation -> {
this.executor.execute(() -> {
handler.handleMessage(invocation.getArgument(0));
});
return true;
}).when(channel).send(any(Message.clreplaced), anyLong());
replacedertNull(this.template.convertSendAndReceive(channel, "request", String.clreplaced));
replacedertTrue(latch.await(10_000, TimeUnit.MILLISECONDS));
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : mindcarver
License : MIT License
Project Creator : mindcarver
@Test
public void sendWithTimeoutMutable() {
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
final AtomicReference<Message<?>> sent = new AtomicReference<>();
doAnswer(invocation -> {
sent.set(invocation.getArgument(0));
return true;
}).when(channel).send(any(Message.clreplaced), eq(30_000L));
MessageHeaderAccessor accessor = new MessageHeaderAccessor();
accessor.setLeaveMutable(true);
Message<?> message = new GenericMessage<>("request", accessor.getMessageHeaders());
accessor.setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L);
this.template.send(channel, message);
verify(channel).send(any(Message.clreplaced), eq(30_000L));
replacedertNotNull(sent.get());
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER));
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : mindcarver
License : MIT License
Project Creator : mindcarver
@Test
public void sendAndReceiveVariableTimeoutCustomHeaders() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setSendTimeout(20_000);
this.template.setReceiveTimeout(10_000);
this.template.setThrowExceptionOnLateReply(true);
this.template.setSendTimeoutHeader("sto");
this.template.setReceiveTimeoutHeader("rto");
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
doAnswer(invocation -> {
this.executor.execute(() -> {
handler.handleMessage(invocation.getArgument(0));
});
return true;
}).when(channel).send(any(Message.clreplaced), anyLong());
Message<?> message = MessageBuilder.withPayload("request").setHeader("sto", 30_000L).setHeader("rto", 1L).build();
replacedertNull(this.template.sendAndReceive(channel, message));
replacedertTrue(latch.await(10_000, TimeUnit.MILLISECONDS));
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : mindcarver
License : MIT License
Project Creator : mindcarver
@Test
public void sendAndReceiveVariableTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setSendTimeout(20_000);
this.template.setReceiveTimeout(10_000);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
MessageHandler handler = createLateReplier(latch, failure);
doAnswer(invocation -> {
this.executor.execute(() -> {
handler.handleMessage(invocation.getArgument(0));
});
return true;
}).when(channel).send(any(Message.clreplaced), anyLong());
Message<?> message = MessageBuilder.withPayload("request").setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L).setHeader(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER, 1L).build();
replacedertNull(this.template.sendAndReceive(channel, message));
replacedertTrue(latch.await(10_000, TimeUnit.MILLISECONDS));
Throwable ex = failure.get();
if (ex != null) {
throw new replacedertionError(ex);
}
verify(channel).send(any(Message.clreplaced), eq(30_000L));
}
19
View Source File : GenericMessagingTemplateTests.java
License : MIT License
Project Creator : mindcarver
License : MIT License
Project Creator : mindcarver
@Test
public void sendWithTimeout() {
SubscribableChannel channel = mock(SubscribableChannel.clreplaced);
final AtomicReference<Message<?>> sent = new AtomicReference<>();
doAnswer(invocation -> {
sent.set(invocation.getArgument(0));
return true;
}).when(channel).send(any(Message.clreplaced), eq(30_000L));
Message<?> message = MessageBuilder.withPayload("request").setHeader(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER, 30_000L).setHeader(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER, 1L).build();
this.template.send(channel, message);
verify(channel).send(any(Message.clreplaced), eq(30_000L));
replacedertNotNull(sent.get());
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER));
replacedertFalse(sent.get().getHeaders().containsKey(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER));
}
19
View Source File : WebMvcStompEndpointRegistryTests.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
@Before
public void setup() {
SubscribableChannel inChannel = mock(SubscribableChannel.clreplaced);
SubscribableChannel outChannel = mock(SubscribableChannel.clreplaced);
this.webSocketHandler = new SubProtocolWebSocketHandler(inChannel, outChannel);
WebSocketTransportRegistration transport = new WebSocketTransportRegistration();
TaskScheduler scheduler = mock(TaskScheduler.clreplaced);
this.endpointRegistry = new WebMvcStompEndpointRegistry(this.webSocketHandler, transport, null, scheduler);
}
19
View Source File : UserDestinationMessageHandlerTests.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
/**
* Unit tests for
* {@link org.springframework.messaging.simp.user.UserDestinationMessageHandler}.
*/
public clreplaced UserDestinationMessageHandlerTests {
private static final String SESSION_ID = "123";
private UserDestinationMessageHandler handler;
private SimpUserRegistry registry;
private SubscribableChannel brokerChannel;
@Before
public void setup() {
this.registry = mock(SimpUserRegistry.clreplaced);
this.brokerChannel = mock(SubscribableChannel.clreplaced);
UserDestinationResolver resolver = new DefaultUserDestinationResolver(this.registry);
this.handler = new UserDestinationMessageHandler(new StubMessageChannel(), this.brokerChannel, resolver);
}
@Test
public void handleSubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", SESSION_ID, "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertEquals("/queue/foo-user123", SimpMessageHeaderAccessor.getDestination(message.getHeaders()));
}
@Test
public void handleUnsubscribe() {
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.UNSUBSCRIBE, "joe", "123", "/user/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
replacedertEquals("/queue/foo-user123", SimpMessageHeaderAccessor.getDestination(message.getHeaders()));
}
@Test
public void handleMessage() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
when(this.registry.getUser("joe")).thenReturn(simpUser);
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertEquals("/queue/foo-user123", accessor.getDestination());
replacedertEquals("/user/queue/foo", accessor.getFirstNativeHeader(ORIGINAL_DESTINATION));
}
@Test
public void handleMessageWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/user/joe/queue/foo"));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
Message message = captor.getValue();
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
replacedertEquals("/topic/unresolved", accessor.getDestination());
replacedertEquals("/user/joe/queue/foo", accessor.getFirstNativeHeader(ORIGINAL_DESTINATION));
// Should ignore our own broadcast to brokerChannel
this.handler.handleMessage(message);
Mockito.verifyNoMoreInteractions(this.brokerChannel);
}
@Test
public void handleMessageFromBrokerWithActiveSession() {
TestSimpUser simpUser = new TestSimpUser("joe");
simpUser.addSessions(new TestSimpSession("123"));
when(this.registry.getUser("joe")).thenReturn(simpUser);
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setNativeHeader("customHeader", "customHeaderValue");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(Charset.forName("UTF-8"));
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
ArgumentCaptor<Message> captor = ArgumentCaptor.forClreplaced(Message.clreplaced);
Mockito.verify(this.brokerChannel).send(captor.capture());
replacedertNotNull(captor.getValue());
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.wrap(captor.getValue());
replacedertEquals("/queue/foo-user123", headers.getDestination());
replacedertEquals("/user/queue/foo", headers.getFirstNativeHeader(ORIGINAL_DESTINATION));
replacedertEquals("customHeaderValue", headers.getFirstNativeHeader("customHeader"));
replacedertArrayEquals(payload, (byte[]) captor.getValue().getPayload());
}
@Test
public void handleMessageFromBrokerWithoutActiveSession() {
this.handler.setBroadcastDestination("/topic/unresolved");
given(this.brokerChannel.send(Mockito.any(Message.clreplaced))).willReturn(true);
StompHeaderAccessor accessor = StompHeaderAccessor.create(StompCommand.MESSAGE);
accessor.setSessionId("system123");
accessor.setDestination("/topic/unresolved");
accessor.setNativeHeader(ORIGINAL_DESTINATION, "/user/joe/queue/foo");
accessor.setLeaveMutable(true);
byte[] payload = "payload".getBytes(Charset.forName("UTF-8"));
this.handler.handleMessage(MessageBuilder.createMessage(payload, accessor.getMessageHeaders()));
// No re-broadcast
verifyNoMoreInteractions(this.brokerChannel);
}
@Test
public void ignoreMessage() {
// no destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", null));
Mockito.verifyZeroInteractions(this.brokerChannel);
// not a user destination
this.handler.handleMessage(createWith(SimpMessageType.MESSAGE, "joe", "123", "/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
// subscribe + not a user destination
this.handler.handleMessage(createWith(SimpMessageType.SUBSCRIBE, "joe", "123", "/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
// no match on message type
this.handler.handleMessage(createWith(SimpMessageType.CONNECT, "joe", "123", "user/joe/queue/foo"));
Mockito.verifyZeroInteractions(this.brokerChannel);
}
private Message<?> createWith(SimpMessageType type, String user, String sessionId, String destination) {
SimpMessageHeaderAccessor headers = SimpMessageHeaderAccessor.create(type);
if (destination != null) {
headers.setDestination(destination);
}
if (user != null) {
headers.setUser(new TestPrincipal(user));
}
if (sessionId != null) {
headers.setSessionId(sessionId);
}
return MessageBuilder.withPayload(new byte[0]).setHeaders(headers).build();
}
}
19
View Source File : UserDestinationMessageHandler.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
/**
* {@code MessageHandler} with support for "user" destinations.
*
* <p>Listens for messages with "user" destinations, translates their destination
* to actual target destinations unique to the active session(s) of a user, and
* then sends the resolved messages to the broker channel to be delivered.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public clreplaced UserDestinationMessageHandler implements MessageHandler, SmartLifecycle {
private static final Log logger = LogFactory.getLog(UserDestinationMessageHandler.clreplaced);
private final SubscribableChannel clientInboundChannel;
private final SubscribableChannel brokerChannel;
private final UserDestinationResolver destinationResolver;
private final MessageSendingOperations<String> messagingTemplate;
private BroadcastHandler broadcastHandler;
private MessageHeaderInitializer headerInitializer;
private final Object lifecycleMonitor = new Object();
private volatile boolean running = false;
/**
* Create an instance with the given client and broker channels subscribing
* to handle messages from each and then sending any resolved messages to the
* broker channel.
* @param clientInboundChannel messages received from clients.
* @param brokerChannel messages sent to the broker.
* @param resolver the resolver for "user" destinations.
*/
public UserDestinationMessageHandler(SubscribableChannel clientInboundChannel, SubscribableChannel brokerChannel, UserDestinationResolver resolver) {
replacedert.notNull(clientInboundChannel, "'clientInChannel' must not be null");
replacedert.notNull(brokerChannel, "'brokerChannel' must not be null");
replacedert.notNull(resolver, "resolver must not be null");
this.clientInboundChannel = clientInboundChannel;
this.brokerChannel = brokerChannel;
this.messagingTemplate = new SimpMessagingTemplate(brokerChannel);
this.destinationResolver = resolver;
}
/**
* Return the configured {@link UserDestinationResolver}.
*/
public UserDestinationResolver getUserDestinationResolver() {
return this.destinationResolver;
}
/**
* Set a destination to broadcast messages to that remain unresolved because
* the user is not connected. In a multi-application server scenario this
* gives other application servers a chance to try.
* <p>By default this is not set.
* @param destination the target destination.
*/
public void setBroadcastDestination(String destination) {
this.broadcastHandler = (StringUtils.hasText(destination) ? new BroadcastHandler(this.messagingTemplate, destination) : null);
}
/**
* Return the configured destination for unresolved messages.
*/
public String getBroadcastDestination() {
return (this.broadcastHandler != null ? this.broadcastHandler.getBroadcastDestination() : null);
}
/**
* Return the messaging template used to send resolved messages to the
* broker channel.
*/
public MessageSendingOperations<String> getBrokerMessagingTemplate() {
return this.messagingTemplate;
}
/**
* Configure a custom {@link MessageHeaderInitializer} to initialize the
* headers of resolved target messages.
* <p>By default this is not set.
*/
public void setHeaderInitializer(MessageHeaderInitializer headerInitializer) {
this.headerInitializer = headerInitializer;
}
/**
* Return the configured header initializer.
*/
public MessageHeaderInitializer getHeaderInitializer() {
return this.headerInitializer;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
@Override
public boolean isAutoStartup() {
return true;
}
@Override
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return this.running;
}
}
@Override
public final void start() {
synchronized (this.lifecycleMonitor) {
this.clientInboundChannel.subscribe(this);
this.brokerChannel.subscribe(this);
this.running = true;
}
}
@Override
public final void stop() {
synchronized (this.lifecycleMonitor) {
this.running = false;
this.clientInboundChannel.unsubscribe(this);
this.brokerChannel.unsubscribe(this);
}
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
@Override
public void handleMessage(Message<?> message) throws MessagingException {
if (this.broadcastHandler != null) {
message = this.broadcastHandler.preHandle(message);
if (message == null) {
return;
}
}
UserDestinationResult result = this.destinationResolver.resolveDestination(message);
if (result == null) {
return;
}
if (result.getTargetDestinations().isEmpty()) {
if (logger.isTraceEnabled()) {
logger.trace("No active sessions for user destination: " + result.getSourceDestination());
}
if (this.broadcastHandler != null) {
this.broadcastHandler.handleUnresolved(message);
}
return;
}
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
initHeaders(accessor);
accessor.setNativeHeader(ORIGINAL_DESTINATION, result.getSubscribeDestination());
accessor.setLeaveMutable(true);
message = MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
if (logger.isTraceEnabled()) {
logger.trace("Translated " + result.getSourceDestination() + " -> " + result.getTargetDestinations());
}
for (String target : result.getTargetDestinations()) {
this.messagingTemplate.send(target, message);
}
}
private void initHeaders(SimpMessageHeaderAccessor headerAccessor) {
if (getHeaderInitializer() != null) {
getHeaderInitializer().initHeaders(headerAccessor);
}
}
@Override
public String toString() {
return "UserDestinationMessageHandler[" + this.destinationResolver + "]";
}
/**
* A handler that broadcasts locally unresolved messages to the broker and
* also handles similar broadcasts received from the broker.
*/
private static clreplaced BroadcastHandler {
private static final List<String> NO_COPY_LIST = Arrays.asList("subscription", "message-id");
private final MessageSendingOperations<String> messagingTemplate;
private final String broadcastDestination;
public BroadcastHandler(MessageSendingOperations<String> template, String destination) {
this.messagingTemplate = template;
this.broadcastDestination = destination;
}
public String getBroadcastDestination() {
return this.broadcastDestination;
}
public Message<?> preHandle(Message<?> message) throws MessagingException {
String destination = SimpMessageHeaderAccessor.getDestination(message.getHeaders());
if (!getBroadcastDestination().equals(destination)) {
return message;
}
SimpMessageHeaderAccessor accessor = getAccessor(message, SimpMessageHeaderAccessor.clreplaced);
if (accessor.getSessionId() == null) {
// Our own broadcast
return null;
}
destination = accessor.getFirstNativeHeader(ORIGINAL_DESTINATION);
if (logger.isTraceEnabled()) {
logger.trace("Checking unresolved user destination: " + destination);
}
SimpMessageHeaderAccessor newAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
for (String name : accessor.toNativeHeaderMap().keySet()) {
if (NO_COPY_LIST.contains(name)) {
continue;
}
newAccessor.setNativeHeader(name, accessor.getFirstNativeHeader(name));
}
newAccessor.setDestination(destination);
// ensure send doesn't block
newAccessor.setHeader(SimpMessageHeaderAccessor.IGNORE_ERROR, true);
return MessageBuilder.createMessage(message.getPayload(), newAccessor.getMessageHeaders());
}
public void handleUnresolved(Message<?> message) {
MessageHeaders headers = message.getHeaders();
if (SimpMessageHeaderAccessor.getFirstNativeHeader(ORIGINAL_DESTINATION, headers) != null) {
// Re-broadcast
return;
}
SimpMessageHeaderAccessor accessor = SimpMessageHeaderAccessor.wrap(message);
String destination = accessor.getDestination();
accessor.setNativeHeader(ORIGINAL_DESTINATION, destination);
accessor.setLeaveMutable(true);
message = MessageBuilder.createMessage(message.getPayload(), accessor.getMessageHeaders());
if (logger.isTraceEnabled()) {
logger.trace("Translated " + destination + " -> " + getBroadcastDestination());
}
this.messagingTemplate.send(getBroadcastDestination(), message);
}
}
}
19
View Source File : MessageBrokerRegistry.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
protected SimpleBrokerMessageHandler getSimpleBroker(SubscribableChannel brokerChannel) {
if (this.simpleBrokerRegistration == null && this.brokerRelayRegistration == null) {
enableSimpleBroker();
}
if (this.simpleBrokerRegistration != null) {
SimpleBrokerMessageHandler handler = this.simpleBrokerRegistration.getMessageHandler(brokerChannel);
handler.setPathMatcher(this.pathMatcher);
return handler;
}
return null;
}
19
View Source File : MessageBrokerRegistry.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
protected StompBrokerRelayMessageHandler getStompBrokerRelay(SubscribableChannel brokerChannel) {
if (this.brokerRelayRegistration != null) {
return this.brokerRelayRegistration.getMessageHandler(brokerChannel);
}
return null;
}
19
View Source File : PubSubChannelAdaptersIntegrationTests.java
License : Apache License 2.0
Project Creator : GoogleCloudPlatform
License : Apache License 2.0
Project Creator : GoogleCloudPlatform
// If this test flakes, delete it.
// It verifies that in AUTO_ACK mode, the message is neither acked nor nacked, and that
// redelivery happens after subscription's ackDeadline preplacedes.
// There is also a client library bug (https://github.com/googleapis/java-pubsub/issues/141) that
// results in ackDeadline being extended by 60 seconds even when maxAckExtensionPeriod is zero,
// making minimum redelivery time is ackDeadline + 60.
@Test
public void sendAndReceiveMessageAutoAckWithFailure() {
this.contextRunner.withUserConfiguration(SubscribableConfiguration.clreplaced, CommonConfiguration.clreplaced).withPropertyValues("spring.cloud.gcp.pubsub.subscriber.max-ack-extension-period=0").run(context -> {
context.getBean(PubSubInboundChannelAdapter.clreplaced).setAckMode(AckMode.AUTO_ACK);
context.getBean("inputChannel", MessageChannel.clreplaced).send(MessageBuilder.withPayload("This message is in trouble.".getBytes()).build());
SubscribableChannel channel = context.getBean("outputChannel", SubscribableChannel.clreplaced);
AtomicInteger numReceivedMessages = new AtomicInteger(0);
channel.subscribe(msg -> {
if (numReceivedMessages.incrementAndGet() == 1) {
throw new RuntimeException("BOOM!");
}
});
// wait for initial delivery
Awaitility.await().atMost(10, TimeUnit.SECONDS).until(() -> numReceivedMessages.get() > 0);
replacedertThat(numReceivedMessages.get()).isEqualTo(1);
// Expect redelivery after at least 10 seconds but within 1.5 minutes:
// 10 seconds subscription ackDeadline
// + 60 seconds https://github.com/googleapis/java-pubsub/issues/141
// + 20 seconds anti-flake buffer
Awaitility.await().atLeast(9, TimeUnit.SECONDS).atMost(90, TimeUnit.SECONDS).until(() -> numReceivedMessages.get() > 1);
replacedertThat(numReceivedMessages.get()).isEqualTo(2);
});
}
19
View Source File : OrderApplication.java
License : Apache License 2.0
Project Creator : alibaba
License : Apache License 2.0
Project Creator : alibaba
public static void main(String[] args) {
ApplicationContext applicationContext = SpringApplication.run(OrderApplication.clreplaced, args);
SubscribableChannel input = (SubscribableChannel) applicationContext.getBean("input");
input.subscribe(message -> {
try {
System.out.println("Get mq message ,content is : " + new String((byte[]) message.getPayload(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
});
}
18
View Source File : WebSocketAnnotationMethodMessageHandlerTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
@Before
public void setUp() throws Exception {
this.applicationContext = new StaticApplicationContext();
this.applicationContext.registerSingleton("controller", TestController.clreplaced);
this.applicationContext.registerSingleton("controllerAdvice", TestControllerAdvice.clreplaced);
this.applicationContext.refresh();
SubscribableChannel channel = Mockito.mock(SubscribableChannel.clreplaced);
SimpMessageSendingOperations brokerTemplate = new SimpMessagingTemplate(channel);
this.messageHandler = new TestWebSocketAnnotationMethodMessageHandler(brokerTemplate, channel, channel);
this.messageHandler.setApplicationContext(this.applicationContext);
this.messageHandler.afterPropertiesSet();
}
18
View Source File : MessageBrokerRegistry.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* A registry for configuring message broker options.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public clreplaced MessageBrokerRegistry {
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
@Nullable
private SimpleBrokerRegistration simpleBrokerRegistration;
@Nullable
private StompBrokerRelayRegistration brokerRelayRegistration;
private final ChannelRegistration brokerChannelRegistration = new ChannelRegistration();
@Nullable
private String[] applicationDestinationPrefixes;
@Nullable
private String userDestinationPrefix;
@Nullable
private Integer userRegistryOrder;
@Nullable
private PathMatcher pathMatcher;
@Nullable
private Integer cacheLimit;
private boolean preservePublishOrder;
public MessageBrokerRegistry(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
replacedert.notNull(clientInboundChannel, "Inbound channel must not be null");
replacedert.notNull(clientOutboundChannel, "Outbound channel must not be null");
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
}
/**
* Enable a simple message broker and configure one or more prefixes to filter
* destinations targeting the broker (e.g. destinations prefixed with "/topic").
*/
public SimpleBrokerRegistration enableSimpleBroker(String... destinationPrefixes) {
this.simpleBrokerRegistration = new SimpleBrokerRegistration(this.clientInboundChannel, this.clientOutboundChannel, destinationPrefixes);
return this.simpleBrokerRegistration;
}
/**
* Enable a STOMP broker relay and configure the destination prefixes supported by the
* message broker. Check the STOMP doreplacedentation of the message broker for supported
* destinations.
*/
public StompBrokerRelayRegistration enableStompBrokerRelay(String... destinationPrefixes) {
this.brokerRelayRegistration = new StompBrokerRelayRegistration(this.clientInboundChannel, this.clientOutboundChannel, destinationPrefixes);
return this.brokerRelayRegistration;
}
/**
* Customize the channel used to send messages from the application to the message
* broker. By default, messages from the application to the message broker are sent
* synchronously, which means application code sending a message will find out
* if the message cannot be sent through an exception. However, this can be changed
* if the broker channel is configured here with task executor properties.
*/
public ChannelRegistration configureBrokerChannel() {
return this.brokerChannelRegistration;
}
protected ChannelRegistration getBrokerChannelRegistration() {
return this.brokerChannelRegistration;
}
@Nullable
protected String getUserDestinationBroadcast() {
return (this.brokerRelayRegistration != null ? this.brokerRelayRegistration.getUserDestinationBroadcast() : null);
}
@Nullable
protected String getUserRegistryBroadcast() {
return (this.brokerRelayRegistration != null ? this.brokerRelayRegistration.getUserRegistryBroadcast() : null);
}
/**
* Configure one or more prefixes to filter destinations targeting application
* annotated methods. For example destinations prefixed with "/app" may be
* processed by annotated methods while other destinations may target the
* message broker (e.g. "/topic", "/queue").
* <p>When messages are processed, the matching prefix is removed from the destination
* in order to form the lookup path. This means annotations should not contain the
* destination prefix.
* <p>Prefixes that do not have a trailing slash will have one automatically appended.
*/
public MessageBrokerRegistry setApplicationDestinationPrefixes(String... prefixes) {
this.applicationDestinationPrefixes = prefixes;
return this;
}
@Nullable
protected Collection<String> getApplicationDestinationPrefixes() {
return (this.applicationDestinationPrefixes != null ? Arrays.asList(this.applicationDestinationPrefixes) : null);
}
/**
* Configure the prefix used to identify user destinations. User destinations
* provide the ability for a user to subscribe to queue names unique to their
* session as well as for others to send messages to those unique,
* user-specific queues.
* <p>For example when a user attempts to subscribe to "/user/queue/position-updates",
* the destination may be translated to "/queue/position-updatesi9oqdfzo" yielding a
* unique queue name that does not collide with any other user attempting to do the same.
* Subsequently when messages are sent to "/user/{username}/queue/position-updates",
* the destination is translated to "/queue/position-updatesi9oqdfzo".
* <p>The default prefix used to identify such destinations is "/user/".
*/
public MessageBrokerRegistry setUserDestinationPrefix(String destinationPrefix) {
this.userDestinationPrefix = destinationPrefix;
return this;
}
@Nullable
protected String getUserDestinationPrefix() {
return this.userDestinationPrefix;
}
/**
* Set the order for the
* {@link org.springframework.messaging.simp.user.SimpUserRegistry
* SimpUserRegistry} to use as a {@link SmartApplicationListener}.
* @param order the order value
* @since 5.0.8
*/
public void setUserRegistryOrder(int order) {
this.userRegistryOrder = order;
}
@Nullable
protected Integer getUserRegistryOrder() {
return this.userRegistryOrder;
}
/**
* Configure the PathMatcher to use to match the destinations of incoming
* messages to {@code @MessageMapping} and {@code @SubscribeMapping} methods.
* <p>By default {@link org.springframework.util.AntPathMatcher} is configured.
* However applications may provide an {@code AntPathMatcher} instance
* customized to use "." (commonly used in messaging) instead of "/" as path
* separator or provide a completely different PathMatcher implementation.
* <p>Note that the configured PathMatcher is only used for matching the
* portion of the destination after the configured prefix. For example given
* application destination prefix "/app" and destination "/app/price.stock.**",
* the message might be mapped to a controller with "price" and "stock.**"
* as its type and method-level mappings respectively.
* <p>When the simple broker is enabled, the PathMatcher configured here is
* also used to match message destinations when brokering messages.
* @since 4.1
* @see org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#setPathMatcher
*/
public MessageBrokerRegistry setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
return this;
}
@Nullable
protected PathMatcher getPathMatcher() {
return this.pathMatcher;
}
/**
* Configure the cache limit to apply for registrations with the broker.
* <p>This is currently only applied for the destination cache in the
* subscription registry. The default cache limit there is 1024.
* @since 4.3.2
* @see org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry#setCacheLimit
*/
public MessageBrokerRegistry setCacheLimit(int cacheLimit) {
this.cacheLimit = cacheLimit;
return this;
}
/**
* Whether the client must receive messages in the order of publication.
* <p>By default messages sent to the {@code "clientOutboundChannel"} may
* not be processed in the same order because the channel is backed by a
* ThreadPoolExecutor that in turn does not guarantee processing in order.
* <p>When this flag is set to {@code true} messages within the same session
* will be sent to the {@code "clientOutboundChannel"} one at a time in
* order to preserve the order of publication. Enable this only if needed
* since there is some performance overhead to keep messages in order.
* @since 5.1
*/
public MessageBrokerRegistry setPreservePublishOrder(boolean preservePublishOrder) {
this.preservePublishOrder = preservePublishOrder;
return this;
}
@Nullable
protected SimpleBrokerMessageHandler getSimpleBroker(SubscribableChannel brokerChannel) {
if (this.simpleBrokerRegistration == null && this.brokerRelayRegistration == null) {
enableSimpleBroker();
}
if (this.simpleBrokerRegistration != null) {
SimpleBrokerMessageHandler handler = this.simpleBrokerRegistration.getMessageHandler(brokerChannel);
handler.setPathMatcher(this.pathMatcher);
handler.setCacheLimit(this.cacheLimit);
handler.setPreservePublishOrder(this.preservePublishOrder);
return handler;
}
return null;
}
@Nullable
protected StompBrokerRelayMessageHandler getStompBrokerRelay(SubscribableChannel brokerChannel) {
if (this.brokerRelayRegistration != null) {
StompBrokerRelayMessageHandler relay = this.brokerRelayRegistration.getMessageHandler(brokerChannel);
relay.setPreservePublishOrder(this.preservePublishOrder);
return relay;
}
return null;
}
}
18
View Source File : AbstractBrokerRegistration.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* Base clreplaced for message broker registration clreplacedes.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract clreplaced AbstractBrokerRegistration {
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
private final List<String> destinationPrefixes;
public AbstractBrokerRegistration(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel, @Nullable String[] destinationPrefixes) {
replacedert.notNull(clientOutboundChannel, "'clientInboundChannel' must not be null");
replacedert.notNull(clientOutboundChannel, "'clientOutboundChannel' must not be null");
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
this.destinationPrefixes = (destinationPrefixes != null ? Arrays.asList(destinationPrefixes) : Collections.emptyList());
}
protected SubscribableChannel getClientInboundChannel() {
return this.clientInboundChannel;
}
protected MessageChannel getClientOutboundChannel() {
return this.clientOutboundChannel;
}
protected Collection<String> getDestinationPrefixes() {
return this.destinationPrefixes;
}
protected abstract AbstractBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel);
}
18
View Source File : AbstractBrokerMessageHandler.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* Abstract base clreplaced for a {@link MessageHandler} that broker messages to
* registered subscribers.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract clreplaced AbstractBrokerMessageHandler implements MessageHandler, ApplicationEventPublisherAware, SmartLifecycle {
protected final Log logger = SimpLogging.forLogName(getClreplaced());
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
private final SubscribableChannel brokerChannel;
private final Collection<String> destinationPrefixes;
private boolean preservePublishOrder = false;
@Nullable
private ApplicationEventPublisher eventPublisher;
private AtomicBoolean brokerAvailable = new AtomicBoolean(false);
private final BrokerAvailabilityEvent availableEvent = new BrokerAvailabilityEvent(true, this);
private final BrokerAvailabilityEvent notAvailableEvent = new BrokerAvailabilityEvent(false, this);
private boolean autoStartup = true;
private volatile boolean running = false;
private final Object lifecycleMonitor = new Object();
private final ChannelInterceptor unsentDisconnectInterceptor = new UnsentDisconnectChannelInterceptor();
/**
* Constructor with no destination prefixes (matches all destinations).
* @param inboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param outboundChannel the channel for sending messages to clients (e.g. WebSocket clients)
* @param brokerChannel the channel for the application to send messages to the broker
*/
public AbstractBrokerMessageHandler(SubscribableChannel inboundChannel, MessageChannel outboundChannel, SubscribableChannel brokerChannel) {
this(inboundChannel, outboundChannel, brokerChannel, Collections.emptyList());
}
/**
* Constructor with destination prefixes to match to destinations of messages.
* @param inboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param outboundChannel the channel for sending messages to clients (e.g. WebSocket clients)
* @param brokerChannel the channel for the application to send messages to the broker
* @param destinationPrefixes prefixes to use to filter out messages
*/
public AbstractBrokerMessageHandler(SubscribableChannel inboundChannel, MessageChannel outboundChannel, SubscribableChannel brokerChannel, @Nullable Collection<String> destinationPrefixes) {
replacedert.notNull(inboundChannel, "'inboundChannel' must not be null");
replacedert.notNull(outboundChannel, "'outboundChannel' must not be null");
replacedert.notNull(brokerChannel, "'brokerChannel' must not be null");
this.clientInboundChannel = inboundChannel;
this.clientOutboundChannel = outboundChannel;
this.brokerChannel = brokerChannel;
destinationPrefixes = (destinationPrefixes != null ? destinationPrefixes : Collections.emptyList());
this.destinationPrefixes = Collections.unmodifiableCollection(destinationPrefixes);
}
public SubscribableChannel getClientInboundChannel() {
return this.clientInboundChannel;
}
public MessageChannel getClientOutboundChannel() {
return this.clientOutboundChannel;
}
public SubscribableChannel getBrokerChannel() {
return this.brokerChannel;
}
public Collection<String> getDestinationPrefixes() {
return this.destinationPrefixes;
}
/**
* Whether the client must receive messages in the order of publication.
* <p>By default messages sent to the {@code "clientOutboundChannel"} may
* not be processed in the same order because the channel is backed by a
* ThreadPoolExecutor that in turn does not guarantee processing in order.
* <p>When this flag is set to {@code true} messages within the same session
* will be sent to the {@code "clientOutboundChannel"} one at a time in
* order to preserve the order of publication. Enable this only if needed
* since there is some performance overhead to keep messages in order.
* @param preservePublishOrder whether to publish in order
* @since 5.1
*/
public void setPreservePublishOrder(boolean preservePublishOrder) {
OrderedMessageSender.configureOutboundChannel(this.clientOutboundChannel, preservePublishOrder);
this.preservePublishOrder = preservePublishOrder;
}
/**
* Whether to ensure messages are received in the order of publication.
* @since 5.1
*/
public boolean isPreservePublishOrder() {
return this.preservePublishOrder;
}
@Override
public void setApplicationEventPublisher(@Nullable ApplicationEventPublisher publisher) {
this.eventPublisher = publisher;
}
@Nullable
public ApplicationEventPublisher getApplicationEventPublisher() {
return this.eventPublisher;
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
logger.info("Starting...");
this.clientInboundChannel.subscribe(this);
this.brokerChannel.subscribe(this);
if (this.clientInboundChannel instanceof InterceptableChannel) {
((InterceptableChannel) this.clientInboundChannel).addInterceptor(0, this.unsentDisconnectInterceptor);
}
startInternal();
this.running = true;
logger.info("Started.");
}
}
protected void startInternal() {
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
logger.info("Stopping...");
stopInternal();
this.clientInboundChannel.unsubscribe(this);
this.brokerChannel.unsubscribe(this);
if (this.clientInboundChannel instanceof InterceptableChannel) {
((InterceptableChannel) this.clientInboundChannel).removeInterceptor(this.unsentDisconnectInterceptor);
}
this.running = false;
logger.info("Stopped.");
}
}
protected void stopInternal() {
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
/**
* Check whether this message handler is currently running.
* <p>Note that even when this message handler is running the
* {@link #isBrokerAvailable()} flag may still independently alternate between
* being on and off depending on the concrete sub-clreplaced implementation.
*/
@Override
public final boolean isRunning() {
return this.running;
}
/**
* Whether the message broker is currently available and able to process messages.
* <p>Note that this is in addition to the {@link #isRunning()} flag, which
* indicates whether this message handler is running. In other words the message
* handler must first be running and then the {@code #isBrokerAvailable()} flag
* may still independently alternate between being on and off depending on the
* concrete sub-clreplaced implementation.
* <p>Application components may implement
* {@code org.springframework.context.ApplicationListener<BrokerAvailabilityEvent>}
* to receive notifications when broker becomes available and unavailable.
*/
public boolean isBrokerAvailable() {
return this.brokerAvailable.get();
}
@Override
public void handleMessage(Message<?> message) {
if (!this.running) {
if (logger.isTraceEnabled()) {
logger.trace(this + " not running yet. Ignoring " + message);
}
return;
}
handleMessageInternal(message);
}
protected abstract void handleMessageInternal(Message<?> message);
protected boolean checkDestinationPrefix(@Nullable String destination) {
if (destination == null || CollectionUtils.isEmpty(this.destinationPrefixes)) {
return true;
}
for (String prefix : this.destinationPrefixes) {
if (destination.startsWith(prefix)) {
return true;
}
}
return false;
}
protected void publishBrokerAvailableEvent() {
boolean shouldPublish = this.brokerAvailable.compareAndSet(false, true);
if (this.eventPublisher != null && shouldPublish) {
if (logger.isInfoEnabled()) {
logger.info(this.availableEvent);
}
this.eventPublisher.publishEvent(this.availableEvent);
}
}
protected void publishBrokerUnavailableEvent() {
boolean shouldPublish = this.brokerAvailable.compareAndSet(true, false);
if (this.eventPublisher != null && shouldPublish) {
if (logger.isInfoEnabled()) {
logger.info(this.notAvailableEvent);
}
this.eventPublisher.publishEvent(this.notAvailableEvent);
}
}
/**
* Get the MessageChannel to use for sending messages to clients, possibly
* a per-session wrapper when {@code preservePublishOrder=true}.
* @since 5.1
*/
protected MessageChannel getClientOutboundChannelForSession(String sessionId) {
return this.preservePublishOrder ? new OrderedMessageSender(getClientOutboundChannel(), logger) : getClientOutboundChannel();
}
/**
* Detect unsent DISCONNECT messages and process them anyway.
*/
private clreplaced UnsentDisconnectChannelInterceptor implements ChannelInterceptor {
@Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, @Nullable Exception ex) {
if (!sent) {
SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(message.getHeaders());
if (SimpMessageType.DISCONNECT.equals(messageType)) {
logger.debug("Detected unsent DISCONNECT message. Processing anyway.");
handleMessage(message);
}
}
}
}
}
18
View Source File : WebSocketAnnotationMethodMessageHandlerTests.java
License : Apache License 2.0
Project Creator : SourceHot
License : Apache License 2.0
Project Creator : SourceHot
@BeforeEach
public void setUp() throws Exception {
this.applicationContext = new StaticApplicationContext();
this.applicationContext.registerSingleton("controller", TestController.clreplaced);
this.applicationContext.registerSingleton("controllerAdvice", TestControllerAdvice.clreplaced);
this.applicationContext.refresh();
SubscribableChannel channel = Mockito.mock(SubscribableChannel.clreplaced);
SimpMessageSendingOperations brokerTemplate = new SimpMessagingTemplate(channel);
this.messageHandler = new TestWebSocketAnnotationMethodMessageHandler(brokerTemplate, channel, channel);
this.messageHandler.setApplicationContext(this.applicationContext);
this.messageHandler.afterPropertiesSet();
}
18
View Source File : RpcMessageHandler.java
License : Apache License 2.0
Project Creator : ralscha
License : Apache License 2.0
Project Creator : ralscha
public clreplaced RpcMessageHandler implements MessageHandler, SmartLifecycle, InitializingBean, ApplicationContextAware {
protected final Log logger = LogFactory.getLog(getClreplaced());
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
private boolean autoStartup = true;
private volatile boolean running = false;
private final Object lifecycleMonitor = new Object();
private ApplicationContext applicationContext;
private final ProcedureRegistry procedureRegistry;
private final Map<String, InvocableHandlerMethod> wampMethods = new ConcurrentHashMap<>();
private final HandlerMethodService handlerMethodService;
private final Features features;
public RpcMessageHandler(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel, ProcedureRegistry procedureRegistry, HandlerMethodService handlerMethodService, Features features) {
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
this.procedureRegistry = procedureRegistry;
this.handlerMethodService = handlerMethodService;
this.features = features;
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
this.clientInboundChannel.subscribe(this);
this.running = true;
}
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
this.clientInboundChannel.unsubscribe(this);
this.running = false;
}
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
@Override
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return this.running;
}
}
@Override
public void handleMessage(Message<?> message) {
if (!this.running) {
if (this.logger.isTraceEnabled()) {
this.logger.trace(this + " not running yet. Ignoring " + message);
}
return;
}
if (message instanceof RegisterMessage) {
RegisterMessage registerMessage = (RegisterMessage) message;
long registrationId = this.procedureRegistry.register(registerMessage);
if (registrationId != -1) {
sendMessageToClient(new RegisteredMessage(registerMessage, registrationId));
this.applicationContext.publishEvent(new WampProcedureRegisteredEvent(registerMessage, registrationId));
} else {
sendMessageToClient(new ErrorMessage(registerMessage, WampError.PROCEDURE_ALREADY_EXISTS));
}
} else if (message instanceof UnregisterMessage) {
UnregisterMessage unregisterMessage = (UnregisterMessage) message;
UnregisterResult result = this.procedureRegistry.unregister(unregisterMessage);
if (result.isSuccess()) {
sendMessageToClient(new UnregisteredMessage(unregisterMessage));
this.applicationContext.publishEvent(new WampProcedureUnregisteredEvent(unregisterMessage, result.getProcedure(), result.getRegistrationId()));
for (ErrorMessage errorMessage : result.getInvocationErrors()) {
handleErrorMessage(errorMessage);
}
} else {
sendMessageToClient(new ErrorMessage(unregisterMessage, WampError.NO_SUCH_REGISTRATION));
}
} else if (message instanceof CallMessage) {
CallMessage callMessage = (CallMessage) message;
if (callMessage.isDiscloseMe() && this.features.isDisabled(Feature.DEALER_CALLER_IDENTIFICATION)) {
sendMessageToClient(new ErrorMessage(callMessage, WampError.DISCLOSE_ME_DISALLOWED));
}
InvocableHandlerMethod handlerMethod = this.wampMethods.get(callMessage.getProcedure());
if (handlerMethod != null) {
callWampMethod(callMessage, handlerMethod);
} else {
WampMessage errorOrInvocationMessage = this.procedureRegistry.createInvocationMessage(callMessage);
try {
this.clientOutboundChannel.send(errorOrInvocationMessage);
} catch (Throwable ex) {
if (errorOrInvocationMessage instanceof InvocationMessage) {
sendMessageToClient(new ErrorMessage(callMessage, WampError.NETWORK_FAILURE));
}
}
}
} else if (message instanceof YieldMessage) {
YieldMessage yieldMessage = (YieldMessage) message;
CallMessage callMessage = this.procedureRegistry.removeInvocationCall(yieldMessage);
if (callMessage != null) {
ResultMessage resultMessage = new ResultMessage(yieldMessage, callMessage);
sendMessageToClient(resultMessage);
}
} else if (message instanceof ErrorMessage) {
handleErrorMessage((ErrorMessage) message);
}
}
@EventListener
void handleDisconnectEvent(WampDisconnectEvent event) {
List<UnregisterResult> unregisterResults = this.procedureRegistry.unregisterWebSocketSession(event.getWebSocketSessionId());
for (UnregisterResult unregisterResult : unregisterResults) {
this.applicationContext.publishEvent(new WampProcedureUnregisteredEvent(event, unregisterResult.getProcedure(), unregisterResult.getRegistrationId()));
for (ErrorMessage errorMessage : unregisterResult.getInvocationErrors()) {
handleErrorMessage(errorMessage);
}
}
}
private void handleErrorMessage(ErrorMessage errorMessage) {
CallMessage callMessage = this.procedureRegistry.removeInvocationCall(errorMessage);
if (callMessage != null) {
ErrorMessage calErrorMessage = new ErrorMessage(errorMessage, callMessage);
sendMessageToClient(calErrorMessage);
}
}
@SuppressWarnings("unchecked")
private void callWampMethod(CallMessage callMessage, InvocableHandlerMethod handlerMethod) {
try {
Object returnValue = this.handlerMethodService.invoke(callMessage, handlerMethod);
List<Object> arguments = null;
Map<String, Object> argumentsKw = null;
if (returnValue instanceof WampResult) {
WampResult wampResult = (WampResult) returnValue;
arguments = wampResult.getResults();
argumentsKw = wampResult.getResultsKw();
} else if (returnValue instanceof List) {
arguments = (List) returnValue;
} else if (returnValue instanceof Map) {
argumentsKw = (Map) returnValue;
} else if (returnValue != null) {
arguments = Collections.singletonList(returnValue);
}
ResultMessage resultMessage = new ResultMessage(callMessage, arguments, argumentsKw);
sendMessageToClient(resultMessage);
} catch (WampException e) {
sendMessageToClient(new ErrorMessage(callMessage, e.getUri(), e.getArguments(), e.getArgumentsKw()));
if (this.logger.isDebugEnabled()) {
this.logger.debug("Error while invoking the handlerMethod " + handlerMethod, e);
}
} catch (Exception e) {
if ("org.springframework.security.access.AccessDeniedException".equals(e.getClreplaced().getName())) {
sendMessageToClient(new ErrorMessage(callMessage, WampError.NOT_AUTHORIZED));
} else {
sendMessageToClient(new ErrorMessage(callMessage, WampError.INVALID_ARGUMENT));
}
if (this.logger.isErrorEnabled()) {
this.logger.error("Error while invoking the handlerMethod " + handlerMethod, e);
}
}
}
protected void sendMessageToClient(Message<?> message) {
try {
this.clientOutboundChannel.send(message);
} catch (Throwable ex) {
this.logger.error("Failed to send " + message, ex);
}
}
@Override
public void afterPropertiesSet() throws Exception {
for (String beanName : this.applicationContext.getBeanNamesForType(Object.clreplaced)) {
Clreplaced<?> handlerType = this.applicationContext.getType(beanName);
if (handlerType != null) {
final Clreplaced<?> userType = ClreplacedUtils.getUserClreplaced(handlerType);
detectWampMethods(beanName, userType);
}
}
}
private void detectWampMethods(String beanName, Clreplaced<?> userType) {
Set<Method> methods = MethodIntrospector.selectMethods(userType, (MethodFilter) method -> AnnotationUtils.findAnnotation(method, WampProcedure.clreplaced) != null);
for (Method method : methods) {
WampProcedure annotation = AnnotationUtils.findAnnotation(method, WampProcedure.clreplaced);
InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(new HandlerMethod(this.applicationContext.getBean(beanName), method));
String procedure = (String) AnnotationUtils.getValue(annotation);
if (!StringUtils.hasText(procedure)) {
procedure = beanName + "." + method.getName();
}
this.wampMethods.put(procedure, handlerMethod);
if (this.logger.isInfoEnabled()) {
this.logger.info("Mapped \"" + procedure + "\" onto " + handlerMethod);
}
}
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
18
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
@Test
public void sendAndReceive() {
SubscribableChannel channel = new ExecutorSubscribableChannel(this.executor);
channel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
replyChannel.send(new GenericMessage<String>("response"));
}
});
String actual = this.template.convertSendAndReceive(channel, "request", String.clreplaced);
replacedertEquals("response", actual);
}
18
View Source File : GenericMessagingTemplateTests.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
@Test
public void sendAndReceiveTimeout() throws InterruptedException {
final AtomicReference<Throwable> failure = new AtomicReference<Throwable>();
final CountDownLatch latch = new CountDownLatch(1);
this.template.setReceiveTimeout(1);
this.template.setThrowExceptionOnLateReply(true);
SubscribableChannel channel = new ExecutorSubscribableChannel(this.executor);
channel.subscribe(new MessageHandler() {
@Override
public void handleMessage(Message<?> message) throws MessagingException {
try {
Thread.sleep(500);
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
replyChannel.send(new GenericMessage<String>("response"));
failure.set(new IllegalStateException("Expected exception"));
} catch (InterruptedException e) {
failure.set(e);
} catch (MessageDeliveryException ex) {
String expected = "Reply message received but the receiving thread has exited due to a timeout";
String actual = ex.getMessage();
if (!expected.equals(actual)) {
failure.set(new IllegalStateException("Unexpected error: '" + actual + "'"));
}
} finally {
latch.countDown();
}
}
});
replacedertNull(this.template.convertSendAndReceive(channel, "request", String.clreplaced));
replacedertTrue(latch.await(1000, TimeUnit.MILLISECONDS));
if (failure.get() != null) {
throw new replacedertionError(failure.get());
}
}
18
View Source File : MessageBrokerRegistry.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
/**
* A registry for configuring message broker options.
*
* @author Rossen Stoyanchev
* @author Sebastien Deleuze
* @since 4.0
*/
public clreplaced MessageBrokerRegistry {
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
private SimpleBrokerRegistration simpleBrokerRegistration;
private StompBrokerRelayRegistration brokerRelayRegistration;
private final ChannelRegistration brokerChannelRegistration = new ChannelRegistration();
private String[] applicationDestinationPrefixes;
private String userDestinationPrefix;
private PathMatcher pathMatcher;
public MessageBrokerRegistry(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel) {
replacedert.notNull(clientInboundChannel);
replacedert.notNull(clientOutboundChannel);
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
}
/**
* Enable a simple message broker and configure one or more prefixes to filter
* destinations targeting the broker (e.g. destinations prefixed with "/topic").
*/
public SimpleBrokerRegistration enableSimpleBroker(String... destinationPrefixes) {
this.simpleBrokerRegistration = new SimpleBrokerRegistration(this.clientInboundChannel, this.clientOutboundChannel, destinationPrefixes);
return this.simpleBrokerRegistration;
}
/**
* Enable a STOMP broker relay and configure the destination prefixes supported by the
* message broker. Check the STOMP doreplacedentation of the message broker for supported
* destinations.
*/
public StompBrokerRelayRegistration enableStompBrokerRelay(String... destinationPrefixes) {
this.brokerRelayRegistration = new StompBrokerRelayRegistration(this.clientInboundChannel, this.clientOutboundChannel, destinationPrefixes);
return this.brokerRelayRegistration;
}
/**
* Customize the channel used to send messages from the application to the message
* broker. By default, messages from the application to the message broker are sent
* synchronously, which means application code sending a message will find out
* if the message cannot be sent through an exception. However, this can be changed
* if the broker channel is configured here with task executor properties.
*/
public ChannelRegistration configureBrokerChannel() {
return this.brokerChannelRegistration;
}
protected ChannelRegistration getBrokerChannelRegistration() {
return this.brokerChannelRegistration;
}
/**
* Configure one or more prefixes to filter destinations targeting application
* annotated methods. For example destinations prefixed with "/app" may be
* processed by annotated methods while other destinations may target the
* message broker (e.g. "/topic", "/queue").
* <p>When messages are processed, the matching prefix is removed from the destination
* in order to form the lookup path. This means annotations should not contain the
* destination prefix.
* <p>Prefixes that do not have a trailing slash will have one automatically appended.
*/
public MessageBrokerRegistry setApplicationDestinationPrefixes(String... prefixes) {
this.applicationDestinationPrefixes = prefixes;
return this;
}
protected Collection<String> getApplicationDestinationPrefixes() {
return (this.applicationDestinationPrefixes != null ? Arrays.asList(this.applicationDestinationPrefixes) : null);
}
/**
* Configure the prefix used to identify user destinations. User destinations
* provide the ability for a user to subscribe to queue names unique to their
* session as well as for others to send messages to those unique,
* user-specific queues.
* <p>For example when a user attempts to subscribe to "/user/queue/position-updates",
* the destination may be translated to "/queue/position-updatesi9oqdfzo" yielding a
* unique queue name that does not collide with any other user attempting to do the same.
* Subsequently when messages are sent to "/user/{username}/queue/position-updates",
* the destination is translated to "/queue/position-updatesi9oqdfzo".
* <p>The default prefix used to identify such destinations is "/user/".
*/
public MessageBrokerRegistry setUserDestinationPrefix(String destinationPrefix) {
this.userDestinationPrefix = destinationPrefix;
return this;
}
protected String getUserDestinationPrefix() {
return this.userDestinationPrefix;
}
protected String getUserDestinationBroadcast() {
return (this.brokerRelayRegistration != null ? this.brokerRelayRegistration.getUserDestinationBroadcast() : null);
}
protected String getUserRegistryBroadcast() {
return (this.brokerRelayRegistration != null ? this.brokerRelayRegistration.getUserRegistryBroadcast() : null);
}
/**
* Configure the PathMatcher to use to match the destinations of incoming
* messages to {@code @MessageMapping} and {@code @SubscribeMapping} methods.
* <p>By default {@link org.springframework.util.AntPathMatcher} is configured.
* However applications may provide an {@code AntPathMatcher} instance
* customized to use "." (commonly used in messaging) instead of "/" as path
* separator or provide a completely different PathMatcher implementation.
* <p>Note that the configured PathMatcher is only used for matching the
* portion of the destination after the configured prefix. For example given
* application destination prefix "/app" and destination "/app/price.stock.**",
* the message might be mapped to a controller with "price" and "stock.**"
* as its type and method-level mappings respectively.
* <p>When the simple broker is enabled, the PathMatcher configured here is
* also used to match message destinations when brokering messages.
* @since 4.1
*/
public MessageBrokerRegistry setPathMatcher(PathMatcher pathMatcher) {
this.pathMatcher = pathMatcher;
return this;
}
protected PathMatcher getPathMatcher() {
return this.pathMatcher;
}
protected SimpleBrokerMessageHandler getSimpleBroker(SubscribableChannel brokerChannel) {
if (this.simpleBrokerRegistration == null && this.brokerRelayRegistration == null) {
enableSimpleBroker();
}
if (this.simpleBrokerRegistration != null) {
SimpleBrokerMessageHandler handler = this.simpleBrokerRegistration.getMessageHandler(brokerChannel);
handler.setPathMatcher(this.pathMatcher);
return handler;
}
return null;
}
protected StompBrokerRelayMessageHandler getStompBrokerRelay(SubscribableChannel brokerChannel) {
if (this.brokerRelayRegistration != null) {
return this.brokerRelayRegistration.getMessageHandler(brokerChannel);
}
return null;
}
}
18
View Source File : AbstractBrokerRegistration.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
/**
* Base clreplaced for message broker registration clreplacedes.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract clreplaced AbstractBrokerRegistration {
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
private final List<String> destinationPrefixes;
public AbstractBrokerRegistration(SubscribableChannel clientInboundChannel, MessageChannel clientOutboundChannel, String[] destinationPrefixes) {
replacedert.notNull(clientOutboundChannel, "'clientInboundChannel' must not be null");
replacedert.notNull(clientOutboundChannel, "'clientOutboundChannel' must not be null");
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
this.destinationPrefixes = (destinationPrefixes != null) ? Arrays.<String>asList(destinationPrefixes) : Collections.<String>emptyList();
}
protected SubscribableChannel getClientInboundChannel() {
return this.clientInboundChannel;
}
protected MessageChannel getClientOutboundChannel() {
return this.clientOutboundChannel;
}
protected Collection<String> getDestinationPrefixes() {
return this.destinationPrefixes;
}
protected abstract AbstractBrokerMessageHandler getMessageHandler(SubscribableChannel brokerChannel);
}
18
View Source File : AbstractBrokerMessageHandler.java
License : Apache License 2.0
Project Creator : langtianya
License : Apache License 2.0
Project Creator : langtianya
/**
* Abstract base clreplaced for a {@link MessageHandler} that broker messages to
* registered subscribers.
*
* @author Rossen Stoyanchev
* @since 4.0
*/
public abstract clreplaced AbstractBrokerMessageHandler implements MessageHandler, ApplicationEventPublisherAware, SmartLifecycle {
protected final Log logger = LogFactory.getLog(getClreplaced());
private final SubscribableChannel clientInboundChannel;
private final MessageChannel clientOutboundChannel;
private final SubscribableChannel brokerChannel;
private final Collection<String> destinationPrefixes;
private ApplicationEventPublisher eventPublisher;
private AtomicBoolean brokerAvailable = new AtomicBoolean(false);
private final BrokerAvailabilityEvent availableEvent = new BrokerAvailabilityEvent(true, this);
private final BrokerAvailabilityEvent notAvailableEvent = new BrokerAvailabilityEvent(false, this);
private boolean autoStartup = true;
private volatile boolean running = false;
private final Object lifecycleMonitor = new Object();
private final ChannelInterceptor unsentDisconnectInterceptor = new UnsentDisconnectChannelInterceptor();
/**
* Constructor with no destination prefixes (matches all destinations).
* @param inboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param outboundChannel the channel for sending messages to clients (e.g. WebSocket clients)
* @param brokerChannel the channel for the application to send messages to the broker
*/
public AbstractBrokerMessageHandler(SubscribableChannel inboundChannel, MessageChannel outboundChannel, SubscribableChannel brokerChannel) {
this(inboundChannel, outboundChannel, brokerChannel, Collections.<String>emptyList());
}
/**
* Constructor with destination prefixes to match to destinations of messages.
* @param inboundChannel the channel for receiving messages from clients (e.g. WebSocket clients)
* @param outboundChannel the channel for sending messages to clients (e.g. WebSocket clients)
* @param brokerChannel the channel for the application to send messages to the broker
* @param destinationPrefixes prefixes to use to filter out messages
*/
public AbstractBrokerMessageHandler(SubscribableChannel inboundChannel, MessageChannel outboundChannel, SubscribableChannel brokerChannel, Collection<String> destinationPrefixes) {
replacedert.notNull(inboundChannel, "'inboundChannel' must not be null");
replacedert.notNull(outboundChannel, "'outboundChannel' must not be null");
replacedert.notNull(brokerChannel, "'brokerChannel' must not be null");
this.clientInboundChannel = inboundChannel;
this.clientOutboundChannel = outboundChannel;
this.brokerChannel = brokerChannel;
destinationPrefixes = (destinationPrefixes != null) ? destinationPrefixes : Collections.<String>emptyList();
this.destinationPrefixes = Collections.unmodifiableCollection(destinationPrefixes);
}
public SubscribableChannel getClientInboundChannel() {
return this.clientInboundChannel;
}
public MessageChannel getClientOutboundChannel() {
return this.clientOutboundChannel;
}
public SubscribableChannel getBrokerChannel() {
return this.brokerChannel;
}
public Collection<String> getDestinationPrefixes() {
return this.destinationPrefixes;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.eventPublisher = publisher;
}
public ApplicationEventPublisher getApplicationEventPublisher() {
return this.eventPublisher;
}
public void setAutoStartup(boolean autoStartup) {
this.autoStartup = autoStartup;
}
@Override
public boolean isAutoStartup() {
return this.autoStartup;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
@Override
public void start() {
synchronized (this.lifecycleMonitor) {
if (logger.isInfoEnabled()) {
logger.info("Starting...");
}
this.clientInboundChannel.subscribe(this);
this.brokerChannel.subscribe(this);
if (this.clientInboundChannel instanceof InterceptableChannel) {
((InterceptableChannel) this.clientInboundChannel).addInterceptor(0, this.unsentDisconnectInterceptor);
}
startInternal();
this.running = true;
logger.info("Started.");
}
}
protected void startInternal() {
}
@Override
public void stop() {
synchronized (this.lifecycleMonitor) {
if (logger.isInfoEnabled()) {
logger.info("Stopping...");
}
stopInternal();
this.clientInboundChannel.unsubscribe(this);
this.brokerChannel.unsubscribe(this);
if (this.clientInboundChannel instanceof InterceptableChannel) {
((InterceptableChannel) this.clientInboundChannel).removeInterceptor(this.unsentDisconnectInterceptor);
}
this.running = false;
logger.info("Stopped.");
}
}
protected void stopInternal() {
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
/**
* Check whether this message handler is currently running.
* <p>Note that even when this message handler is running the
* {@link #isBrokerAvailable()} flag may still independently alternate between
* being on and off depending on the concrete sub-clreplaced implementation.
*/
@Override
public final boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return this.running;
}
}
/**
* Whether the message broker is currently available and able to process messages.
* <p>Note that this is in addition to the {@link #isRunning()} flag, which
* indicates whether this message handler is running. In other words the message
* handler must first be running and then the {@code #isBrokerAvailable()} flag
* may still independently alternate between being on and off depending on the
* concrete sub-clreplaced implementation.
* <p>Application components may implement
* {@code org.springframework.context.ApplicationListener<BrokerAvailabilityEvent>}
* to receive notifications when broker becomes available and unavailable.
*/
public boolean isBrokerAvailable() {
return this.brokerAvailable.get();
}
@Override
public void handleMessage(Message<?> message) {
if (!this.running) {
if (logger.isTraceEnabled()) {
logger.trace(this + " not running yet. Ignoring " + message);
}
return;
}
handleMessageInternal(message);
}
protected abstract void handleMessageInternal(Message<?> message);
protected boolean checkDestinationPrefix(String destination) {
if ((destination == null) || CollectionUtils.isEmpty(this.destinationPrefixes)) {
return true;
}
for (String prefix : this.destinationPrefixes) {
if (destination.startsWith(prefix)) {
return true;
}
}
return false;
}
protected void publishBrokerAvailableEvent() {
boolean shouldPublish = this.brokerAvailable.compareAndSet(false, true);
if (this.eventPublisher != null && shouldPublish) {
if (logger.isInfoEnabled()) {
logger.info(this.availableEvent);
}
this.eventPublisher.publishEvent(this.availableEvent);
}
}
protected void publishBrokerUnavailableEvent() {
boolean shouldPublish = this.brokerAvailable.compareAndSet(true, false);
if (this.eventPublisher != null && shouldPublish) {
if (logger.isInfoEnabled()) {
logger.info(this.notAvailableEvent);
}
this.eventPublisher.publishEvent(this.notAvailableEvent);
}
}
/**
* Detect unsent DISCONNECT messages and process them anyway.
*/
private clreplaced UnsentDisconnectChannelInterceptor extends ChannelInterceptorAdapter {
@Override
public void afterSendCompletion(Message<?> message, MessageChannel channel, boolean sent, Exception ex) {
if (!sent) {
SimpMessageType messageType = SimpMessageHeaderAccessor.getMessageType(message.getHeaders());
if (SimpMessageType.DISCONNECT.equals(messageType)) {
logger.debug("Detected unsent DISCONNECT message. Processing anyway.");
handleMessage(message);
}
}
}
}
}
17
View Source File : SubProtocolWebSocketHandlerTests.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* Test fixture for {@link SubProtocolWebSocketHandler}.
*
* @author Rossen Stoyanchev
* @author Andy Wilkinson
*/
public clreplaced SubProtocolWebSocketHandlerTests {
private SubProtocolWebSocketHandler webSocketHandler;
private TestWebSocketSession session;
@Mock
SubProtocolHandler stompHandler;
@Mock
SubProtocolHandler mqttHandler;
@Mock
SubProtocolHandler defaultHandler;
@Mock
MessageChannel inClientChannel;
@Mock
SubscribableChannel outClientChannel;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
this.webSocketHandler = new SubProtocolWebSocketHandler(this.inClientChannel, this.outClientChannel);
given(stompHandler.getSupportedProtocols()).willReturn(Arrays.asList("v10.stomp", "v11.stomp", "v12.stomp"));
given(mqttHandler.getSupportedProtocols()).willReturn(Arrays.asList("MQTT"));
this.session = new TestWebSocketSession();
this.session.setId("1");
this.session.setOpen(true);
}
@Test
public void subProtocolMatch() throws Exception {
this.webSocketHandler.setProtocolHandlers(Arrays.asList(stompHandler, mqttHandler));
this.session.setAcceptedProtocol("v12.sToMp");
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.stompHandler).afterSessionStarted(isA(ConcurrentWebSocketSessionDecorator.clreplaced), eq(this.inClientChannel));
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
}
@Test
public void subProtocolDefaultHandlerOnly() throws Exception {
this.webSocketHandler.setDefaultProtocolHandler(stompHandler);
this.session.setAcceptedProtocol("v12.sToMp");
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.stompHandler).afterSessionStarted(isA(ConcurrentWebSocketSessionDecorator.clreplaced), eq(this.inClientChannel));
}
@Test(expected = IllegalStateException.clreplaced)
public void subProtocolNoMatch() throws Exception {
this.webSocketHandler.setDefaultProtocolHandler(defaultHandler);
this.webSocketHandler.setProtocolHandlers(Arrays.asList(stompHandler, mqttHandler));
this.session.setAcceptedProtocol("wamp");
this.webSocketHandler.afterConnectionEstablished(session);
}
@Test
public void nullSubProtocol() throws Exception {
this.webSocketHandler.setDefaultProtocolHandler(defaultHandler);
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.defaultHandler).afterSessionStarted(isA(ConcurrentWebSocketSessionDecorator.clreplaced), eq(this.inClientChannel));
verify(this.stompHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
}
@Test
public void emptySubProtocol() throws Exception {
this.session.setAcceptedProtocol("");
this.webSocketHandler.setDefaultProtocolHandler(this.defaultHandler);
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.defaultHandler).afterSessionStarted(isA(ConcurrentWebSocketSessionDecorator.clreplaced), eq(this.inClientChannel));
verify(this.stompHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
verify(this.mqttHandler, times(0)).afterSessionStarted(session, this.inClientChannel);
}
@Test
public void noSubProtocolOneHandler() throws Exception {
this.webSocketHandler.setProtocolHandlers(Arrays.asList(stompHandler));
this.webSocketHandler.afterConnectionEstablished(session);
verify(this.stompHandler).afterSessionStarted(isA(ConcurrentWebSocketSessionDecorator.clreplaced), eq(this.inClientChannel));
}
@Test(expected = IllegalStateException.clreplaced)
public void noSubProtocolTwoHandlers() throws Exception {
this.webSocketHandler.setProtocolHandlers(Arrays.asList(stompHandler, mqttHandler));
this.webSocketHandler.afterConnectionEstablished(session);
}
@Test(expected = IllegalStateException.clreplaced)
public void noSubProtocolNoDefaultHandler() throws Exception {
this.webSocketHandler.setProtocolHandlers(Arrays.asList(stompHandler, mqttHandler));
this.webSocketHandler.afterConnectionEstablished(session);
}
@Test
@SuppressWarnings("unchecked")
public void checkSession() throws Exception {
TestWebSocketSession session1 = new TestWebSocketSession("id1");
TestWebSocketSession session2 = new TestWebSocketSession("id2");
session1.setOpen(true);
session2.setOpen(true);
session1.setAcceptedProtocol("v12.stomp");
session2.setAcceptedProtocol("v12.stomp");
this.webSocketHandler.setProtocolHandlers(Arrays.asList(this.stompHandler));
this.webSocketHandler.afterConnectionEstablished(session1);
this.webSocketHandler.afterConnectionEstablished(session2);
DirectFieldAccessor handlerAccessor = new DirectFieldAccessor(this.webSocketHandler);
Map<String, ?> map = (Map<String, ?>) handlerAccessor.getPropertyValue("sessions");
DirectFieldAccessor session1Accessor = new DirectFieldAccessor(map.get("id1"));
DirectFieldAccessor session2Accessor = new DirectFieldAccessor(map.get("id2"));
long sixtyOneSecondsAgo = System.currentTimeMillis() - 61 * 1000;
handlerAccessor.setPropertyValue("lastSessionCheckTime", sixtyOneSecondsAgo);
session1Accessor.setPropertyValue("createTime", sixtyOneSecondsAgo);
session2Accessor.setPropertyValue("createTime", sixtyOneSecondsAgo);
this.webSocketHandler.start();
this.webSocketHandler.handleMessage(session1, new TextMessage("foo"));
replacedertTrue(session1.isOpen());
replacedertNull(session1.getCloseStatus());
replacedertFalse(session2.isOpen());
replacedertEquals(CloseStatus.SESSION_NOT_RELIABLE, session2.getCloseStatus());
replacedertNotEquals("lastSessionCheckTime not updated", sixtyOneSecondsAgo, handlerAccessor.getPropertyValue("lastSessionCheckTime"));
}
}
17
View Source File : SubProtocolWebSocketHandler.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
/**
* An implementation of {@link WebSocketHandler} that delegates incoming WebSocket
* messages to a {@link SubProtocolHandler} along with a {@link MessageChannel} to which
* the sub-protocol handler can send messages from WebSocket clients to the application.
*
* <p>Also an implementation of {@link MessageHandler} that finds the WebSocket session
* replacedociated with the {@link Message} and preplacedes it, along with the message, to the
* sub-protocol handler to send messages from the application back to the client.
*
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @author Andy Wilkinson
* @author Artem Bilan
* @since 4.0
*/
public clreplaced SubProtocolWebSocketHandler implements WebSocketHandler, SubProtocolCapable, MessageHandler, SmartLifecycle {
/**
* The default value for {@link #setTimeToFirstMessage(int) timeToFirstMessage}.
*/
private static final int DEFAULT_TIME_TO_FIRST_MESSAGE = 60 * 1000;
private final Log logger = LogFactory.getLog(SubProtocolWebSocketHandler.clreplaced);
private final MessageChannel clientInboundChannel;
private final SubscribableChannel clientOutboundChannel;
private final Map<String, SubProtocolHandler> protocolHandlerLookup = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
private final Set<SubProtocolHandler> protocolHandlers = new LinkedHashSet<>();
@Nullable
private SubProtocolHandler defaultProtocolHandler;
private final Map<String, WebSocketSessionHolder> sessions = new ConcurrentHashMap<>();
private int sendTimeLimit = 10 * 1000;
private int sendBufferSizeLimit = 512 * 1024;
private int timeToFirstMessage = DEFAULT_TIME_TO_FIRST_MESSAGE;
private volatile long lastSessionCheckTime = System.currentTimeMillis();
private final ReentrantLock sessionCheckLock = new ReentrantLock();
private final DefaultStats stats = new DefaultStats();
private volatile boolean running = false;
private final Object lifecycleMonitor = new Object();
/**
* Create a new {@code SubProtocolWebSocketHandler} for the given inbound and outbound channels.
* @param clientInboundChannel the inbound {@code MessageChannel}
* @param clientOutboundChannel the outbound {@code MessageChannel}
*/
public SubProtocolWebSocketHandler(MessageChannel clientInboundChannel, SubscribableChannel clientOutboundChannel) {
replacedert.notNull(clientInboundChannel, "Inbound MessageChannel must not be null");
replacedert.notNull(clientOutboundChannel, "Outbound MessageChannel must not be null");
this.clientInboundChannel = clientInboundChannel;
this.clientOutboundChannel = clientOutboundChannel;
}
/**
* Configure one or more handlers to use depending on the sub-protocol requested by
* the client in the WebSocket handshake request.
* @param protocolHandlers the sub-protocol handlers to use
*/
public void setProtocolHandlers(List<SubProtocolHandler> protocolHandlers) {
this.protocolHandlerLookup.clear();
this.protocolHandlers.clear();
for (SubProtocolHandler handler : protocolHandlers) {
addProtocolHandler(handler);
}
}
public List<SubProtocolHandler> getProtocolHandlers() {
return new ArrayList<>(this.protocolHandlers);
}
/**
* Register a sub-protocol handler.
*/
public void addProtocolHandler(SubProtocolHandler handler) {
List<String> protocols = handler.getSupportedProtocols();
if (CollectionUtils.isEmpty(protocols)) {
if (logger.isErrorEnabled()) {
logger.error("No sub-protocols for " + handler);
}
return;
}
for (String protocol : protocols) {
SubProtocolHandler replaced = this.protocolHandlerLookup.put(protocol, handler);
if (replaced != null && replaced != handler) {
throw new IllegalStateException("Cannot map " + handler + " to protocol '" + protocol + "': already mapped to " + replaced + ".");
}
}
this.protocolHandlers.add(handler);
}
/**
* Return the sub-protocols keyed by protocol name.
*/
public Map<String, SubProtocolHandler> getProtocolHandlerMap() {
return this.protocolHandlerLookup;
}
/**
* Set the {@link SubProtocolHandler} to use when the client did not request a
* sub-protocol.
* @param defaultProtocolHandler the default handler
*/
public void setDefaultProtocolHandler(@Nullable SubProtocolHandler defaultProtocolHandler) {
this.defaultProtocolHandler = defaultProtocolHandler;
if (this.protocolHandlerLookup.isEmpty()) {
setProtocolHandlers(Collections.singletonList(defaultProtocolHandler));
}
}
/**
* Return the default sub-protocol handler to use.
*/
@Nullable
public SubProtocolHandler getDefaultProtocolHandler() {
return this.defaultProtocolHandler;
}
/**
* Return all supported protocols.
*/
public List<String> getSubProtocols() {
return new ArrayList<>(this.protocolHandlerLookup.keySet());
}
/**
* Specify the send-time limit (milliseconds).
* @see ConcurrentWebSocketSessionDecorator
*/
public void setSendTimeLimit(int sendTimeLimit) {
this.sendTimeLimit = sendTimeLimit;
}
/**
* Return the send-time limit (milliseconds).
*/
public int getSendTimeLimit() {
return this.sendTimeLimit;
}
/**
* Specify the buffer-size limit (number of bytes).
* @see ConcurrentWebSocketSessionDecorator
*/
public void setSendBufferSizeLimit(int sendBufferSizeLimit) {
this.sendBufferSizeLimit = sendBufferSizeLimit;
}
/**
* Return the buffer-size limit (number of bytes).
*/
public int getSendBufferSizeLimit() {
return this.sendBufferSizeLimit;
}
/**
* Set the maximum time allowed in milliseconds after the WebSocket connection
* is established and before the first sub-protocol message is received.
* <p>This handler is for WebSocket connections that use a sub-protocol.
* Therefore, we expect the client to send at least one sub-protocol message
* in the beginning, or else we replacedume the connection isn't doing well, e.g.
* proxy issue, slow network, and can be closed.
* <p>By default this is set to {@code 60,000} (1 minute).
* @param timeToFirstMessage the maximum time allowed in milliseconds
* @since 5.1
* @see #checkSessions()
*/
public void setTimeToFirstMessage(int timeToFirstMessage) {
this.timeToFirstMessage = timeToFirstMessage;
}
/**
* Return the maximum time allowed after the WebSocket connection is
* established and before the first sub-protocol message.
* @since 5.1
*/
public int getTimeToFirstMessage() {
return this.timeToFirstMessage;
}
/**
* Return a String describing internal state and counters.
*/
public String getStatsInfo() {
return this.stats.toString();
}
/**
* Return a {@link Stats} object that contains various session counters.
* @since 5.2
*/
public Stats getStats() {
return this.stats;
}
@Override
public final void start() {
replacedert.isTrue(this.defaultProtocolHandler != null || !this.protocolHandlers.isEmpty(), "No handlers");
synchronized (this.lifecycleMonitor) {
this.clientOutboundChannel.subscribe(this);
this.running = true;
}
}
@Override
public final void stop() {
synchronized (this.lifecycleMonitor) {
this.running = false;
this.clientOutboundChannel.unsubscribe(this);
}
// Proactively notify all active WebSocket sessions
for (WebSocketSessionHolder holder : this.sessions.values()) {
try {
holder.getSession().close(CloseStatus.GOING_AWAY);
} catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to close '" + holder.getSession() + "': " + ex);
}
}
}
}
@Override
public final void stop(Runnable callback) {
synchronized (this.lifecycleMonitor) {
stop();
callback.run();
}
}
@Override
public final boolean isRunning() {
return this.running;
}
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// WebSocketHandlerDecorator could close the session
if (!session.isOpen()) {
return;
}
this.stats.incrementSessionCount(session);
session = decorateSession(session);
this.sessions.put(session.getId(), new WebSocketSessionHolder(session));
findProtocolHandler(session).afterSessionStarted(session, this.clientInboundChannel);
}
/**
* Handle an inbound message from a WebSocket client.
*/
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
WebSocketSessionHolder holder = this.sessions.get(session.getId());
if (holder != null) {
session = holder.getSession();
}
SubProtocolHandler protocolHandler = findProtocolHandler(session);
protocolHandler.handleMessageFromClient(session, message, this.clientInboundChannel);
if (holder != null) {
holder.setHasHandledMessages();
}
checkSessions();
}
/**
* Handle an outbound Spring Message to a WebSocket client.
*/
@Override
public void handleMessage(Message<?> message) throws MessagingException {
String sessionId = resolveSessionId(message);
if (sessionId == null) {
if (logger.isErrorEnabled()) {
logger.error("Could not find session id in " + message);
}
return;
}
WebSocketSessionHolder holder = this.sessions.get(sessionId);
if (holder == null) {
if (logger.isDebugEnabled()) {
// The broker may not have removed the session yet
logger.debug("No session for " + message);
}
return;
}
WebSocketSession session = holder.getSession();
try {
findProtocolHandler(session).handleMessageToClient(session, message);
} catch (SessionLimitExceededException ex) {
try {
if (logger.isDebugEnabled()) {
logger.debug("Terminating '" + session + "'", ex);
}
this.stats.incrementLimitExceededCount();
// clear first, session may be unresponsive
clearSession(session, ex.getStatus());
session.close(ex.getStatus());
} catch (Exception secondException) {
logger.debug("Failure while closing session " + sessionId + ".", secondException);
}
} catch (Exception ex) {
// Could be part of normal workflow (e.g. browser tab closed)
if (logger.isDebugEnabled()) {
logger.debug("Failed to send message to client in " + session + ": " + message, ex);
}
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
this.stats.incrementTransportError();
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
clearSession(session, closeStatus);
}
@Override
public boolean supportsPartialMessages() {
return false;
}
/**
* Decorate the given {@link WebSocketSession}, if desired.
* <p>The default implementation builds a {@link ConcurrentWebSocketSessionDecorator}
* with the configured {@link #getSendTimeLimit() send-time limit} and
* {@link #getSendBufferSizeLimit() buffer-size limit}.
* @param session the original {@code WebSocketSession}
* @return the decorated {@code WebSocketSession}, or potentially the given session as-is
* @since 4.3.13
*/
protected WebSocketSession decorateSession(WebSocketSession session) {
return new ConcurrentWebSocketSessionDecorator(session, getSendTimeLimit(), getSendBufferSizeLimit());
}
/**
* Find a {@link SubProtocolHandler} for the given session.
* @param session the {@code WebSocketSession} to find a handler for
*/
protected final SubProtocolHandler findProtocolHandler(WebSocketSession session) {
String protocol = null;
try {
protocol = session.getAcceptedProtocol();
} catch (Exception ex) {
// Shouldn't happen
logger.error("Failed to obtain session.getAcceptedProtocol(): " + "will use the default protocol handler (if configured).", ex);
}
SubProtocolHandler handler;
if (StringUtils.hasLength(protocol)) {
handler = this.protocolHandlerLookup.get(protocol);
if (handler == null) {
throw new IllegalStateException("No handler for '" + protocol + "' among " + this.protocolHandlerLookup);
}
} else {
if (this.defaultProtocolHandler != null) {
handler = this.defaultProtocolHandler;
} else if (this.protocolHandlers.size() == 1) {
handler = this.protocolHandlers.iterator().next();
} else {
throw new IllegalStateException("Multiple protocol handlers configured and " + "no protocol was negotiated. Consider configuring a default SubProtocolHandler.");
}
}
return handler;
}
@Nullable
private String resolveSessionId(Message<?> message) {
for (SubProtocolHandler handler : this.protocolHandlerLookup.values()) {
String sessionId = handler.resolveSessionId(message);
if (sessionId != null) {
return sessionId;
}
}
if (this.defaultProtocolHandler != null) {
String sessionId = this.defaultProtocolHandler.resolveSessionId(message);
if (sessionId != null) {
return sessionId;
}
}
return null;
}
/**
* When a session is connected through a higher-level protocol it has a chance
* to use heartbeat management to shut down sessions that are too slow to send
* or receive messages. However, after a WebSocketSession is established and
* before the higher level protocol is fully connected there is a possibility for
* sessions to hang. This method checks and closes any sessions that have been
* connected for more than 60 seconds without having received a single message.
*/
private void checkSessions() {
long currentTime = System.currentTimeMillis();
if (!isRunning() || (currentTime - this.lastSessionCheckTime < getTimeToFirstMessage())) {
return;
}
if (this.sessionCheckLock.tryLock()) {
try {
for (WebSocketSessionHolder holder : this.sessions.values()) {
if (holder.hasHandledMessages()) {
continue;
}
long timeSinceCreated = currentTime - holder.getCreateTime();
if (timeSinceCreated < getTimeToFirstMessage()) {
continue;
}
WebSocketSession session = holder.getSession();
if (logger.isInfoEnabled()) {
logger.info("No messages received after " + timeSinceCreated + " ms. " + "Closing " + holder.getSession() + ".");
}
try {
this.stats.incrementNoMessagesReceivedCount();
session.close(CloseStatus.SESSION_NOT_RELIABLE);
} catch (Throwable ex) {
if (logger.isWarnEnabled()) {
logger.warn("Failed to close unreliable " + session, ex);
}
}
}
} finally {
this.lastSessionCheckTime = currentTime;
this.sessionCheckLock.unlock();
}
}
}
private void clearSession(WebSocketSession session, CloseStatus closeStatus) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Clearing session " + session.getId());
}
if (this.sessions.remove(session.getId()) != null) {
this.stats.decrementSessionCount(session);
}
findProtocolHandler(session).afterSessionEnded(session, closeStatus, this.clientInboundChannel);
}
@Override
public String toString() {
return "SubProtocolWebSocketHandler" + this.protocolHandlers;
}
private static clreplaced WebSocketSessionHolder {
private final WebSocketSession session;
private final long createTime;
private volatile boolean hasHandledMessages;
public WebSocketSessionHolder(WebSocketSession session) {
this.session = session;
this.createTime = System.currentTimeMillis();
}
public WebSocketSession getSession() {
return this.session;
}
public long getCreateTime() {
return this.createTime;
}
public void setHasHandledMessages() {
this.hasHandledMessages = true;
}
public boolean hasHandledMessages() {
return this.hasHandledMessages;
}
@Override
public String toString() {
return "WebSocketSessionHolder[session=" + this.session + ", createTime=" + this.createTime + ", hasHandledMessages=" + this.hasHandledMessages + "]";
}
}
/**
* Contract for access to session counters.
* @since 5.2
*/
public interface Stats {
int getTotalSessions();
int getWebSocketSessions();
int getHttpStreamingSessions();
int getHttpPollingSessions();
int getLimitExceededSessions();
int getNoMessagesReceivedSessions();
int getTransportErrorSessions();
}
private clreplaced DefaultStats implements Stats {
private final AtomicInteger total = new AtomicInteger();
private final AtomicInteger webSocket = new AtomicInteger();
private final AtomicInteger httpStreaming = new AtomicInteger();
private final AtomicInteger httpPolling = new AtomicInteger();
private final AtomicInteger limitExceeded = new AtomicInteger();
private final AtomicInteger noMessagesReceived = new AtomicInteger();
private final AtomicInteger transportError = new AtomicInteger();
@Override
public int getTotalSessions() {
return this.total.get();
}
@Override
public int getWebSocketSessions() {
return this.webSocket.get();
}
@Override
public int getHttpStreamingSessions() {
return this.httpStreaming.get();
}
@Override
public int getHttpPollingSessions() {
return this.httpPolling.get();
}
@Override
public int getLimitExceededSessions() {
return this.limitExceeded.get();
}
@Override
public int getNoMessagesReceivedSessions() {
return this.noMessagesReceived.get();
}
@Override
public int getTransportErrorSessions() {
return this.transportError.get();
}
void incrementSessionCount(WebSocketSession session) {
getCountFor(session).incrementAndGet();
this.total.incrementAndGet();
}
void decrementSessionCount(WebSocketSession session) {
getCountFor(session).decrementAndGet();
}
void incrementLimitExceededCount() {
this.limitExceeded.incrementAndGet();
}
void incrementNoMessagesReceivedCount() {
this.noMessagesReceived.incrementAndGet();
}
void incrementTransportError() {
this.transportError.incrementAndGet();
}
AtomicInteger getCountFor(WebSocketSession session) {
if (session instanceof PollingSockJsSession) {
return this.httpPolling;
} else if (session instanceof StreamingSockJsSession) {
return this.httpStreaming;
} else {
return this.webSocket;
}
}
public String toString() {
return SubProtocolWebSocketHandler.this.sessions.size() + " current WS(" + this.webSocket.get() + ")-HttpStream(" + this.httpStreaming.get() + ")-HttpPoll(" + this.httpPolling.get() + "), " + this.total.get() + " total, " + (this.limitExceeded.get() + this.noMessagesReceived.get()) + " closed abnormally (" + this.noMessagesReceived.get() + " connect failure, " + this.limitExceeded.get() + " send limit, " + this.transportError.get() + " transport error)";
}
}
}
17
View Source File : StompBrokerRelayRegistration.java
License : MIT License
Project Creator : Vip-Augus
License : MIT License
Project Creator : Vip-Augus
protected StompBrokerRelayMessageHandler getMessageHandler(SubscribableChannel brokerChannel) {
StompBrokerRelayMessageHandler handler = new StompBrokerRelayMessageHandler(getClientInboundChannel(), getClientOutboundChannel(), brokerChannel, getDestinationPrefixes());
handler.setRelayHost(this.relayHost);
handler.setRelayPort(this.relayPort);
handler.setClientLogin(this.clientLogin);
handler.setClientPreplacedcode(this.clientPreplacedcode);
handler.setSystemLogin(this.systemLogin);
handler.setSystemPreplacedcode(this.systemPreplacedcode);
if (this.systemHeartbeatSendInterval != null) {
handler.setSystemHeartbeatSendInterval(this.systemHeartbeatSendInterval);
}
if (this.systemHeartbeatReceiveInterval != null) {
handler.setSystemHeartbeatReceiveInterval(this.systemHeartbeatReceiveInterval);
}
if (this.virtualHost != null) {
handler.setVirtualHost(this.virtualHost);
}
if (this.tcpClient != null) {
handler.setTcpClient(this.tcpClient);
}
handler.setAutoStartup(this.autoStartup);
return handler;
}
See More Examples