<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
</head>
<body>
<p>Hi List,</p>
<p>This is a bit of a long post. I'm mainly wondering if I did
something wrong that FX should detect early, or if I'm doing
nothing unusual and FX should handle the case described below
correctly.<br>
</p>
<p>I encountered the bug where an NPE occurs in
Scene$ScenePulseListener#synchronizeNodes, and it is reproducable
at the moment. I'm not sure it is the same one others sometimes
see, but the version I encountered is prefaced with a failing
assert (which may easily get lost as synchronizeNodes will spam
NPE's in your log, as it doesn't recover).</p>
<p>In Parent#validatePG it prints:</p>
<p> *** pgnodes.size validatePG() [1] != children.size() [2]</p>
<p>And then throws an AssertionError with a long stacktrace.<br>
</p>
<p> java.lang.AssertionError: validation of PGGroup children
failed<br>
(stack trace omitted)<br>
</p>
<p>Immediately after this, the NPE in synchronizeNodes starts to get
spammed, and the application never recovers.<br>
</p>
<p>This seems to have something to do with nested event loops, as I
introduced a new one in the code involved. When I remove the
nested event loop, there is no problem (it also initially works
fine when the application is just starting, only in a different
situation, when there is some user interaction via a keypress,
does the bug trigger).</p>
<p>The nested event loop is entered from a ChangeListener, which may
be a bit unusual. The documentation of
Platform#enterNestedEventLoop says:</p>
<div style="background-color:#ffffff;padding:0px 0px 0px 2px;">
<div
style="color:#000000;background-color:#ffffff;font-family:"Consolas";font-size:11pt;white-space:pre;"><p
style="margin:0;"><span style="color:#3f5fbf;"> * This method must either be called from an input event handler or</span></p><p
style="margin:0;"><span style="color:#3f5fbf;"> * from the run method of a Runnable passed to</span></p><p
style="margin:0;"><span style="color:#3f5fbf;"> * </span><span
style="color:#3f3fbf;">{@link javafx.application.Platform#runLater Platform.runLater}</span><span
style="color:#3f5fbf;">.</span></p><p style="margin:0;"><span
style="color:#3f5fbf;"> * It must not be called during animation or layout processing.</span></p></div>
</div>
<p></p>
<p>It is also documented to throw an IllegalStateException if used
incorrectly. That is however not happening, so I guess I'm using
it correctly...? On the other hand, I'm not in an input event
handler.... the whole process is triggered by a keypress though,
and deep down in the AssertionError trace you can see:</p>
<p> at
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)<br>
at
com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)<br>
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)<br>
at javafx.event.Event.fireEvent(Event.java:198)<br>
at javafx.scene.Scene$KeyHandler.process(Scene.java:4113)<br>
at javafx.scene.Scene.processKeyEvent(Scene.java:2159)<br>
at
javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2627)<br>
at
com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:218)<br>
<br>
So IMHO technically I am in an input event handler...?</p>
<p>Now the code does a LOT of stuff, which is going to make this
tough to analyze. In short:</p>
<p>- I'm displaying a Scene<br>
- The user indicates (with a keypress) they want to go to a
different Pane<br>
- The previous Pane is created, and added to the scene as a child
to a TransitionPane that will cross-fade between the current Pane
and the new Pane<br>
- As soon as the new Pane becomes part of the Scene, lots of
things get triggered because the Scene property goes from null to
the active Scene:<br>
<br>
at javafx.scene.Node.invalidatedScenes(Node.java:1075)<br>
at javafx.scene.Node.setScenes(Node.java:1142)
<-- the new Pane will get a Scene assigned to it<br>
at javafx.scene.Parent$2.onChanged(Parent.java:372)
<-- the new Pane has its Parent changed to the TransitionPane<br>
<br>
- Several parts of the new Pane listen (directly or indirectly) to
a change in Scene, as they only become "active" when the Node
involved is displayed (this happens with a Scene listener)<br>
- One of those things is some code that will create and populate a
ListView; this code uses a relatively fast query, but I had marked
it as something that should be done in the background as it
sometimes does take a bit too much time on the FX thread</p>
<p>Now, how I normally do things in the background is to display an
automatically closing "Please wait" dialog potentially with a
progress bar on it (this dialog is actually invisible, unless
sufficient time passes, so in 99% of the cases when things respond
fast, the user never sees it, even though it was created and is
there). This involves starting a nested event loop. This works
marvelously throughout this application (and has done so for
years), and it is used for almost every transition from one part
of the application to the next. In all cases so far however I did
this directly from an event handler.</p>
<p>So the main difference is that I'm trying to enter a nested event
loop from a ChangeListener (which deep down was triggered by an
Event). In the AssertionError stack trace (which I will include
at the end), there is no layout or animation code **before**
entering the nested loop, although there is some layout code
**after** it was entered.</p>
<p>I can live with the fact that I may be using enterNestedEventLoop
incorrectly here, but in that case it should probably also detect
this incorrect use and throw the IllegalStateException.</p>
<p>Technically, all this code is triggered while "adding" a Child to
the TransitionPane, so I suspect that is what the AssertionError
is about (it indicates the child count 1 != 2, which is what
TransitionPane has, one active pane, and just added to cross fade
to). Still, is this really incorrect usage?</p>
<p>I've included an annotated stack trace below.</p>
<p>As it is quite reproducable, I can debug this further by adding
breakpoints/prints -- I'm just unsure where to start looking.<br>
</p>
<p>--John</p>
<p>java.lang.AssertionError: validation of PGGroup children failed<br>
at javafx.scene.Parent.validatePG(Parent.java:243)<br>
at javafx.scene.Parent.doUpdatePeer(Parent.java:201)<br>
at javafx.scene.Parent$1.doUpdatePeer(Parent.java:109)<br>
at
com.sun.javafx.scene.ParentHelper.updatePeerImpl(ParentHelper.java:78)<br>
at
com.sun.javafx.scene.layout.RegionHelper.updatePeerImpl(RegionHelper.java:72)<br>
at
com.sun.javafx.scene.NodeHelper.updatePeer(NodeHelper.java:104)<br>
at javafx.scene.Node.syncPeer(Node.java:721)<br>
at
javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2396)<br>
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2542)<br>
at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:407)<br>
at
java.base/java.security.AccessController.doPrivileged(AccessController.java:400)<br>
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:406)<br>
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:436)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:352)<br>
at
com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)<br>
at
com.sun.glass.ui.win.WinApplication._enterNestedEventLoopImpl(Native
Method)<br>
at
com.sun.glass.ui.win.WinApplication._enterNestedEventLoop(WinApplication.java:211)<br>
at
com.sun.glass.ui.Application.enterNestedEventLoop(Application.java:515)<br>
at com.sun.glass.ui.EventLoop.enter(EventLoop.java:107)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.enterNestedEventLoop(QuantumToolkit.java:631)<br>
at
javafx.application.Platform.enterNestedEventLoop(Platform.java:301)<br>
at
hs.mediasystem.util.javafx.SceneUtil.enterNestedEventLoop(SceneUtil.java:75)
<-- just my wrapper to trace slow calls, it delegates to
Platform<br>
at
hs.mediasystem.runner.dialog.DialogPane.showDialog(DialogPane.java:78)<br>
at
hs.mediasystem.runner.dialog.Dialogs.showProgressDialog(Dialogs.java:163)<br>
at
hs.mediasystem.runner.dialog.Dialogs.runNested(Dialogs.java:103)<br>
at
hs.mediasystem.plugin.home.HomeScreenNodeFactory.lambda$1(HomeScreenNodeFactory.java:123) <br>
</p>
<p>The above line is where the nested event loop is entered. It
starts to display a "busy" dialog. A background task (on a new
thread) will create a ListView (this never actually happens, the
AssertionError is thrown immediately even with an empty task that
just sleeps 10 seconds).<br>
</p>
<p> at
com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:360)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:80)<br>
at
javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)<br>
at
javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:118)<br>
at
javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:172)<br>
at
javafx.scene.control.SelectionModel.setSelectedItem(SelectionModel.java:105)<br>
at
javafx.scene.control.MultipleSelectionModelBase.lambda$new$0(MultipleSelectionModelBase.java:67)<br>
at
com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:348)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.property.ReadOnlyIntegerPropertyBase.fireValueChangedEvent(ReadOnlyIntegerPropertyBase.java:78)<br>
at
javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:102)<br>
at
javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:114)<br>
at
javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:148)<br>
at
javafx.scene.control.SelectionModel.setSelectedIndex(SelectionModel.java:69)<br>
at
javafx.scene.control.MultipleSelectionModelBase.select(MultipleSelectionModelBase.java:424)<br>
at
javafx.scene.control.MultipleSelectionModelBase.select(MultipleSelectionModelBase.java:456)<br>
at
hs.mediasystem.plugin.home.HomeScreenNodeFactory.lambda$3(HomeScreenNodeFactory.java:176)
<-- triggers the creation of a ListView<br>
at
javafx.beans.value.ObservableValue.lambda$0(ObservableValue.java:364)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:192)<br>
at
com.sun.javafx.binding.ConditionalBinding.conditionChanged(ConditionalBinding.java:53)<br>
at
com.sun.javafx.binding.Subscription.lambda$2(Subscription.java:63)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:192)<br>
at
com.sun.javafx.binding.Subscription.lambda$4(Subscription.java:83)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:192)<br>
at
com.sun.javafx.binding.Subscription.lambda$4(Subscription.java:83)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:192)<br>
at
com.sun.javafx.binding.FlatMappedBinding.invalidateAll(FlatMappedBinding.java:102)<br>
at
com.sun.javafx.binding.Subscription.lambda$4(Subscription.java:83)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:192)<br>
at
com.sun.javafx.binding.FlatMappedBinding.invalidateAll(FlatMappedBinding.java:102)<br>
at
com.sun.javafx.binding.Subscription.lambda$4(Subscription.java:83)<br>
at
com.sun.javafx.binding.ExpressionHelper$Generic.fireValueChangedEvent(ExpressionHelper.java:348)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:80)<br>
at
javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)<br>
at
javafx.scene.Node$ReadOnlyObjectWrapperManualFire.fireSuperValueChangedEvent(Node.java:1053)
<-- there is a listener on a Scene property<br>
at javafx.scene.Node.invalidatedScenes(Node.java:1104)<br>
at javafx.scene.Node.setScenes(Node.java:1142)<br>
at javafx.scene.Parent.scenesChanged(Parent.java:772) <--
the new Pane has a few nested Panes, so you see multiple Parent
assignments<br>
at javafx.scene.Node.invalidatedScenes(Node.java:1075)<br>
at javafx.scene.Node.setScenes(Node.java:1142)<br>
at javafx.scene.Parent.scenesChanged(Parent.java:772)<br>
at javafx.scene.Node.invalidatedScenes(Node.java:1075)<br>
at javafx.scene.Node.setScenes(Node.java:1142) <--
the Parent is part of a Scene, so this new Pane also gets the same
Scene assigned<br>
at javafx.scene.Parent$2.onChanged(Parent.java:372) <--
the new Pane gets its parent assigned (TransitionPane)<br>
at
com.sun.javafx.collections.TrackableObservableList.lambda$new$0(TrackableObservableList.java:45)<br>
at
com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329)<br>
at
com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73)<br>
at
javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:239)<br>
at
javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)<br>
at
javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)<br>
at
javafx.collections.ObservableListBase.endChange(ObservableListBase.java:211)<br>
at
javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:162)<br>
at
com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:319)<br>
at
hs.mediasystem.util.javafx.ui.transition.TransitionPane.add(TransitionPane.java:94)
<-- new Pane is added to a TransitionPane that handles cross
fade between old and new Pane<br>
at
hs.mediasystem.util.javafx.ui.transition.TransitionPane.addAtStart(TransitionPane.java:105)<br>
at
hs.mediasystem.util.javafx.ui.transition.TransitionPane.add(TransitionPane.java:113)<br>
at
hs.mediasystem.runner.presentation.ViewPort.updateChildNode(ViewPort.java:68)
<-- here it installs the new Pane to display<br>
at
hs.mediasystem.runner.presentation.ViewPort.lambda$0(ViewPort.java:39)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.binding.ObjectBinding.invalidate(ObjectBinding.java:192)<br>
at
com.sun.javafx.binding.Subscription.lambda$4(Subscription.java:83)<br>
at
com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:136)<br>
at
com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80)<br>
at
javafx.beans.property.ObjectPropertyBase.fireValueChangedEvent(ObjectPropertyBase.java:111)<br>
at
javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:118)<br>
at
javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:172)<br>
at
hs.mediasystem.presentation.ParentPresentation.navigateBack(ParentPresentation.java:39)
<-- this handles the keyPress (it was a "back" press)<br>
at hs.mediasystem.util.expose.Expose.lambda$3(Expose.java:55)<br>
at hs.mediasystem.util.expose.Trigger$1.run(Trigger.java:58)<br>
at
hs.mediasystem.runner.RootPresentationHandler.tryRunAction(RootPresentationHandler.java:106)<br>
at
hs.mediasystem.runner.RootPresentationHandler.handleActionEvent(RootPresentationHandler.java:81)<br>
at
com.sun.javafx.event.CompositeEventHandler$NormalEventHandlerRecord.handleBubblingEvent(CompositeEventHandler.java:247)<br>
at
com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:80)<br>
at
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)<br>
at
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)<br>
at
com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)<br>
at
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)<br>
at
hs.mediasystem.util.javafx.SceneUtil.lambda$1(SceneUtil.java:101)
<-- this is just my Slow Event detection, and just delegates
the event<br>
at
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)<br>
at
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)<br>
at
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)<br>
at
com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)<br>
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:49)<br>
at
hs.mediasystem.util.javafx.base.Events.dispatchEvent(Events.java:35)<br>
at
hs.mediasystem.runner.action.InputActionHandler.handleKeyEvent(InputActionHandler.java:153)
<-- just code that handles a keypress that bubbled all the way
to the top<br>
at
hs.mediasystem.runner.action.InputActionHandler.onKeyPressed(InputActionHandler.java:138)<br>
at
com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(CompositeEventHandler.java:86)<br>
at
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:234)<br>
at
com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(EventHandlerManager.java:191)<br>
at
com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(CompositeEventDispatcher.java:59)<br>
at
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:58)<br>
at
hs.mediasystem.util.javafx.SceneUtil.lambda$1(SceneUtil.java:101)
<-- this is just my Slow Event detection, and just delegates
the event<br>
at
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)<br>
at
com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(BasicEventDispatcher.java:56)<br>
at
com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(EventDispatchChainImpl.java:114)<br>
at
com.sun.javafx.event.EventUtil.fireEventImpl(EventUtil.java:74)<br>
at com.sun.javafx.event.EventUtil.fireEvent(EventUtil.java:54)<br>
at javafx.event.Event.fireEvent(Event.java:198)<br>
at javafx.scene.Scene$KeyHandler.process(Scene.java:4113)<br>
at javafx.scene.Scene.processKeyEvent(Scene.java:2159)<br>
at
javafx.scene.Scene$ScenePeerListener.keyEvent(Scene.java:2627)<br>
at
com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:218)<br>
at
com.sun.javafx.tk.quantum.GlassViewEventHandler$KeyEventNotification.run(GlassViewEventHandler.java:150)<br>
at
java.base/java.security.AccessController.doPrivileged(AccessController.java:400)<br>
at
com.sun.javafx.tk.quantum.GlassViewEventHandler.lambda$handleKeyEvent$1(GlassViewEventHandler.java:250)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.runWithoutRenderLock(QuantumToolkit.java:424)<br>
at
com.sun.javafx.tk.quantum.GlassViewEventHandler.handleKeyEvent(GlassViewEventHandler.java:249)<br>
at com.sun.glass.ui.View.handleKeyEvent(View.java:542)<br>
at com.sun.glass.ui.View.notifyKey(View.java:966)<br>
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)<br>
at
com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)<br>
at java.base/java.lang.Thread.run(Thread.java:1583)<br>
</p>
<p>And the NPE exception:</p>
<p>java.lang.NullPointerException: Cannot invoke
"javafx.scene.Node.getScene()" because "<local2>" is null<br>
at
javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2395)<br>
at
javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2542)<br>
at
com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:407)<br>
at
java.base/java.security.AccessController.doPrivileged(AccessController.java:400)<br>
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:406)<br>
(rest of trace identical to the AssertionError one)</p>
<p>And another variant of the NPE exception:<br>
<br>
java.lang.NullPointerException: Cannot invoke
"javafx.scene.Node.getScene()" because "<local2>" is null<br>
at
javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2395)<br>
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2542)<br>
at com.sun.javafx.tk.Toolkit.lambda$runPulse$2(Toolkit.java:407)<br>
at
java.base/java.security.AccessController.doPrivileged(AccessController.java:400)<br>
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:406)<br>
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:436)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:575)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:555)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.pulseFromQueue(QuantumToolkit.java:548)<br>
at
com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$11(QuantumToolkit.java:352)<br>
at
com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96)<br>
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)<br>
at
com.sun.glass.ui.win.WinApplication.lambda$runLoop$3(WinApplication.java:184)<br>
at java.base/java.lang.Thread.run(Thread.java:1583)<br>
</p>
<p><br>
</p>
<p><br>
</p>
<p><br>
</p>
<p><br>
</p>
</body>
</html>