提供多个端点的 Cometd 可能不会部署到同一路径 [/cometd]

Posted

技术标签:

【中文标题】提供多个端点的 Cometd 可能不会部署到同一路径 [/cometd]【英文标题】:Cometd giving multiple endpoints may not be deployed to same path [/cometd] 【发布时间】:2015-04-13 09:57:09 【问题描述】:

我正在开发一个 Spring-MVC 应用程序,我正在尝试将聊天功能与 Cometd 集成。我收到无法在同一路径中部署多个端点的错误。

错误日志:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cometDInitializer.Processor': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private org.cometd.bayeux.server.BayeuxServer com.journaldev.spring.chat.CometDInitializer$Processor.bayeuxServer; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'bayeuxServer' defined in class path resource [com/journaldev/spring/chat/CometDInitializer.class]: Invocation of init method failed; nested exception is java.lang.RuntimeException: javax.websocket.DeploymentException: Multiple Endpoints may not be deployed to the same path [/cometd]
    org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:292)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1185)
    org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)

如果有人想要完整的错误日志,Here it is on pastebin

CometdInitializer.java:

@Component
public class CometDInitializer implements ServletContextAware

    private ServletContext servletContext;

    @Bean(initMethod = "start", destroyMethod = "stop")
    public BayeuxServer bayeuxServer()
    
        BayeuxServerImpl bean = new BayeuxServerImpl();
        bean.setTransports(new WebSocketTransport(bean), new JSONTransport(bean), new JSONPTransport(bean));
        servletContext.setAttribute(BayeuxServer.ATTRIBUTE, bean);
        bean.setOption(ServletContext.class.getName(), servletContext);
        bean.setOption("ws.cometdURLMapping", "/cometd/*");
        return bean;
    

    public void setServletContext(ServletContext servletContext)
    
        this.servletContext = servletContext;
    

    @Component
    public static class Processor implements DestructionAwareBeanPostProcessor
    
        @Inject
        private BayeuxServer bayeuxServer;
        private ServerAnnotationProcessor processor;

        @PostConstruct
        private void init()
        
            this.processor = new ServerAnnotationProcessor(bayeuxServer);
        

        @PreDestroy
        private void destroy()
        
        

        public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException
        
            processor.processDependencies(bean);
            processor.processConfigurations(bean);
            processor.processCallbacks(bean);
            return bean;
        

        public Object postProcessAfterInitialization(Object bean, String name) throws BeansException
        
            return bean;
        

        public void postProcessBeforeDestruction(Object bean, String name) throws BeansException
        
            processor.deprocessCallbacks(bean);
        
    

HelloService.java:

@Named
@Singleton
@Service("helloService")
public class HelloService

    @Inject
    private BayeuxServer bayeux;
    @Session
    private ServerSession serverSession;

    @PostConstruct
    public void init()
    
    

    @Listener("/service/hello")
    public void processHello(ServerSession remote, ServerMessage message)
    
        System.out.println("We recieved a helo msg");
        Map<String, Object> input = message.getDataAsMap();
        String name = (String)input.get("name");

        Map<String, Object> output = new HashMap<>();
        output.put("greeting", "Hello, " + name);
        remote.deliver(serverSession, "/hello", output);
    

Application.js:

(function($)

    var cometd = $.cometd;

    $(document).ready(function()
    
        function _connectionEstablished()
        
            $('#body').append('<div>CometD Connection Established</div>');
        

        function _connectionBroken()
        
            $('#body').append('<div>CometD Connection Broken</div>');
        

        function _connectionClosed()
        
            $('#body').append('<div>CometD Connection Closed</div>');
        

        // Function that manages the connection status with the Bayeux server
        var _connected = false;
        function _metaConnect(message)
        
            if (cometd.isDisconnected())
            
                _connected = false;
                _connectionClosed();
                return;
            

            var wasConnected = _connected;
            _connected = message.successful === true;
            if (!wasConnected && _connected)
            
                _connectionEstablished();
            
            else if (wasConnected && !_connected)
            
                _connectionBroken();
            
        

        // Function invoked when first contacting the server and
        // when the server has lost the state of this client
        function _metaHandshake(handshake)
        
            if (handshake.successful === true)
            
                cometd.batch(function()
                
                    cometd.subscribe('/hello', function(message)
                    
                        $('#body').append('<div>Server Says: ' + message.data.greeting + '</div>');
                    );
                    // Publish on a service channel since the message is for the server only
                    cometd.publish('/service/hello',  name: 'World' );
                );
            
        

        // Disconnect when the page unloads
        $(window).unload(function()
        
            cometd.disconnect(true);
        );

        var cometURL = location.protocol + "//" + location.host + config.contextPath + "/cometd";
        cometd.configure(
            url: cometURL,
            logLevel: 'debug'
        );

        cometd.addListener('/meta/handshake', _metaHandshake);
        cometd.addListener('/meta/connect', _metaConnect);

        cometd.handshake();
    );
)(jQuery);

web.xml:

<servlet-mapping>
        <servlet-name>cometd</servlet-name>
        <url-pattern>/cometd/*</url-pattern>
    </servlet-mapping>
    <servlet>
        <servlet-name>cometd</servlet-name>
        <servlet-class>org.cometd.server.CometDServlet</servlet-class>
        <init-param>
            <param-name>transports</param-name>
            <param-value>org.cometd.websocket.server.WebSocketTransport</param-value>
        </init-param>
        <init-param>
            <param-name>ws.cometdURLMapping</param-name>
            <param-value>/cometd/*</param-value>
        </init-param>

        <init-param>
            <param-name>logLevel</param-name>
            <param-value>3</param-value>
        </init-param>

        <load-on-startup>1</load-on-startup>
    </servlet>

编辑

最后,这是来自 cometd 的 mvn:archtype 代码的版本不匹配。以下文件有效:

@Component
public class BayeuxInitializer implements DestructionAwareBeanPostProcessor, ServletContextAware

    private BayeuxServer bayeuxServer;
    private ServerAnnotationProcessor processor;

    @Inject
    private void setBayeuxServer(BayeuxServer bayeuxServer)
    
        this.bayeuxServer = bayeuxServer;
    

    @PostConstruct
    private void init()
    
        this.processor = new ServerAnnotationProcessor(bayeuxServer);
    

    @PreDestroy
    private void destroy()
    
    

    public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException
    
        processor.processDependencies(bean);
        processor.processConfigurations(bean);
        processor.processCallbacks(bean);
        return bean;
    

    public Object postProcessAfterInitialization(Object bean, String name) throws BeansException
    
        return bean;
    

    public void postProcessBeforeDestruction(Object bean, String name) throws BeansException
    
        processor.deprocessCallbacks(bean);
    

    @Bean(initMethod = "start", destroyMethod = "stop")
    public BayeuxServer bayeuxServer()
    
        BayeuxServerImpl bean = new BayeuxServerImpl();
    //    bean.setTransports(new WebSocketTransport(bean), new JSONTransport(bean), new JSONPTransport(bean));
        return bean;
    

    public void setServletContext(ServletContext servletContext)
    
        servletContext.setAttribute(BayeuxServer.ATTRIBUTE, bayeuxServer);
    

【问题讨论】:

【参考方案1】:

异常是由你的设计引起的:

BayeuxServer的实例只能有一个,因为注册了

bean.setOption("ws.cometdURLMapping", "/cometd/*");

只能为一个实例调用。

有多个@Inject 注释。因此生成了多个实例。每个@Inject 一个实例

解决方案:添加@Singleton

@Bean(initMethod = "start", destroyMethod = "stop")
@Singleton
public BayeuxServer bayeuxServer()

现在只有一个 BayeuxServer 实例。所有@Inject 都会得到相同的实例。

这仅在实现支持 JSR330 时才有效。 (抱歉,春天不是!?)

单例的硬编码方式:

private static BayeuxServer beanInstance;
public static synchronized BayeuxServer bayeuxServer()

    if (beanInstance != null)
        return beanInstance;
    BayeuxServerImpl bean = new BayeuxServerImpl();
    bean.setTransports(new WebSocketTransport(bean), new JSONTransport(bean), new JSONPTransport(bean));
    servletContext.setAttribute(BayeuxServer.ATTRIBUTE, bean);
    bean.setOption(ServletContext.class.getName(), servletContext);
    bean.setOption("ws.cometdURLMapping", "/cometd/*");
    beanInstance = bean;
    return beanInstance;

【讨论】:

这没有帮助,错误是一样的,这是错误代码:pastebin.com/YkCvZTpC 我也尝试更改 bean.setOptions 的第二个参数,但没有任何效果,我得到了同样的错误。

以上是关于提供多个端点的 Cometd 可能不会部署到同一路径 [/cometd]的主要内容,如果未能解决你的问题,请参考以下文章

两台服务器部署相同的平台系统,这两台服务器之间可能得关系是啥?

握手后,cometd立即返回“402 :: Unknown Client”错误

我们可以使用云发布/订阅接收多个用户到同一推送端点的 gmail 推送通知吗

Spring MVC:实现 Cometd 通道功能

无服务器部署多个功能

Spring Data Rest 中同一实体的多个存储库