5

I have absolutely simple SpringBoot project with simple configuration and simple integration test to test WebSockets.

pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>sandbox.websocket</groupId>
    <artifactId>websocket</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

SpringBootApplication:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Message broker configuration

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic");
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/greeting")
                .withSockJS();
    }
}

Integration Test to simply connect to server, subscribe to message broker and send message.

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
public class WebSocketIntegrationTest {

    @LocalServerPort
    private int localServerPort;

    private BlockingQueue<String> blockingQueue;

    @Before
    public void setup() {
        blockingQueue = new LinkedBlockingDeque<>();
    }

    @Test
    public void shouldReceiveAMessageFromTheServer() throws Exception {
        String uri = "ws://localhost:" + localServerPort + "/greeting";

        WebSocketStompClient stompClient = new WebSocketStompClient(
                new SockJsClient(Collections.singletonList(
                        new WebSocketTransport(
                                new StandardWebSocketClient()))));

        String message = "MESSAGE TEST";

        ListenableFuture<StompSession> connect = stompClient.connect(uri, new StompSessionHandlerAdapter() {});
        StompSession session = connect.get(1, SECONDS);

        session.subscribe("/topic", new DefaultStompFrameHandler());
        session.send("/topic", message.getBytes());

        Assert.assertEquals(message, blockingQueue.poll(10, SECONDS));
    }

    class DefaultStompFrameHandler implements StompFrameHandler {

        @Override
        public Type getPayloadType(StompHeaders stompHeaders) {
            return byte[].class;
        }

        @Override
        public void handleFrame(StompHeaders stompHeaders, Object o) {
            System.out.println("=============================================================");
            System.out.println(new String((byte[]) o));
            System.out.println("=============================================================");
            blockingQueue.offer(new String((byte[]) o));
        }
    }
}

If I run it and test it from javascript client it works like a charm. If I run the integration test it works only sometimes. The problem is that sometimes DefaultStompFrameHandler.handleFrame() method is not called, therefore nothing is saved to the queue and assert fails.

I wrote an InboundChannel interceptor to intercept frames and print commands to the console and all four commands are allways printed (CONNECT, SUBSCRIBE, SEND, DISCONNECT).

So, all commands go to the server including SEND, but sometimes (60-70%) StompFrameHandler is not being used no matter how long the queue.poll timeout is set.

Any help, please?

JiKra
  • 1,618
  • 3
  • 29
  • 45
  • did you find solution? I am facing same issue! – M.Ahsen Taqi Jul 13 '20 at 12:44
  • Setting the logging level to DEBUG revealed that the server received the subscribe event after the message was published. This is a race-condtion question. – Napoleon May 08 '21 at 14:05
  • Looking at the [DefaultStompSession](https://github.com/spring-projects/spring-framework/blob/main/spring-messaging/src/main/java/org/springframework/messaging/simp/stomp/DefaultStompSession.java#L429) implementation, we see that it does not handle acknowledgements, which makes it very hard to ensure that the subscription has happened. – Napoleon May 08 '21 at 14:08

0 Answers0