问题
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 @ManagedBean
s 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