Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8334593: Adding, removing and then adding a JFXPanel again leads to NullPointerException #1493

Open
wants to merge 4 commits into
base: master
Choose a base branch
from

Conversation

prsadhuk
Copy link
Collaborator

@prsadhuk prsadhuk commented Jun 28, 2024

Adding, then removing, and then adding a JFXPanel to the same component in the Swing scene graph leads to a NullPointerException in GlassScene because the sceneState is null.
Removing JFXPanel calls JFXPanel.removeNotify which calls Window.hide which calls SceneHelper.disposePeer -> Scene.disposePeer -> EmbeddedScene.dispose -> GlassScene.dispose which sets "sceneState" to null...
so when GlassScene.updateSceneState is called, it results in NPE.
Fix is to check if host (which is usually javafx.embed.swing.JFXPanel$HostContainer for active JFXPanel) has been reset/deleted which is done when EmbeddedScene.dispose is called during removeNotify and abstain from updating the scene state


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8334593: Adding, removing and then adding a JFXPanel again leads to NullPointerException (Bug - P3)

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1493/head:pull/1493
$ git checkout pull/1493

Update a local copy of the PR:
$ git checkout pull/1493
$ git pull https://git.openjdk.org/jfx.git pull/1493/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 1493

View PR using the GUI difftool:
$ git pr show -t 1493

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1493.diff

Webrev

Link to Webrev Comment

@bridgekeeper
Copy link

bridgekeeper bot commented Jun 28, 2024

👋 Welcome back psadhukhan! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jun 28, 2024

❗ This change is not yet ready to be integrated.
See the Progress checklist in the description for automated requirements.

@openjdk openjdk bot added the rfr Ready for review label Jun 28, 2024
@mlbridge
Copy link

mlbridge bot commented Jun 28, 2024

Webrevs

@kevinrushforth
Copy link
Member

Can you add an automated test to cover this fix?

Worth noting, this won't fully solve the problem that the reporter of this bug filed. The null check will prevent the NPE, but the scene will still not be visible. To allow removing and then re-adding the JFXPanel, we would need to not call Window.hide when the JFXPanel is removed. We should file a follow-on Enhancement to consider doing this, but that will need more discussion. The main point that would need to be solved is to figure out when to call Window.hide if not when the JFXPanel is removed.

@kevinrushforth
Copy link
Member

@prsadhuk One more question: Do you know what changed in JavaFX 21 to trigger the NPE?

@andy-goryachev-oracle
Copy link
Contributor

Since this problem straddles two different toolkits, I would suggest to specify a minimum of 2 reviewers.

@prsadhuk
Copy link
Collaborator Author

prsadhuk commented Jul 1, 2024

To allow removing and then re-adding the JFXPanel, we would need to not call Window.hide when the JFXPanel is removed.

I am not sure if this is a problem as just as JFXPanel.removeNotify does Window.hide but at the same time when we add JFXPanel to frame, it calls JFXPanel.addNotify which does Window.show so tit gets hidden when JFXPanel is removed and shown when JFXPanel is added

@prsadhuk
Copy link
Collaborator Author

prsadhuk commented Jul 1, 2024

@prsadhuk One more question: Do you know what changed in JavaFX 21 to trigger the NPE?

It is not reproducible in jfx21 as per my testing...The problematic code to call updateSceneState was added for JDK-8274932 so it has regressed from jfx22

@openjdk openjdk bot removed the rfr Ready for review label Jul 1, 2024
@openjdk openjdk bot added the rfr Ready for review label Jul 1, 2024
@prsadhuk
Copy link
Collaborator Author

prsadhuk commented Jul 1, 2024

Check is not without precedence in this file..There are instance of assert host != null; and host != null check like below

@prsadhuk
Copy link
Collaborator Author

prsadhuk commented Jul 1, 2024

Automated test added

@@ -157,7 +157,9 @@ public void setPixelScaleFactors(float scalex, float scaley) {
entireSceneNeedsRepaint();
Platform.runLater(() -> {
QuantumToolkit.runWithRenderLock(() -> {
updateSceneState();
if (host != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need a different solution here, as mentioned in line 58

// TODO: synchronize access to embedder from ET and RT

@andy-goryachev-oracle
Copy link
Contributor

Kevin is right, this fix does not solve the issue mentioned in the ticket. Once the fxPanel is added back, its content is not visible. Does not matter whether removing/adding happens at startup or the button event handler.

(attaching a slightly modified test case to the ticket, notice lines 30 and 44.

Also, I think swing requires validate() and repaint() called after modifying the component's children.

// fxPanel removed from frame
frame.remove(fxPanel);
// fxPanel added to frame again
frame.add(fxPanel); // <-- leads to NullPointerException
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use JUnit5 to write this test (class) and use assertDoesNotThrow(() -> frame.add(fxPanel));, which makes this a bit better to understand

@prsadhuk
Copy link
Collaborator Author

prsadhuk commented Jul 2, 2024

Kevin is right, this fix does not solve the issue mentioned in the ticket. Once the fxPanel is added back, its content is not visible. Does not matter whether removing/adding happens at startup or the button event handler.

(attaching a slightly modified test case to the ticket, notice lines 30 and 44.

Also, I think swing requires validate() and repaint() called after modifying the component's children.

OK..I guess I probably misunderstood the expectation...it says

EXPECTED -
The JFXPanel is visible to the user of the application and no Exceptions are thrown.
ACTUAL -
The JFXPanel is visible but the following Exception is thrown

which I deciphered as the JFXPanel window being visible and I guess in original testcase execution, the "TestButton" was still visible, only NPE was thrown..

ANyway, I guess it was mentioned that "We should file a follow-on Enhancement to consider doing this" and regarding synchro mentioned at line58, I guess we already have QuantumToolkit.runWithRenderLock in the problematic code

@andy-goryachev-oracle
Copy link
Contributor

QuantumToolkit.runWithRenderLock

locking a class field may not be sufficient. or rather, cannot be sufficient when multiple threads are accessing the field, as there are no guarantees as to the order of invocations.

Instead, we might need to pass the reference as a variable to the lambda which executes in a different thread. This way no matter what happens to the field, the code that executes in a different context will always have the right object.

And, it might be better not to share the field between the threads and toolkits. Always mutate a particular field in the context of one toolkit - like fieldAWT / fieldFX.

Just a suggestion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rfr Ready for review
4 participants