A reconnecting websocket can't connect to the server with FacesContext.getCurrentInstance() being resolved to null

陌路散爱 提交于 2021-01-04 11:08:20

问题


I have a plain Tomcat 8.5.47 having the following dependencies installed.

<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 seems to work fine, or at least, I didn't bump into any issues migrating @ManagedBeans to @Named. Having CDI installed means I can inject a PushContext and send some messages from the server to the client.

I declared a simple bean

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

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

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

}

and a channel in a JSF template

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

with the callbacks printing a console message.

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

When I load a page with the socket defined in it, the socket is being opened and a message in the console appears.

_onopen

Then I go to the network tab in the browser and see a HUGE number of requests being actively sent. Here's one of them.

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

I assume it's due to a reconnecting websocket that fails to re-establish the connection. I go to the server log and, indeed, see this exception repeated over and over again.

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)

I debug the stacktrace and get puzzled over this method.

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

where FacesContext.getCurrentInstance() returns null resolving the CDI manager to null which causes the exception. It's quite understandable since no JSF artifacts is being touched on the way meaning either CDI being configured incorrectly or I am missing something.

Another thing that caught my eye is the way WebsocketSessionManager is retrieved and the comment along that.

/**
 * 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;
}

Any nudge is really welcomed. Thanks.


Update:

I noticed com.sun.faces.config.FacesInitializer looks for

org.glassfish.tyrus.servlet.TyrusServletContainerInitializer

in this bit

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

and I was wondering if I needed to have this dependency.


Update 2:

I found these issues that seem related:

  • Websocket CdiUtils NPE using <f:websocket />
  • https://github.com/javaserverfaces/mojarra/issues/4306

来源:https://stackoverflow.com/questions/62407646/a-reconnecting-websocket-cant-connect-to-the-server-with-facescontext-getcurren

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!