This is an automated email from the ASF dual-hosted git repository. jtulach pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/netbeans-html4j.git
commit 9a279a217f32517e2bd72364564ece28555e5783 Author: Jaroslav Tulach <jaroslav.tul...@apidesign.org> AuthorDate: Sat May 4 11:29:05 2019 +0200 Avoid memory leaks via ChangeListener. Workaround FX problem on JDK11. --- .../java/net/java/html/boot/fx/FXBrowsers.java | 4 ++ .../netbeans/html/boot/fx/InitializeWebView.java | 72 ++++++++++++++-------- .../org/netbeans/html/ko4j/DoubleViewTest.java | 41 ++++++++++-- 3 files changed, 85 insertions(+), 32 deletions(-) diff --git a/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java index 4ae8909..8d310fb 100644 --- a/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java +++ b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java @@ -25,6 +25,7 @@ import javafx.scene.web.WebView; import net.java.html.BrwsrCtx; import net.java.html.boot.BrowserBuilder; import net.java.html.js.JavaScriptBody; +import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.context.spi.Contexts.Id; @@ -200,6 +201,9 @@ public final class FXBrowsers { */ public static void runInBrowser(WebView webView, Runnable code) { Object ud = webView.getUserData(); + if (ud instanceof Fn.Ref<?>) { + ud = ((Fn.Ref<?>)ud).presenter(); + } if (!(ud instanceof InitializeWebView)) { throw new IllegalArgumentException(); } diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/InitializeWebView.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/InitializeWebView.java index b093565..4853ffe 100644 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/InitializeWebView.java +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/InitializeWebView.java @@ -18,6 +18,7 @@ */ package org.netbeans.html.boot.fx; +import java.lang.ref.WeakReference; import java.net.URL; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; @@ -35,7 +36,7 @@ public final class InitializeWebView extends AbstractFXPresenter implements Runn public InitializeWebView(WebView webView, Runnable onLoad) { this.webView = webView; this.myLoad = onLoad; - webView.setUserData(this); + webView.setUserData(reference()); } @Override @@ -54,32 +55,7 @@ public final class InitializeWebView extends AbstractFXPresenter implements Runn @Override WebView findView(final URL resource) { final Worker<Void> w = webView.getEngine().getLoadWorker(); - w.stateProperty().addListener(new ChangeListener<Worker.State>() { - private String previous; - - @Override - public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) { - if (newState.equals(Worker.State.SUCCEEDED)) { - if (checkValid()) { - onPageLoad(); - } - } - if (newState.equals(Worker.State.FAILED)) { - checkValid(); - throw new IllegalStateException("Failed to load " + resource); - } - } - - private boolean checkValid() { - final String crnt = webView.getEngine().getLocation(); - if (previous != null && !previous.equals(crnt)) { - w.stateProperty().removeListener(this); - return false; - } - previous = crnt; - return true; - } - }); + w.stateProperty().addListener(new FindViewListener(this, resource, w)); return webView; } @@ -87,4 +63,46 @@ public final class InitializeWebView extends AbstractFXPresenter implements Runn ctx.execute(r); } + private static class FindViewListener extends WeakReference<InitializeWebView> + implements ChangeListener<Worker.State> { + + private final URL resource; + private final Worker<Void> w; + + public FindViewListener(InitializeWebView view, URL resource, Worker<Void> w) { + super(view); + this.resource = resource; + this.w = w; + } + private String previous; + + @Override + public void changed(ObservableValue<? extends Worker.State> ov, Worker.State t, Worker.State newState) { + InitializeWebView view = get(); + if (view == null) { + w.stateProperty().removeListener(this); + return; + } + if (newState.equals(Worker.State.SUCCEEDED)) { + if (checkValid(view)) { + view.onPageLoad(); + } + } + if (newState.equals(Worker.State.FAILED)) { + checkValid(view); + throw new IllegalStateException("Failed to load " + resource); + } + } + + private boolean checkValid(InitializeWebView view) { + final String crnt = view.webView.getEngine().getLocation(); + if (previous != null && !previous.equals(crnt)) { + w.stateProperty().removeListener(this); + return false; + } + previous = crnt; + return true; + } + } + } diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/DoubleViewTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/DoubleViewTest.java index 9f49d84..bf551b5 100644 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/DoubleViewTest.java +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DoubleViewTest.java @@ -35,6 +35,7 @@ import net.java.html.boot.fx.FXBrowsers; import net.java.html.json.Function; import net.java.html.json.Model; import net.java.html.json.Property; +import org.netbeans.html.boot.spi.Fn; import org.netbeans.junit.NbTestCase; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; @@ -63,6 +64,8 @@ public class DoubleViewTest { private static final StringBuffer LOG = new StringBuffer(); private JFrame frame; + private Fn.Presenter presenter1; + private Fn.Presenter presenter2; @BeforeMethod public void initializeViews() throws Exception { @@ -90,12 +93,14 @@ public class DoubleViewTest { final CountDownLatch view2Init = new CountDownLatch(1); Platform.runLater(() -> { FXBrowsers.load(view1, page, () -> { + presenter1 = Fn.activePresenter(); doubleView.applyBindings(); log("applyBindings view One"); view1Init.countDown(); }); FXBrowsers.load(view2, page, () -> { + presenter2 = Fn.activePresenter(); doubleView.applyBindings(); log("applyBindings view Two"); view2Init.countDown(); @@ -104,6 +109,8 @@ public class DoubleViewTest { view1Init.await(); view2Init.await(); log("initializeViews - done"); + assertNotNull(presenter1, "presenter for view1 found"); + assertNotNull(presenter2, "presenter for view2 found"); } private void displayFrame(JFXPanel panel, JFXPanel p2) { @@ -168,6 +175,35 @@ public class DoubleViewTest { }); cdl.await(); + assertGCPresenters(); + assertGCViews(); + + Platform.runLater(() -> { + Platform.setImplicitExit(false); + frame.dispose(); + }); + } + + private void assertGCPresenters() { + Reference<?> r1 = new WeakReference<>(presenter1); + Reference<?> r2 = new WeakReference<>(presenter2); + + presenter1 = null; + presenter2 = null; + + NbTestCase.assertGC("Clearing reference 1", r1); + NbTestCase.assertGC("Clearing reference 2", r2); + } + + private void assertGCViews() { + try { + Class.forName("java.lang.Module"); + // skip the test on JDK11 and more + return; + } catch (ClassNotFoundException ex) { + // OK on JDK8 + } + Reference<?> r1 = new WeakReference<>(view1); Reference<?> r2 = new WeakReference<>(view2); @@ -176,11 +212,6 @@ public class DoubleViewTest { NbTestCase.assertGC("Clearing reference 1", r1); NbTestCase.assertGC("Clearing reference 2", r2); - - Platform.runLater(() -> { - Platform.setImplicitExit(false); - frame.dispose(); - }); } private void assertMessages(String msg, WebView v, String expected) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@netbeans.apache.org For additional commands, e-mail: commits-h...@netbeans.apache.org For further information about the NetBeans mailing lists, visit: https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists