Search code examples
spring-websocketspring-cloud-stream

dependency cycle on spring WebSocket interceptor and spring cloud stream


i am trying to create a websocket interceptor that do send a message using MessageChannel from spring cloud stream . i am facing a dependency cycle

┌─────┐
|  myChannelInterceptor defined in file [/Users/shahbour/IdeaProjects/proxy/target/classes/com/xxxxx/proxy/broker/MyChannelInterceptor.class]
↑     ↓
|  com.xxxx.proxy.service.XxxxxBinding (field private java.util.Map org.springframework.cloud.stream.binding.BindableProxyFactory.bindingTargetFactories)
↑     ↓
|  org.springframework.cloud.stream.config.BindingServiceConfiguration (field private java.util.List org.springframework.cloud.stream.config.BindingServiceConfiguration.customMessageConverters)
↑     ↓
|  org.springframework.web.socket.config.annotation.DelegatingWebSocketMessageBrokerConfiguration
↑     ↓
|  webSocketConfig defined in file [/Users/shahbour/IdeaProjects/proxy/target/classes/com/xxxx/proxy/config/WebSocketConfig.class]
└─────┘

My problem is that i need to inject a MessageChannel into the websocket interceptor

I am receiving the below error if i use @Autowire

org.springframework.context.ApplicationContextException: Failed to start bean 'subProtocolWebSocketHandler'; nested exception is java.lang.IllegalArgumentException: No handlers
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:178) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:167) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.DefaultLifecycleProcessor.access$200(DefaultLifecycleProcessor.java:50) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.DefaultLifecycleProcessor$LifecycleGroup.start(DefaultLifecycleProcessor.java:348) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.DefaultLifecycleProcessor.startBeans(DefaultLifecycleProcessor.java:151) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.DefaultLifecycleProcessor.onRefresh(DefaultLifecycleProcessor.java:114) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.finishRefresh(AbstractApplicationContext.java:879) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.finishRefresh(EmbeddedWebApplicationContext.java:144) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:545) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:314) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1162) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1151) [spring-boot-1.5.2.RELEASE.jar:1.5.2.RELEASE]
    at com.xxxx.proxy.xxxxxxProxyApplication.main(XxxxxProxyApplication.java:29) [classes/:na]
Caused by: java.lang.IllegalArgumentException: No handlers
    at org.springframework.util.Assert.isTrue(Assert.java:92) ~[spring-core-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.start(SubProtocolWebSocketHandler.java:244) ~[spring-websocket-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:175) ~[spring-context-4.3.7.RELEASE.jar:4.3.7.RELEASE]
    ... 15 common frames omitted

Solution

  • OK. Thank you! No I see the problem.

    Look:

    • Your MyChannelInterceptor depends on the auto-created channel by the BinderService.
    • That one tries to infer MessageConverters from the application context.
    • The AbstractMessageBrokerConfiguration provides one in face of CompositeMessageConverter brokerMessageConverter
    • That class is instantiated by the @EnableWebSocketMessageBroker
    • which, in turn, scans your WebSocketConfig because of the AbstractWebSocketMessageBrokerConfigurer
    • And this last one wants your MyChannelInterceptor to be injected.

    Not sure how to fix that as an out-of-the-box feature, but here is some kind of workaround:

    public class MyChannelInterceptor extends ChannelInterceptorAdapter {
    
        @Autowired
        private MessageChannel output;
    

    @Configuration
    public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
    
        @Bean
        public MyChannelInterceptor myChannelInterceptor() {
            return new MyChannelInterceptor();
        }
    ...
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.setInterceptors(myChannelInterceptor());
    }