-
Bug
-
Resolution: Fixed
-
Normal
-
9.5.0.2
-
Security Level: Jimmy
-
reproduced on Tomcat 7.0.105 and 9.0.38
the problem doesn't happen on Jetty (9.4.36)
Steps to Reproduce
run the attached example using Tomcat
place a breakpoint in org.zkoss.zkex.ui.comet.CometServerPush#activate (Line 514)
click the buttons in the sequence:
- start long-op
- stop long-op (reaches the breakpoint)
- simulate comet re-connect
- continue the code from the breakpoint
Current Result
18:10:31.632 [ForkJoinPool.commonPool-worker-3] ERROR org.zkoss.zk.ui.impl.UiEngineImpl - java.lang.NullPointerException: null at org.apache.catalina.connector.Request.notifyAttributeRemoved(Request.java:1623) at org.apache.catalina.connector.Request.removeAttribute(Request.java:1502) at org.apache.catalina.connector.RequestFacade.removeAttribute(RequestFacade.java:552) at org.zkoss.zk.ui.http.ExecutionImpl.removeAttribute(ExecutionImpl.java:503) at LongOpComposer_12188.handleCustomEvent(LongOpComposer_12188.java:66) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:566) at org.zkoss.zk.ui.select.Selectors$ComposerEventListener.onEvent(Selectors.java:673) at org.zkoss.zk.ui.AbstractComponent.onEvent(AbstractComponent.java:3185) at org.zkoss.zk.ui.AbstractComponent.service(AbstractComponent.java:3155) at org.zkoss.zk.ui.AbstractComponent.service(AbstractComponent.java:3097) at org.zkoss.zk.ui.impl.EventProcessor.process(EventProcessor.java:138) at org.zkoss.zk.ui.impl.UiEngineImpl.processEvent(UiEngineImpl.java:1884) at org.zkoss.zk.ui.impl.UiEngineImpl.process(UiEngineImpl.java:1656) at org.zkoss.zk.ui.impl.UiEngineImpl.endUpdate(UiEngineImpl.java:1224) at org.zkoss.zkex.ui.comet.CometServerPush.deactivate(CometServerPush.java:535) at org.zkoss.zk.ui.impl.DesktopImpl.deactivateServerPush(DesktopImpl.java:1682) at org.zkoss.zk.ui.Executions.deactivate(Executions.java:1011) at LongOpComposer_12188.lambda$startLongOp$0(LongOpComposer_12188.java:52) at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run$$$capture(CompletableFuture.java:1736) at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java) at java.base/java.util.concurrent.CompletableFuture$AsyncRun.exec(CompletableFuture.java:1728) at java.base/java.util.concurrent.ForkJoinTask.doExec$$$capture(ForkJoinTask.java:290) at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java) at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020) at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656) at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594) at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
Expected Result
no exception
Debug Information
2 threads are accessing the CometServerpush._active attribute resulting in old Request objects stored in the current execution, when activating the serverpush
The manual breakpoint makes the error happen 100% of the time (in a real situation that's a very rare race condition)
cleanAsyncInfo is called from the re-connecting thread during
org.zkoss.zkex.ui.comet.CometServerPush#processRequest (line 339)
This method is trying to check for this.isActive() (line 434) to avoid clearing an activated request. However due to the breakpoint this will not detect the currently activated execution, and eventually call AsyncContext#complete and creating a new AsyncContext (with a new Request object)
Once the breakpoint continues it will use an old Request object of the previous AsyncContext ... causing the exception.