首页 > 解决方案 > 重新连接的 websocket 无法连接到服务器,FacesContext.getCurrentInstance() 被解析为 null

问题描述

我有一个安装了以下依赖项的普通 Tomcat 8.5.47。

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.faces</artifactId>
    <version>2.3.9</version>
</dependency>
<dependency>
    <groupId>org.jboss.weld.servlet</groupId>
    <artifactId>weld-servlet</artifactId>
    <version>2.4.8.Final</version>
</dependency>
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>1.1</version>
</dependency>

CDI 似乎工作正常,或者至少,我在迁移@ManagedBeans 到@Named. 安装了 CDI 意味着我可以注入 aPushContext并将一些消息从服务器发送到客户端。

我声明了一个简单的bean

@ApplicationScoped
@Named("ButtonController")
public class ButtonController implements Serializable {

    @Inject
    @Setter
    @Push(channel = "someChannel")
    private PushContext _someChannel;

    public void onPress() {
        _someChannel.send("...");
    }

}

和 JSF 模板中的通道

<f:websocket channel="someChannel"
             onmessage="_onmessage"
             onopen="_onopen"
             onclose="_onclose"
/>

回调打印控制台消息。

<h:outputScript>
    function _onopen() {
        console.log('_onopen')
    }
</h:outputScript>

当我加载其中定义了套接字的页面时,正在打开套接字并在控制台中显示一条消息。

_onopen

然后我转到浏览器中的网络选项卡,看到大量请求正在主动发送。这是其中之一。

Request URL: ws://localhost:10000/bg/javax.faces.push/someChannel?55685979-de8b-4a27-b497-f726cbea02ca
Request Method: GET
Status Code: 101

Connection: upgrade
Date: Tue, 16 Jun 2020 10:46:22 GMT
Sec-WebSocket-Accept: L4F6dcI3YMvkvsRhG7IyGCWYxuI=
Sec-WebSocket-Extensions: permessage-deflate;client_max_window_bits=15
Upgrade: websocket

我认为这是由于重新连接的 websocket 无法重新建立连接。我转到服务器日志,确实看到这个异常一遍又一遍地重复。

16-Jun-2020 13:15:54.153 SEVERE [http-nio-10000-exec-2] org.apache.coyote.AbstractProtocol$ConnectionHandler.process Error reading request, ignored
    java.lang.NullPointerException
        at com.sun.faces.cdi.CdiUtils.getBeanReferenceByType(CdiUtils.java:230)
        at com.sun.faces.cdi.CdiUtils.getBeanReference(CdiUtils.java:213)
        at com.sun.faces.push.WebsocketSessionManager.getInstance(WebsocketSessionManager.java:240)
        at com.sun.faces.push.WebsocketEndpoint.onOpen(WebsocketEndpoint.java:88)
        at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.init(WsHttpUpgradeHandler.java:133)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:856)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1500)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.lang.Thread.run(Thread.java:748)

我调试堆栈跟踪并对这种方法感到困惑。

public static <T> T getBeanReference(Class<T> type, Annotation... qualifiers) {
    return type.cast(getBeanReferenceByType(Util.getCdiBeanManager(FacesContext.getCurrentInstance()), type, qualifiers));
}

whereFacesContext.getCurrentInstance()返回null解析null导致异常的 CDI 管理器。这是完全可以理解的,因为在这意味着 CDI 配置不正确或我遗漏了一些东西的过程中没有触及 JSF 工件。

引起我注意的另一件事WebsocketSessionManager是检索方式以及沿途的评论。

/**
 * Internal usage only. Awkward workaround for it being unavailable via @Inject in endpoint in Tomcat+Weld/OWB.
 */
static WebsocketSessionManager getInstance() {
    if (instance == null) {
        instance = getBeanReference(WebsocketSessionManager.class);
    }

    return instance;
}

任何轻推都非常受欢迎。谢谢。


更新:

我注意到com.sun.faces.config.FacesInitializer寻找

org.glassfish.tyrus.servlet.TyrusServletContainerInitializer

在这一点

    Class<?> tyrusInitializerClass;
    try {
        tyrusInitializerClass = cl.loadClass("org.glassfish.tyrus.servlet.TyrusServletContainerInitializer");
    } catch (ClassNotFoundException cnfe) {
        // No possibility of WebSocket.
        return;
    }

我想知道我是否需要这种依赖。


更新 2:

我发现这些似乎相关的问题:

标签: tomcatjsfwebsocketweldjsf-2.3

解决方案


推荐阅读