Urbanleg
Urbanleg

Reputation: 6532

Junit and spring websockets - after each test I get a weird exception

I am getting the following exception when my jUnit test is ending:

7 May 2014 18:06:29  INFO GenericApplicationContext - Closing  org.springframework.context.support.GenericApplicationContext@7b0133c2: startup date [Tue May 27      18:06:09 IDT 2014]; root of context hierarchy
27 May 2014 18:06:29  INFO DefaultLifecycleProcessor - Stopping beans in phase 2147483647
27 May 2014 18:06:29  INFO NettyTcpClient - CLOSED: [id: 0x09b0bb52, /127.0.0.1:56869 :>  /127.0.0.1:61613]
27 May 2014 18:06:31 ERROR StompBrokerRelayMessageHandler - Error while shutting down TCP client
java.util.concurrent.TimeoutException
at org.springframework.messaging.tcp.reactor.AbstractPromiseToListenableFutureAdapter.get(AbstractPromiseToListenableFutureAdapter.java:84)
at org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler.stopInternal(StompBrokerRelayMessageHandler.java:377)
at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.stop(AbstractBrokerMessageHandler.java:150)
at org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler.stop(AbstractBrokerMessageHandler.java:164)
at org.springframework.context.support.DefaultLifecycleProcessor.doStop(DefaultLifecycleProcessor.java:229)
at org.springframework.context.support.DefaultLifecycleProcessor.access$300(DefaultLifecycleProcessor.java:51)
at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.stop(DefaultLifecycleProcessor.java:363)
at org.springframework.context.support.DefaultLifecycleProcessor.stopBeans(DefaultLifecycleProcessor.java:202)
at org.springframework.context.support.DefaultLifecycleProcessor.onClose(DefaultLifecycleProcessor.java:118)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:888)
at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:809)
27 May 2014 18:06:31  INFO ThreadPoolTaskExecutor - Shutting down ExecutorService 'brokerChannelExecutor'
27 May 2014 18:06:31  INFO ThreadPoolTaskScheduler - Shutting down ExecutorService 'messageBrokerSockJsTaskScheduler'
27 May 2014 18:06:31  INFO ThreadPoolTaskExecutor - Shutting down ExecutorService 'clientOutboundChannelExecutor'
27 May 2014 18:06:31  INFO ThreadPoolTaskExecutor - Shutting down ExecutorService 'clientInboundChannelExecutor'
27 May 2014 18:06:31  INFO EhCacheManagerFactoryBean - Shutting down EhCache CacheManager
27 May 2014 18:06:31  INFO LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
27 May 2014 18:06:31  INFO DefaultContextLoadTimeWeaver - Removing all registered transformers for class loader: sun.misc.Launcher$AppClassLoader

Any ideas how to prevent that?

P.S - The tests are succeeding but I really hate to see that stack trace

EDIT - my test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "file:src/main/webapp/WEB-INF/spring/application-context.xml" })
public class EmptyTest{
  /**
  * Test (empty)
  */
  @Test()
  public void emptyTest() {

  assertTrue(true);

  }

}

And here is my config file for the broker:

@Configuration
@EnableWebSocketMessageBroker
@EnableScheduling
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

@Override
public void configureMessageBroker(MessageBrokerRegistry config) {

config.enableStompBrokerRelay(
                  "/topic",
                  "/queue/");

config.setApplicationDestinationPrefixes("/app");
}


@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {

registry.addEndpoint(
             "/wsdemo").withSockJS();
}

}

Upvotes: 0

Views: 2481

Answers (1)

andersschuller
andersschuller

Reputation: 13907

As I see it, you have a few options, depending on exactly what you want to achieve in your tests now, and what you might want to be able to do in the future.

1) If your test does not need the websocket configuration at all, change it to point at a custom context configuration that does not include the WebSocketConfig.

2) If your test needs the websocket config, but you don't need the broker relay (I can't see why it would be required in a test), you could add another Configuration for testing that uses registry.enableSimpleBroker("/topic", "/queue/") instead of enableStompBrokerRelay. Then there will be no TCP connection to the broker. The obvious disadvantage with this approach is that you are not testing your actual config, and that you are duplicating the destination prefixes.

3) Run an embedded STOMP broker for your tests. I'm not 100% certain such a thing exists - I know ActiveMQ has STOMP support and some support for running inside a VM, but I haven't tried this with STOMP. If possible, the advantage of this approach is that your tests would be testing something very close to the real code.

4) You could customise the STOMP broker relay to such an extent that you have full control over what your application receives from the broker. You can customise the StompBrokerRelayMessageHandler which manages the connection to the relay broker by adding a Configuration class that extends DelegatingWebSocketMessageBrokerConfiguration in order to override the stompBrokerRelayMessageHandler() method. For example, you can set the TCP client it uses to your own implementation of TcpOperations. Below is an example TCP client that simply does nothing, i.e. makes the Handler think it is connected, but cannot receive or send messages.

@Override
public AbstractBrokerMessageHandler stompBrokerRelayMessageHandler() {
    AbstractBrokerMessageHandler handler = super.stompBrokerRelayMessageHandler();

    if (handler instanceof StompBrokerRelayMessageHandler) {
        StompBrokerRelayMessageHandler stompHandler = (StompBrokerRelayMessageHandler) handler;
        stompHandler.setTcpClient(new TcpOperations<byte[]>() {
            @Override
            public ListenableFuture<Void> connect(TcpConnectionHandler<byte[]> connectionHandler) {
                return new CompletedListenableFuture<>(null);
            }

            @Override
            public ListenableFuture<Void> connect(TcpConnectionHandler<byte[]> connectionHandler, ReconnectStrategy reconnectStrategy) {
                return new CompletedListenableFuture<>(null);
            }

            @Override
            public ListenableFuture<Void> shutdown() {
                return new CompletedListenableFuture<>(null);
            }
        });
    }

    return handler;
}

Note that CompletedListenableFuture is just an implementation of ListenableFuture that is done after construction, and immediately calls any callbacks passed to addCallback with the value passed into the constructor.

The point here is that you can easily customise the exact behaviour of the broker relay components, so you can control them better in your tests. I am not aware of any built-in support to make this kind of testing easier, but then again the websocket support is still pretty new. I would suggest that you look at Rossen Stoyanchev's excellent example project spring-websocket-portfolio, if you haven't done so already, as it includes several examples of how to test the websocket configuration at different levels (just one controller, loading the full context, running an embedded server, ...). Hopefully this is also helpful for deciding how you want to test your application, and what you might need to customise to do it.

Upvotes: 1

Related Questions