org.springframework.messaging.SubscribableChannel

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 7

19 View Source File : WebMvcStompEndpointRegistryTests.java
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

/**
 * 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

@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

@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

@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

@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

@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

@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

/**
 * {@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

@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

@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

// 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

@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

/**
 * 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

@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

@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

@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

@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

@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

@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

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

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

@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

/**
 * 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

@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

@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

@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

@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

@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

@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

/**
 * 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

/**
 * {@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

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

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

// 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

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

@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

/**
 * 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

/**
 * 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

/**
 * 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

@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

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

@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

@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

/**
 * 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

/**
 * 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

/**
 * 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

/**
 * 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

/**
 * 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

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