You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@netbeans.apache.org by jt...@apache.org on 2019/05/04 11:05:07 UTC

[netbeans-html4j] branch master updated (b618068 -> a74337e)

This is an automated email from the ASF dual-hosted git repository.

jtulach pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans-html4j.git.


    from b618068  Use triggerEvent to notify about an index change
     new a458fd4  Assert that unused FX presenters can be GCed
     new eb8179d  Avoid null presenter as soon as possible
     new dd0f930  No presenter means empty MapObjs.toArray
     new 998a508  Allow Presenters to define their own way to reference itself
     new 9f5b51d  Documenting use of Fn.ref
     new 9a279a2  Avoid memory leaks via ChangeListener. Workaround FX problem on JDK11.
     new a74337e  Point to PR-19 that introduces Fn.Ref

The 7 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 boot-fx/pom.xml                                    |   7 +
 .../java/net/java/html/boot/fx/FXBrowsers.java     |   4 +
 .../netbeans/html/boot/fx/AbstractFXPresenter.java |  32 +++-
 .../netbeans/html/boot/fx/InitializeWebView.java   |  72 +++++---
 .../html/boot/fx/AbstractFXPresenterTest.java      |  79 +++++++++
 boot/pom.xml                                       |   7 +
 .../java/org/netbeans/html/boot/impl/FnUtils.java  |   3 +
 .../html/boot/impl/JavaScriptProcesor.java         |  18 +-
 .../netbeans/html/boot/spi/FallbackIdentity.java   |  49 +++---
 .../main/java/org/netbeans/html/boot/spi/Fn.java   | 189 +++++++++++++++------
 .../java/org/netbeans/html/boot/impl/FnTest.java   |   5 +
 .../netbeans/html/boot/impl/JsClassLoaderBase.java |   4 +-
 .../html/boot/spi/FallbackIdentityTest.java        |  98 +++++++++++
 ko4j/pom.xml                                       |   6 +
 .../java/org/netbeans/html/ko4j/CacheObjs.java     |   8 +-
 .../main/java/org/netbeans/html/ko4j/Knockout.java |  16 +-
 .../main/java/org/netbeans/html/ko4j/MapObjs.java  |  63 ++++---
 .../org/netbeans/html/ko4j/DoubleViewTest.java     |  72 +++++++-
 .../java/org/netbeans/html/ko4j/MapObjsTest.java   |   7 +
 src/main/javadoc/overview.html                     |  14 +-
 20 files changed, 608 insertions(+), 145 deletions(-)
 create mode 100644 boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java
 copy json/src/main/java/org/netbeans/html/json/impl/LinkedListTypes.java => boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java (53%)
 create mode 100644 boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 01/07: Assert that unused FX presenters can be GCed

Posted by jt...@apache.org.
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 a458fd4bb91836518d9a6a831eb34ff2ea9b888d
Author: Jaroslav Tulach <ja...@apidesign.org>
AuthorDate: Sat May 4 07:30:13 2019 +0200

    Assert that unused FX presenters can be GCed
---
 .../html/boot/impl/JavaScriptProcesor.java         |   7 +-
 .../main/java/org/netbeans/html/boot/spi/Fn.java   | 127 ++++++++++++---------
 ko4j/pom.xml                                       |   6 +
 .../java/org/netbeans/html/ko4j/CacheObjs.java     |   8 +-
 .../main/java/org/netbeans/html/ko4j/Knockout.java |  15 ++-
 .../main/java/org/netbeans/html/ko4j/MapObjs.java  |  50 +++++---
 .../org/netbeans/html/ko4j/DoubleViewTest.java     |  41 ++++++-
 7 files changed, 174 insertions(+), 80 deletions(-)

diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
index 7e13130..3f259fa 100644
--- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
@@ -400,16 +400,16 @@ public final class JavaScriptProcesor extends AbstractProcessor {
             source.append("package ").append(pkgName).append(";\n");
             source.append("public final class $JsCallbacks$ {\n");
             source.append("  static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
-            source.append("  private final org.netbeans.html.boot.spi.Fn.Presenter p;\n");
+            source.append("  private final java.lang.ref.Reference<org.netbeans.html.boot.spi.Fn.Presenter> ref;\n");
             source.append("  private $JsCallbacks$ next;\n");
             source.append("  private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n");
-            source.append("    this.p = p;\n");
+            source.append("    this.ref = new java.lang.ref.WeakReference<org.netbeans.html.boot.spi.Fn.Presenter>(p);\n");
             source.append("  }\n");
             source.append("  synchronized final $JsCallbacks$ current() {\n");
             source.append("    org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n");
             source.append("    $JsCallbacks$ thiz = this;\n");
             source.append("    for (;;) {\n");
-            source.append("      if (now == thiz.p) return thiz;\n");
+            source.append("      if (now == thiz.ref.get()) return thiz;\n");
             source.append("      if (thiz.next == null) {\n");
             source.append("        return thiz.next = new $JsCallbacks$(now);\n");
             source.append("      }\n");
@@ -486,6 +486,7 @@ public final class JavaScriptProcesor extends AbstractProcessor {
             sep = ", ";
         }
         source.append(") throws Throwable {\n");
+        source.append("    org.netbeans.html.boot.spi.Fn.Presenter p = ref.get(); \n");
         source.append(convert);
         if (useTryResources()) {
             source.append("    try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n");
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
index e70ad91..3dc4e30 100644
--- a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
@@ -23,6 +23,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -38,8 +40,7 @@ import org.netbeans.html.boot.impl.FnContext;
  * @author Jaroslav Tulach
  */
 public abstract class Fn {
-    private static Map<String, Set<Presenter>> LOADED;
-    private final Presenter presenter;
+    private final Reference<Presenter> presenter;
     
     /**
      * @deprecated Ineffective as of 0.6. 
@@ -57,7 +58,7 @@ public abstract class Fn {
      * @since 0.6 
      */
     protected Fn(Presenter presenter) {
-        this.presenter = presenter;
+        this.presenter = new WeakReference<Presenter>(presenter);
     }
 
     /** True, if currently active presenter is the same as presenter this
@@ -66,7 +67,7 @@ public abstract class Fn {
      * @return true, if proper presenter is used
      */
     public final boolean isValid() {
-        return presenter != null && FnContext.currentPresenter(false) == presenter;
+        return FnContext.currentPresenter(false) == presenter.get();
     }
     
     /** Helper method to check if the provided instance is valid function.
@@ -144,52 +145,7 @@ public abstract class Fn {
         if (fn == null) {
             return null;
         }
-        return new Fn(fn.presenter()) {
-            @Override
-            public Object invoke(Object thiz, Object... args) throws Exception {
-                loadResource();
-                return fn.invoke(thiz, args);
-            }
-
-            @Override
-            public void invokeLater(Object thiz, Object... args) throws Exception {
-                loadResource();
-                fn.invokeLater(thiz, args);
-            }
-            
-            private void loadResource() throws Exception {
-                Presenter p = presenter();
-                if (p == null) {
-                    p = FnContext.currentPresenter(false);
-                }
-                if (p != null) {
-                    if (LOADED == null) {
-                        LOADED = new HashMap<String, Set<Presenter>>();
-                    }
-                    Set<Presenter> there = LOADED.get(resource);
-                    if (there == null) {
-                        there = new HashSet<Presenter>();
-                        LOADED.put(resource, there);
-                    }
-                    if (there.add(p)) {
-                        final ClassLoader l = caller.getClassLoader();
-                        InputStream is = l.getResourceAsStream(resource);
-                        if (is == null && resource.startsWith("/")) {
-                            is = l.getResourceAsStream(resource.substring(1));
-                        }
-                        if (is == null) {
-                            throw new IOException("Cannot find " + resource + " in " + l);
-                        }
-                        try {
-                            InputStreamReader r = new InputStreamReader(is, "UTF-8");
-                            p.loadScript(r);
-                        } finally {
-                            is.close();
-                        }
-                    }
-                }
-            }
-        };
+        return new Preload(fn.presenter(), fn, resource, caller);
     }
 
     
@@ -251,7 +207,7 @@ public abstract class Fn {
      * @since 0.7
      */
     protected final Presenter presenter() {
-        return presenter;
+        return presenter.get();
     }
     
     /** The representation of a <em>presenter</em> - usually a browser window.
@@ -363,4 +319,73 @@ public abstract class Fn {
          */
         public Fn defineFn(String code, String[] names, boolean[] keepAlive);
     }
+
+    private static class Preload extends Fn {
+        private static Map<String, Set<Reference<Presenter>>> LOADED;
+        private final Fn fn;
+        private final String resource;
+        private final Class<?> caller;
+
+        Preload(Presenter presenter, Fn fn, String resource, Class<?> caller) {
+            super(presenter);
+            this.fn = fn;
+            this.resource = resource;
+            this.caller = caller;
+        }
+
+        @Override
+        public Object invoke(Object thiz, Object... args) throws Exception {
+            loadResource();
+            return fn.invoke(thiz, args);
+        }
+
+        @Override
+        public void invokeLater(Object thiz, Object... args) throws Exception {
+            loadResource();
+            fn.invokeLater(thiz, args);
+        }
+
+        private void loadResource() throws Exception {
+            Reference<Presenter> ref = super.presenter;
+            if (ref == null) {
+                ref = new WeakReference<Fn.Presenter>(FnContext.currentPresenter(false));
+            }
+            Fn.Presenter realPresenter = ref == null ? null : ref.get();
+            if (realPresenter != null) {
+                if (LOADED == null) {
+                    LOADED = new HashMap<String, Set<Reference<Presenter>>>();
+                }
+                Set<Reference<Presenter>> there = LOADED.get(resource);
+                if (there == null) {
+                    there = new HashSet<Reference<Fn.Presenter>>();
+                    LOADED.put(resource, there);
+                }
+                if (addNewRef(there, ref)) {
+                    final ClassLoader l = caller.getClassLoader();
+                    InputStream is = l.getResourceAsStream(resource);
+                    if (is == null && resource.startsWith("/")) {
+                        is = l.getResourceAsStream(resource.substring(1));
+                    }
+                    if (is == null) {
+                        throw new IOException("Cannot find " + resource + " in " + l);
+                    }
+                    try {
+                        InputStreamReader r = new InputStreamReader(is, "UTF-8");
+                        realPresenter.loadScript(r);
+                    } finally {
+                        is.close();
+                    }
+                }
+            }
+        }
+
+        private static synchronized boolean addNewRef(Set<Reference<Presenter>> set, Reference<Presenter> ref) {
+            for (Reference<Presenter> r : set) {
+                if (r.get() == ref.get()) {
+                    return false;
+                }
+            }
+            return set.add(ref);
+        }
+    }
 }
diff --git a/ko4j/pom.xml b/ko4j/pom.xml
index fbe56bb..1d8b9d1 100644
--- a/ko4j/pom.xml
+++ b/ko4j/pom.xml
@@ -130,6 +130,12 @@
         <artifactId>javax.servlet-api</artifactId>
         <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-netbeans-modules-nbjunit</artifactId>
+      <version>${netbeans.version}</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
     <description>Binds net.java.html.json APIs together with knockout.js</description>
 </project>
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
index 4da77c2..74fe85d 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
@@ -19,24 +19,26 @@
 
 package org.netbeans.html.ko4j;
 
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import org.netbeans.html.boot.spi.Fn;
 
 final class CacheObjs {
     /* both @GuardedBy CacheObjs.class */
     private static CacheObjs[] list = new CacheObjs[16];
     private static int listAt = 0;
-    private final Fn.Presenter ref;
+    private final Reference<Fn.Presenter> ref;
 
     /* both @GuardedBy presenter single threaded access */
     private Object[] jsObjects;
     private int jsIndex;
 
     private CacheObjs(Fn.Presenter p) {
-        this.ref = p;
+        this.ref = new WeakReference<Fn.Presenter>(p);
     }
 
     Fn.Presenter get() {
-        return ref;
+        return ref.get();
     }
 
     static synchronized CacheObjs find(Fn.Presenter key) {
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
index 912d12d..e95ff87 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
@@ -20,6 +20,7 @@ package org.netbeans.html.ko4j;
 
 import java.io.Closeable;
 import java.io.IOException;
+import java.lang.ref.Reference;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.Executor;
@@ -150,10 +151,22 @@ final class Knockout  {
         funcs[index].call(data, ev);
     }
 
+    private static Fn.Presenter getPresenter(Object obj) {
+        if (obj instanceof Fn.Presenter) {
+            return (Fn.Presenter) obj;
+        } else {
+            if (obj == null) {
+                return null;
+            } else {
+                return (Fn.Presenter) ((Reference<?>)obj).get();
+            }
+        }
+    }
+
     final void valueHasMutated(final String propertyName, Object oldValue, Object newValue) {
         Object[] all = MapObjs.toArray(objs);
         for (int i = 0; i < all.length; i += 2) {
-            Fn.Presenter p = (Fn.Presenter) all[i];
+            Fn.Presenter p = getPresenter(all[i]);
             final Object o = all[i + 1];
             if (p != Fn.activePresenter()) {
                 if (p instanceof Executor) {
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
index 1f3a998..bf9d998 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
@@ -19,12 +19,14 @@
 
 package org.netbeans.html.ko4j;
 
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import java.util.List;
 import net.java.html.json.Models;
 import org.netbeans.html.boot.spi.Fn;
 
 final class MapObjs {
-    private static Object onlyPresenter;
+    private static Reference<Fn.Presenter> onlyPresenter;
     private static boolean usePresenter;
 
     static {
@@ -32,7 +34,7 @@ final class MapObjs {
     }
 
     static void reset() {
-        onlyPresenter = null;
+        setOnlyPresenter(null);
         usePresenter = true;
     }
 
@@ -43,33 +45,33 @@ final class MapObjs {
     }
 
 
-    static Object put(Object now, Fn.Presenter key, Object js) {
+    synchronized static Object put(Object now, Fn.Presenter key, Object js) {
         if (now instanceof MapObjs) {
             return ((MapObjs)now).put(key, js);
         } else {
             if (usePresenter) {
-                if (onlyPresenter == null) {
-                    onlyPresenter = key;
+                if (getOnlyPresenter() == null) {
+                    setOnlyPresenter(key);
                     return js;
-                } else if (onlyPresenter == key) {
+                } else if (getOnlyPresenter() == key) {
                     return js;
                 } else {
                     usePresenter = false;
                 }
             }
             if (now == null) {
-                return new MapObjs(key, js);
+                return new MapObjs(new WeakReference<Fn.Presenter>(key), js);
             } else {
-                return new MapObjs(onlyPresenter, now, key, js);
+                return new MapObjs(onlyPresenter, now, new WeakReference<Fn.Presenter>(key), js);
             }
         }
     }
 
-    static Object get(Object now, Fn.Presenter key) {
+    synchronized static Object get(Object now, Fn.Presenter key) {
         if (now instanceof MapObjs) {
             return ((MapObjs)now).get(key);
         }
-        return key == onlyPresenter ? now : null;
+        return key == getOnlyPresenter() ? now : null;
     }
 
     static Object[] remove(Object now, Fn.Presenter key) {
@@ -79,28 +81,36 @@ final class MapObjs {
         return new Object[] { now, null };
     }
 
-    static Object[] toArray(Object now) {
+    synchronized static Object[] toArray(Object now) {
         if (now instanceof MapObjs) {
             return ((MapObjs) now).all.toArray();
         }
-        return new Object[] { onlyPresenter, now };
+        return new Object[] { getOnlyPresenter(), now };
     }
 
     private Object put(Fn.Presenter key, Object js) {
         for (int i = 0; i < all.size(); i += 2) {
-            if (all.get(i) == key) {
+            if (isSameKey(i, key)) {
                 all.set(i + 1, js);
                 return this;
             }
         }
-        all.add(key);
+        all.add(new WeakReference<Fn.Presenter>(key));
         all.add(js);
         return this;
     }
 
+    boolean isSameKey(int index, Fn.Presenter key) {
+        Object at = all.get(index);
+        if (at instanceof Reference<?>) {
+            at = ((Reference<?>)at).get();
+        }
+        return at == key;
+    }
+
     private Object get(Fn.Presenter key) {
         for (int i = 0; i < all.size(); i += 2) {
-            if (all.get(i) == key) {
+            if (isSameKey(i, key)) {
                 return all.get(i + 1);
             }
         }
@@ -109,10 +119,18 @@ final class MapObjs {
 
     private Object[] remove(Fn.Presenter key) {
         for (int i = 0; i < all.size(); i += 2) {
-            if (all.get(i) == key) {
+            if (isSameKey(i, key)) {
                 return new Object[] { all.get(i + 1), this };
             }
         }
         return new Object[] { null, this };
     }
+
+    private static Fn.Presenter getOnlyPresenter() {
+        return onlyPresenter.get();
+    }
+
+    private static void setOnlyPresenter(Fn.Presenter aOnlyPresenter) {
+        onlyPresenter = new WeakReference<Fn.Presenter>(aOnlyPresenter);
+    }
 }
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 d2ed578..9f49d84 100644
--- a/ko4j/src/test/java/org/netbeans/html/ko4j/DoubleViewTest.java
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/DoubleViewTest.java
@@ -19,11 +19,15 @@
 package org.netbeans.html.ko4j;
 
 import java.awt.FlowLayout;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.concurrent.CountDownLatch;
 import javafx.application.Platform;
 import javafx.embed.swing.JFXPanel;
+import javafx.scene.Parent;
 import javafx.scene.Scene;
+import javafx.scene.control.Label;
 import javafx.scene.layout.BorderPane;
 import javafx.scene.web.WebView;
 import javax.swing.JFrame;
@@ -31,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.junit.NbTestCase;
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import org.testng.annotations.AfterMethod;
@@ -57,6 +62,7 @@ public class DoubleViewTest {
     private WebView view2;
 
     private static final StringBuffer LOG = new StringBuffer();
+    private JFrame frame;
 
     @BeforeMethod
     public void initializeViews() throws Exception {
@@ -104,12 +110,12 @@ public class DoubleViewTest {
         view1 = displayWebView(panel);
         view2 = displayWebView(p2);
 
-        JFrame f = new JFrame();
-        f.getContentPane().setLayout(new FlowLayout());
-        f.getContentPane().add(panel);
-        f.getContentPane().add(p2);
-        f.pack();
-        f.setVisible(true);
+        frame = new JFrame();
+        frame.getContentPane().setLayout(new FlowLayout());
+        frame.getContentPane().add(panel);
+        frame.getContentPane().add(p2);
+        frame.pack();
+        frame.setVisible(true);
     }
 
     private WebView displayWebView(JFXPanel panel) {
@@ -152,6 +158,29 @@ public class DoubleViewTest {
 
     @AfterMethod
     public void waitABit() throws Exception {
+        final CountDownLatch cdl = new CountDownLatch(1);
+        Platform.runLater(() -> {
+            Parent p1 = view1.getParent();
+            ((BorderPane)p1).setCenter(new Label("Searching for GC root"));
+            Parent p2 = view2.getParent();
+            ((BorderPane)p2).setCenter(new Label("Searching for GC root"));
+            cdl.countDown();
+        });
+        cdl.await();
+
+        Reference<?> r1 = new WeakReference<>(view1);
+        Reference<?> r2 = new WeakReference<>(view2);
+
+        view1 = null;
+        view2 = null;
+
+        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-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 03/07: No presenter means empty MapObjs.toArray

Posted by jt...@apache.org.
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 dd0f930218fe9820c40b583d12f5e2a6028999f9
Author: Jaroslav Tulach <ja...@apidesign.org>
AuthorDate: Sat May 4 09:01:21 2019 +0200

    No presenter means empty MapObjs.toArray
---
 .../main/java/org/netbeans/html/ko4j/Knockout.java    |  2 --
 .../src/main/java/org/netbeans/html/ko4j/MapObjs.java | 19 ++++++++++++-------
 .../test/java/org/netbeans/html/ko4j/MapObjsTest.java |  7 +++++++
 3 files changed, 19 insertions(+), 9 deletions(-)

diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
index e95ff87..f2f7635 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
@@ -21,8 +21,6 @@ package org.netbeans.html.ko4j;
 import java.io.Closeable;
 import java.io.IOException;
 import java.lang.ref.Reference;
-import java.util.HashMap;
-import java.util.Map;
 import java.util.concurrent.Executor;
 import net.java.html.js.JavaScriptBody;
 import net.java.html.js.JavaScriptResource;
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
index bf9d998..96114ec 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
@@ -33,8 +33,8 @@ final class MapObjs {
         reset();
     }
 
-    static void reset() {
-        setOnlyPresenter(null);
+    synchronized static void reset() {
+        onlyPresenter = null;
         usePresenter = true;
     }
 
@@ -74,7 +74,7 @@ final class MapObjs {
         return key == getOnlyPresenter() ? now : null;
     }
 
-    static Object[] remove(Object now, Fn.Presenter key) {
+    synchronized static Object[] remove(Object now, Fn.Presenter key) {
         if (now instanceof MapObjs) {
             return ((MapObjs)now).remove(key);
         }
@@ -85,7 +85,11 @@ final class MapObjs {
         if (now instanceof MapObjs) {
             return ((MapObjs) now).all.toArray();
         }
-        return new Object[] { getOnlyPresenter(), now };
+        final Fn.Presenter p = getOnlyPresenter();
+        if (p == null) {
+            return new Object[0];
+        }
+        return new Object[] { p, now };
     }
 
     private Object put(Fn.Presenter key, Object js) {
@@ -127,10 +131,11 @@ final class MapObjs {
     }
 
     private static Fn.Presenter getOnlyPresenter() {
-        return onlyPresenter.get();
+        final Fn.Presenter p = onlyPresenter == null ? null : onlyPresenter.get();
+        return p;
     }
 
-    private static void setOnlyPresenter(Fn.Presenter aOnlyPresenter) {
-        onlyPresenter = new WeakReference<Fn.Presenter>(aOnlyPresenter);
+    private static void setOnlyPresenter(Fn.Presenter p) {
+        onlyPresenter = new WeakReference<Fn.Presenter>(p);
     }
 }
diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/MapObjsTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/MapObjsTest.java
index 43c5c18..1f397d8 100644
--- a/ko4j/src/test/java/org/netbeans/html/ko4j/MapObjsTest.java
+++ b/ko4j/src/test/java/org/netbeans/html/ko4j/MapObjsTest.java
@@ -20,6 +20,7 @@ package org.netbeans.html.ko4j;
 
 import java.io.Reader;
 import java.net.URL;
+import java.util.Arrays;
 import org.netbeans.html.boot.spi.Fn;
 import static org.testng.Assert.*;
 import org.testng.annotations.BeforeMethod;
@@ -41,6 +42,12 @@ public class MapObjsTest {
     }
 
     @Test
+    public void testToArrayNoPresenterYet() {
+        Object[] arr = MapObjs.toArray(null);
+        assertEquals(arr.length, 0, "Empty array: " + Arrays.toString(arr));
+    }
+
+    @Test
     public void testValuesForP1P2() {
         Value v1 = new Value();
         Value v2 = new Value();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 02/07: Avoid null presenter as soon as possible

Posted by jt...@apache.org.
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 eb8179dc9a67de9b0aebd2b18ddfd6277a508f0e
Author: Jaroslav Tulach <ja...@apidesign.org>
AuthorDate: Sat May 4 09:00:39 2019 +0200

    Avoid null presenter as soon as possible
---
 boot/src/main/java/org/netbeans/html/boot/spi/Fn.java                | 4 ++++
 boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java           | 5 +++++
 .../src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java | 4 ++--
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
index 3dc4e30..0b8eb35 100644
--- a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
@@ -169,9 +169,13 @@ public abstract class Fn {
      * 
      * @param p the presenter that should be active until closable is closed
      * @return the closable to close
+     * @throws NullPointerException if the {@code p} is {@code null}
      * @since 0.7
      */
     public static Closeable activate(Presenter p) {
+        if (p == null) {
+            throw new NullPointerException();
+        }
         return FnContext.activate(p);
     }
     
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java b/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java
index 89689bc..60a824a 100644
--- a/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/FnTest.java
@@ -124,6 +124,11 @@ public class FnTest extends JsClassLoaderBase {
         methodClass = loader.loadClass(JsMethods.class.getName());
         close.close();
     }
+
+    @Test(expectedExceptions = NullPointerException.class)
+    public void nullProcessorNPE() {
+        Fn.activate(null);
+    }
     
     @Test public void flushingPresenter() throws IOException {
         class FP implements Fn.Presenter, Flushable {
diff --git a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java
index e37ffec..06aafaf 100644
--- a/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java
+++ b/boot/src/test/java/org/netbeans/html/boot/impl/JsClassLoaderBase.java
@@ -205,7 +205,7 @@ public class JsClassLoaderBase {
     @Test public void plusOrMul() throws Throwable {
         Method st = methodClass.getMethod("plusOrMul", int.class, int.class);
         assertNotNull(Fn.activePresenter(), "Is there a presenter?");
-        Closeable c = Fn.activate(null);
+        Closeable c = FnContext.activate(null);
         try {
             assertNull(Fn.activePresenter(), "No presenter now");
             assertEquals(st.invoke(null, 6, 7), 42, "Mul in Java");
@@ -214,7 +214,7 @@ public class JsClassLoaderBase {
         }
         assertNotNull(Fn.activePresenter(), "Is there a presenter again");
         assertEquals(st.invoke(null, 6, 7), 13, "Plus in JavaScript");
-        c = Fn.activate(null);
+        c = FnContext.activate(null);
         try {
             assertNull(Fn.activePresenter(), "No presenter again");
             assertEquals(st.invoke(null, 6, 7), 42, "Mul in Java");


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 04/07: Allow Presenters to define their own way to reference itself

Posted by jt...@apache.org.
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 998a5086c49ab1a16b1e926c53d0a49a13bb4482
Author: Jaroslav Tulach <ja...@apidesign.org>
AuthorDate: Sat May 4 10:10:31 2019 +0200

    Allow Presenters to define their own way to reference itself
---
 boot-fx/pom.xml                                    |  7 ++
 .../netbeans/html/boot/fx/AbstractFXPresenter.java | 32 ++++++-
 .../html/boot/fx/AbstractFXPresenterTest.java      | 77 +++++++++++++++++
 boot/pom.xml                                       |  7 ++
 .../java/org/netbeans/html/boot/impl/FnUtils.java  |  3 +
 .../html/boot/impl/JavaScriptProcesor.java         | 19 ++++-
 .../netbeans/html/boot/spi/FallbackIdentity.java   | 56 +++++++++++++
 .../main/java/org/netbeans/html/boot/spi/Fn.java   | 57 +++++++------
 .../html/boot/spi/FallbackIdentityTest.java        | 97 ++++++++++++++++++++++
 .../java/org/netbeans/html/ko4j/CacheObjs.java     | 10 +--
 .../main/java/org/netbeans/html/ko4j/Knockout.java |  3 +-
 .../main/java/org/netbeans/html/ko4j/MapObjs.java  | 26 +++---
 12 files changed, 345 insertions(+), 49 deletions(-)

diff --git a/boot-fx/pom.xml b/boot-fx/pom.xml
index f486bc4..d095f04 100644
--- a/boot-fx/pom.xml
+++ b/boot-fx/pom.xml
@@ -83,6 +83,13 @@
       <artifactId>javafx-web</artifactId>
       <scope>provided</scope>
     </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-netbeans-modules-nbjunit</artifactId>
+      <version>${netbeans.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
   </dependencies>
     <description>A presentation provider to show JavaFX WebView 
 when a Java/HTML based application is about to boot.</description>
diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
index 965d337..26e1329 100644
--- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
@@ -50,7 +50,7 @@ import org.netbeans.html.boot.spi.Fn;
  * @author Jaroslav Tulach
  */
 public abstract class AbstractFXPresenter implements Fn.Presenter,
-Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
+Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable, Fn.Identity {
     static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
     protected static int cnt;
     protected Runnable onLoad;
@@ -62,6 +62,7 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
     private JSObject newPOJOImpl;
     private Object undefined;
     private JavaValues values;
+    private Id id;
 
     @Override
     protected AbstractFXPresenter clone() {
@@ -72,6 +73,7 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
             p.undefined = null;
             p.newPOJOImpl = null;
             p.values = null;
+            p.id = null;
             return p;
         } catch (CloneNotSupportedException ex) {
             throw new IllegalStateException(ex);
@@ -602,4 +604,32 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable {
         return pojo[0];
     }
 
+    @Override
+    public synchronized Fn.Identity id() {
+        if (id == null) {
+            id = new Id(this);
+        }
+        return id;
+    }
+
+    @Override
+    public Fn.Presenter presenter() {
+        return this;
+    }
+
+    private static final class Id extends WeakReference<AbstractFXPresenter> implements Fn.Identity {
+        Id(AbstractFXPresenter referent) {
+            super(referent);
+        }
+
+        @Override
+        public Fn.Identity id() {
+            return this;
+        }
+
+        @Override
+        public Fn.Presenter presenter() {
+            return get();
+        }
+    }
 }
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java
new file mode 100644
index 0000000..7176dbf
--- /dev/null
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java
@@ -0,0 +1,77 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.html.boot.fx;
+
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import javafx.scene.web.WebView;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.junit.NbTestCase;
+import org.testng.Assert;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertSame;
+import org.testng.annotations.Test;
+
+public class AbstractFXPresenterTest {
+    @Test
+    public void id() {
+        AbstractFXPresenter p1 = new AbstractFXPresenter() {
+            @Override
+            void waitFinished() {
+            }
+
+            @Override
+            WebView findView(URL resource) {
+                return null;
+            }
+        };
+        AbstractFXPresenter p2 = new AbstractFXPresenter() {
+            @Override
+            void waitFinished() {
+            }
+
+            @Override
+            WebView findView(URL resource) {
+                return null;
+            }
+        };
+
+        Fn.Identity id1 = Fn.id(p1);
+        Fn.Identity id12 = Fn.id(p1);
+
+        assertSame(id1, id12);
+        assertEquals(id1, id12);
+
+        Fn.Identity id2 = Fn.id(p2);
+        Fn.Identity id22 = Fn.id(p2);
+
+        assertSame(id2, id22);
+        assertEquals(id2, id22);
+
+        Assert.assertNotEquals(id1, id2);
+
+        Reference<AbstractFXPresenter> ref1 = new WeakReference<>(p1);
+        p1 = null;
+        NbTestCase.assertGC("Presenter can disappear", ref1);
+
+        AbstractFXPresenter p2Clone = p2.clone();
+        Assert.assertNotEquals(p2.id(), p2Clone.id());
+    }
+}
diff --git a/boot/pom.xml b/boot/pom.xml
index c86d162..59fc227 100644
--- a/boot/pom.xml
+++ b/boot/pom.xml
@@ -77,6 +77,13 @@
       <version>${project.version}</version>
       <type>jar</type>
     </dependency>
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-netbeans-modules-nbjunit</artifactId>
+      <version>${netbeans.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
   </dependencies>
     <description>Builder to launch your Java/HTML based application.</description>
 </project>
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
index 9d47b06..02e753f 100644
--- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
@@ -651,6 +651,9 @@ public final class FnUtils {
             if (name.equals(Fn.Presenter.class.getName())) {
                 return Fn.Presenter.class;
             }
+            if (name.equals(Fn.Identity.class.getName())) {
+                return Fn.Identity.class;
+            }
             if (name.equals(Fn.ToJavaScript.class.getName())) {
                 return Fn.ToJavaScript.class;
             }
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
index 3f259fa..5e421ae 100644
--- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
@@ -400,19 +400,30 @@ public final class JavaScriptProcesor extends AbstractProcessor {
             source.append("package ").append(pkgName).append(";\n");
             source.append("public final class $JsCallbacks$ {\n");
             source.append("  static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
-            source.append("  private final java.lang.ref.Reference<org.netbeans.html.boot.spi.Fn.Presenter> ref;\n");
+            source.append("  private final org.netbeans.html.boot.spi.Fn.Identity id;\n");
             source.append("  private $JsCallbacks$ next;\n");
             source.append("  private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n");
-            source.append("    this.ref = new java.lang.ref.WeakReference<org.netbeans.html.boot.spi.Fn.Presenter>(p);\n");
+            source.append("    this.id = org.netbeans.html.boot.spi.Fn.id(p);\n");
             source.append("  }\n");
             source.append("  synchronized final $JsCallbacks$ current() {\n");
             source.append("    org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n");
             source.append("    $JsCallbacks$ thiz = this;\n");
+            source.append("    $JsCallbacks$ prev = null;\n");
             source.append("    for (;;) {\n");
-            source.append("      if (now == thiz.ref.get()) return thiz;\n");
+            source.append("      if (thiz.id != null) {\n");
+            source.append("        org.netbeans.html.boot.spi.Fn.Presenter thizPresenter = thiz.id.presenter();\n");
+            source.append("        if (thizPresenter == null) {\n");
+            source.append("          if (prev != null) {\n");
+            source.append("            prev.next = thiz.next;\n");
+            source.append("          }\n");
+            source.append("        } else {\n");
+            source.append("          if (thizPresenter == now) return thiz;\n");
+            source.append("        }\n");
+            source.append("      }\n");
             source.append("      if (thiz.next == null) {\n");
             source.append("        return thiz.next = new $JsCallbacks$(now);\n");
             source.append("      }\n");
+            source.append("      prev = thiz;\n");
             source.append("      thiz = thiz.next;\n");
             source.append("    }\n");
             source.append("  }\n");
@@ -486,7 +497,7 @@ public final class JavaScriptProcesor extends AbstractProcessor {
             sep = ", ";
         }
         source.append(") throws Throwable {\n");
-        source.append("    org.netbeans.html.boot.spi.Fn.Presenter p = ref.get(); \n");
+        source.append("    org.netbeans.html.boot.spi.Fn.Presenter p = id.presenter(); \n");
         source.append(convert);
         if (useTryResources()) {
             source.append("    try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n");
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java b/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java
new file mode 100644
index 0000000..a1d0cfc
--- /dev/null
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java
@@ -0,0 +1,56 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.html.boot.spi;
+
+import java.lang.ref.WeakReference;
+
+final class FallbackIdentity extends WeakReference<Fn.Presenter> implements Fn.Identity {
+    private final int hashCode;
+
+    FallbackIdentity(Fn.Presenter p) {
+        super(p);
+        this.hashCode = p.hashCode();
+    }
+
+    @Override
+    public Fn.Identity id() {
+        return this;
+    }
+
+    @Override
+    public Fn.Presenter presenter() {
+        return get();
+    }
+
+    @Override
+    public int hashCode() {
+        return hashCode;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+        if (obj instanceof FallbackIdentity) {
+            return ((FallbackIdentity) obj).presenter() == presenter();
+        }
+        return false;
+    }
+}
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
index 0b8eb35..f98abde 100644
--- a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
@@ -23,8 +23,6 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -40,7 +38,7 @@ import org.netbeans.html.boot.impl.FnContext;
  * @author Jaroslav Tulach
  */
 public abstract class Fn {
-    private final Reference<Presenter> presenter;
+    private final Identity presenter;
     
     /**
      * @deprecated Ineffective as of 0.6. 
@@ -58,7 +56,7 @@ public abstract class Fn {
      * @since 0.6 
      */
     protected Fn(Presenter presenter) {
-        this.presenter = new WeakReference<Presenter>(presenter);
+        this.presenter = id(presenter);
     }
 
     /** True, if currently active presenter is the same as presenter this
@@ -67,7 +65,7 @@ public abstract class Fn {
      * @return true, if proper presenter is used
      */
     public final boolean isValid() {
-        return FnContext.currentPresenter(false) == presenter.get();
+        return FnContext.currentPresenter(false) == presenter.presenter();
     }
     
     /** Helper method to check if the provided instance is valid function.
@@ -178,6 +176,16 @@ public abstract class Fn {
         }
         return FnContext.activate(p);
     }
+
+    public static Identity id(final Presenter p) {
+        if (p == null) {
+            return null;
+        }
+        if (p instanceof Identity) {
+            return ((Identity) p).id();
+        }
+        return new FallbackIdentity(p);
+    }
     
     /** Invokes the defined function with specified <code>this</code> and
      * appropriate arguments.
@@ -211,7 +219,7 @@ public abstract class Fn {
      * @since 0.7
      */
     protected final Presenter presenter() {
-        return presenter.get();
+        return presenter.presenter();
     }
     
     /** The representation of a <em>presenter</em> - usually a browser window.
@@ -324,8 +332,17 @@ public abstract class Fn {
         public Fn defineFn(String code, String[] names, boolean[] keepAlive);
     }
 
+    public interface Identity {
+        public Identity id();
+        public Presenter presenter();
+        @Override
+        public int hashCode();
+        @Override
+        public boolean equals(Object obj);
+    }
+
     private static class Preload extends Fn {
-        private static Map<String, Set<Reference<Presenter>>> LOADED;
+        private static Map<String, Set<Identity>> LOADED;
         private final Fn fn;
         private final String resource;
         private final Class<?> caller;
@@ -350,21 +367,21 @@ public abstract class Fn {
         }
 
         private void loadResource() throws Exception {
-            Reference<Presenter> ref = super.presenter;
-            if (ref == null) {
-                ref = new WeakReference<Fn.Presenter>(FnContext.currentPresenter(false));
+            Identity id = super.presenter;
+            if (id == null) {
+                id = id(FnContext.currentPresenter(false));
             }
-            Fn.Presenter realPresenter = ref == null ? null : ref.get();
+            Fn.Presenter realPresenter = id == null ? null : id.presenter();
             if (realPresenter != null) {
                 if (LOADED == null) {
-                    LOADED = new HashMap<String, Set<Reference<Presenter>>>();
+                    LOADED = new HashMap<String, Set<Identity>>();
                 }
-                Set<Reference<Presenter>> there = LOADED.get(resource);
+                Set<Identity> there = LOADED.get(resource);
                 if (there == null) {
-                    there = new HashSet<Reference<Fn.Presenter>>();
+                    there = new HashSet<Identity>();
                     LOADED.put(resource, there);
                 }
-                if (addNewRef(there, ref)) {
+                if (there.add(id)) {
                     final ClassLoader l = caller.getClassLoader();
                     InputStream is = l.getResourceAsStream(resource);
                     if (is == null && resource.startsWith("/")) {
@@ -382,14 +399,6 @@ public abstract class Fn {
                 }
             }
         }
-
-        private static synchronized boolean addNewRef(Set<Reference<Presenter>> set, Reference<Presenter> ref) {
-            for (Reference<Presenter> r : set) {
-                if (r.get() == ref.get()) {
-                    return false;
-                }
-            }
-            return set.add(ref);
-        }
     }
+
 }
diff --git a/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java b/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java
new file mode 100644
index 0000000..760531f
--- /dev/null
+++ b/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.html.boot.spi;
+
+import java.io.Reader;
+import java.lang.ref.Reference;
+import java.lang.ref.WeakReference;
+import java.net.URL;
+import org.netbeans.junit.NbTestCase;
+import static org.testng.Assert.*;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+public class FallbackIdentityTest {
+
+    public FallbackIdentityTest() {
+    }
+
+    @Test
+    public void testIdAndWeak() {
+        Fn.Presenter p = new Fn.Presenter() {
+            @Override
+            public Fn defineFn(String arg0, String... arg1) {
+                return null;
+            }
+
+            @Override
+            public void displayPage(URL arg0, Runnable arg1) {
+            }
+
+            @Override
+            public void loadScript(Reader arg0) throws Exception {
+            }
+        };
+
+        Fn.Identity id1 = Fn.id(p);
+        Fn.Identity id2 = Fn.id(p);
+
+        assertNotSame(id1, id2);
+        assertEquals(id1, id2);
+        assertEquals(id1.hashCode(), id2.hashCode());
+
+        Reference<Fn.Presenter> ref = new WeakReference<>(p);
+        p = null;
+        NbTestCase.assertGC("Presenter is held weakly", ref);
+    }
+
+    @Test
+    public void testPresenterCanProvideItsOwnIdentity() {
+        class IdPresenter implements Fn.Presenter, Fn.Identity {
+            @Override
+            public Fn defineFn(String code, String... names) {
+                return null;
+            }
+
+            @Override
+            public void displayPage(URL page, Runnable onPageLoad) {
+            }
+
+            @Override
+            public void loadScript(Reader code) throws Exception {
+            }
+
+            @Override
+            public Fn.Identity id() {
+                return this;
+            }
+
+            @Override
+            public Fn.Presenter presenter() {
+                return this;
+            }
+        }
+        IdPresenter p = new IdPresenter();
+
+        assertSame(p, Fn.id(p));
+    }
+}
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
index 74fe85d..8b969ff 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
@@ -19,26 +19,24 @@
 
 package org.netbeans.html.ko4j;
 
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
 import org.netbeans.html.boot.spi.Fn;
 
 final class CacheObjs {
     /* both @GuardedBy CacheObjs.class */
-    private static CacheObjs[] list = new CacheObjs[16];
+    private static final CacheObjs[] list = new CacheObjs[16];
     private static int listAt = 0;
-    private final Reference<Fn.Presenter> ref;
+    private final Fn.Identity ref;
 
     /* both @GuardedBy presenter single threaded access */
     private Object[] jsObjects;
     private int jsIndex;
 
     private CacheObjs(Fn.Presenter p) {
-        this.ref = new WeakReference<Fn.Presenter>(p);
+        this.ref = Fn.id(p);
     }
 
     Fn.Presenter get() {
-        return ref.get();
+        return ref.presenter();
     }
 
     static synchronized CacheObjs find(Fn.Presenter key) {
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
index f2f7635..883d27b 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
@@ -20,7 +20,6 @@ package org.netbeans.html.ko4j;
 
 import java.io.Closeable;
 import java.io.IOException;
-import java.lang.ref.Reference;
 import java.util.concurrent.Executor;
 import net.java.html.js.JavaScriptBody;
 import net.java.html.js.JavaScriptResource;
@@ -156,7 +155,7 @@ final class Knockout  {
             if (obj == null) {
                 return null;
             } else {
-                return (Fn.Presenter) ((Reference<?>)obj).get();
+                return ((Fn.Identity) obj).presenter();
             }
         }
     }
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
index 96114ec..c2b996e 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
@@ -19,14 +19,12 @@
 
 package org.netbeans.html.ko4j;
 
-import java.lang.ref.Reference;
-import java.lang.ref.WeakReference;
 import java.util.List;
 import net.java.html.json.Models;
 import org.netbeans.html.boot.spi.Fn;
 
 final class MapObjs {
-    private static Reference<Fn.Presenter> onlyPresenter;
+    private static Fn.Identity onlyPresenter;
     private static boolean usePresenter;
 
     static {
@@ -40,8 +38,12 @@ final class MapObjs {
 
     private final List<Object> all;
 
-    private MapObjs(Object... arr) {
-        this.all = Models.asList(arr);
+    private MapObjs(Fn.Identity id1, Object js) {
+        this.all = Models.asList(id1, js);
+    }
+
+    private MapObjs(Fn.Identity id1, Object js1, Fn.Identity id2, Object js2) {
+        this.all = Models.asList(id1, js1, id2, js2);
     }
 
 
@@ -60,9 +62,9 @@ final class MapObjs {
                 }
             }
             if (now == null) {
-                return new MapObjs(new WeakReference<Fn.Presenter>(key), js);
+                return new MapObjs(Fn.id(key), js);
             } else {
-                return new MapObjs(onlyPresenter, now, new WeakReference<Fn.Presenter>(key), js);
+                return new MapObjs(onlyPresenter, now, Fn.id(key), js);
             }
         }
     }
@@ -99,15 +101,15 @@ final class MapObjs {
                 return this;
             }
         }
-        all.add(new WeakReference<Fn.Presenter>(key));
+        all.add(Fn.id(key));
         all.add(js);
         return this;
     }
 
     boolean isSameKey(int index, Fn.Presenter key) {
         Object at = all.get(index);
-        if (at instanceof Reference<?>) {
-            at = ((Reference<?>)at).get();
+        if (at instanceof Fn.Identity) {
+            at = ((Fn.Identity)at).presenter();
         }
         return at == key;
     }
@@ -131,11 +133,11 @@ final class MapObjs {
     }
 
     private static Fn.Presenter getOnlyPresenter() {
-        final Fn.Presenter p = onlyPresenter == null ? null : onlyPresenter.get();
+        final Fn.Presenter p = onlyPresenter == null ? null : onlyPresenter.presenter();
         return p;
     }
 
     private static void setOnlyPresenter(Fn.Presenter p) {
-        onlyPresenter = new WeakReference<Fn.Presenter>(p);
+        onlyPresenter = Fn.id(p);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 07/07: Point to PR-19 that introduces Fn.Ref

Posted by jt...@apache.org.
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 a74337e715e1f3855d60ac098292951346683fd2
Author: Jaroslav Tulach <ja...@apidesign.org>
AuthorDate: Sat May 4 13:04:31 2019 +0200

    Point to PR-19 that introduces Fn.Ref
---
 src/main/javadoc/overview.html | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html
index 3e4a06c..0842c11 100644
--- a/src/main/javadoc/overview.html
+++ b/src/main/javadoc/overview.html
@@ -166,10 +166,12 @@ $ mvn -f client/pom.xml process-classes exec:exec
         <h3>New in version 1.6.1</h3>
         <p>
             One model instance can be used in two views
-            (<a target="_blank" href="https://github.com/apache/incubator-netbeans-html4j/pull/14">PR #14</a>).
+            (<a target="_blank" href="https://github.com/apache/netbeans-html4j/pull/14">PR #14</a>).
+            GC related behavior has been improved
+            (<a target="_blank" href="https://github.com/apache/netbeans-html4j/pull/19">PR #19</a>).
             Safe and {@link net.java.html.boot.script.Scripts sanitized builder} to
             create {@link javax.script.ScriptEngine}-based execution environment
-            (<a target="_blank" href="https://github.com/apache/incubator-netbeans-html4j/pull/15">PR #15</a>).
+            (<a target="_blank" href="https://github.com/apache/netbeans-html4j/pull/15">PR #15</a>).
         </p>
 
         <h3>New in version 1.6</h3>
@@ -180,10 +182,10 @@ $ mvn -f client/pom.xml process-classes exec:exec
             when necessary.
             {@link net.java.html.json.ComputedProperty Computed properties} can
             depend on other computed properties -
-            <a target="_blank" href="https://github.com/apache/incubator-netbeans-html4j/pull/3">PR #3</a>.
+            <a target="_blank" href="https://github.com/apache/netbeans-html4j/pull/3">PR #3</a>.
             {@link net.java.html.js.JavaScriptResource} annotation has been
             made repeatable -
-            <a target="_blank" href="https://github.com/apache/incubator-netbeans-html4j/pull/4">PR #4</a>.
+            <a target="_blank" href="https://github.com/apache/netbeans-html4j/pull/4">PR #4</a>.
             <code>@Model</code> annotation processor bugfix 
             <a target="_blank" href="https://issues.apache.org/jira/browse/NETBEANS-621">#621</a>.
         </p>
@@ -193,9 +195,9 @@ $ mvn -f client/pom.xml process-classes exec:exec
         <p>
             The project has been donated to <a target="_blank" href="http://apache.org">Apache Foundation</a>
             and the code is now hosted in the
-            <a target="_blank" href="http://github.com/apache/incubator-netbeans-html4j">incubator repository</a>
+            <a target="_blank" href="http://github.com/apache/netbeans-html4j">incubator repository</a>
             along other Apache incubating projects. Contribute to the project by forking its
-            <a target="_blank" href="http://github.com/apache/incubator-netbeans-html4j">GitHub repository</a>.
+            <a target="_blank" href="http://github.com/apache/netbeans-html4j">GitHub repository</a>.
         </p>
 
         <p>


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 06/07: Avoid memory leaks via ChangeListener. Workaround FX problem on JDK11.

Posted by jt...@apache.org.
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 <ja...@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-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists


[netbeans-html4j] 05/07: Documenting use of Fn.ref

Posted by jt...@apache.org.
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 9f5b51df2e8db2e66f31085365b5d5b43d7ede83
Author: Jaroslav Tulach <ja...@apidesign.org>
AuthorDate: Sat May 4 10:40:56 2019 +0200

    Documenting use of Fn.ref
---
 .../netbeans/html/boot/fx/AbstractFXPresenter.java | 12 ++--
 .../html/boot/fx/AbstractFXPresenterTest.java      | 12 ++--
 .../java/org/netbeans/html/boot/impl/FnUtils.java  |  4 +-
 .../html/boot/impl/JavaScriptProcesor.java         | 10 +--
 .../netbeans/html/boot/spi/FallbackIdentity.java   |  4 +-
 .../main/java/org/netbeans/html/boot/spi/Fn.java   | 77 ++++++++++++++++++----
 .../html/boot/spi/FallbackIdentityTest.java        | 19 +++---
 .../java/org/netbeans/html/ko4j/CacheObjs.java     |  4 +-
 .../main/java/org/netbeans/html/ko4j/Knockout.java |  2 +-
 .../main/java/org/netbeans/html/ko4j/MapObjs.java  | 18 ++---
 10 files changed, 107 insertions(+), 55 deletions(-)

diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
index 26e1329..0e1737a 100644
--- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
+++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/AbstractFXPresenter.java
@@ -50,7 +50,7 @@ import org.netbeans.html.boot.spi.Fn;
  * @author Jaroslav Tulach
  */
 public abstract class AbstractFXPresenter implements Fn.Presenter,
-Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable, Fn.Identity {
+Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable, Fn.Ref<AbstractFXPresenter> {
     static final Logger LOG = Logger.getLogger(FXPresenter.class.getName());
     protected static int cnt;
     protected Runnable onLoad;
@@ -605,7 +605,7 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable, Fn.Identi
     }
 
     @Override
-    public synchronized Fn.Identity id() {
+    public synchronized Fn.Ref<AbstractFXPresenter> reference() {
         if (id == null) {
             id = new Id(this);
         }
@@ -613,22 +613,22 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable, Fn.Identi
     }
 
     @Override
-    public Fn.Presenter presenter() {
+    public AbstractFXPresenter presenter() {
         return this;
     }
 
-    private static final class Id extends WeakReference<AbstractFXPresenter> implements Fn.Identity {
+    private static final class Id extends WeakReference<AbstractFXPresenter> implements Fn.Ref<AbstractFXPresenter> {
         Id(AbstractFXPresenter referent) {
             super(referent);
         }
 
         @Override
-        public Fn.Identity id() {
+        public Fn.Ref<AbstractFXPresenter> reference() {
             return this;
         }
 
         @Override
-        public Fn.Presenter presenter() {
+        public AbstractFXPresenter presenter() {
             return get();
         }
     }
diff --git a/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java b/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java
index 7176dbf..1c3a1cc 100644
--- a/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java
+++ b/boot-fx/src/test/java/org/netbeans/html/boot/fx/AbstractFXPresenterTest.java
@@ -53,14 +53,16 @@ public class AbstractFXPresenterTest {
             }
         };
 
-        Fn.Identity id1 = Fn.id(p1);
-        Fn.Identity id12 = Fn.id(p1);
+        Fn.Ref<?> id1 = Fn.ref(p1);
+        Fn.Ref<?> id12 = Fn.ref(p1);
 
         assertSame(id1, id12);
         assertEquals(id1, id12);
+        assertSame(p1, id1.presenter());
 
-        Fn.Identity id2 = Fn.id(p2);
-        Fn.Identity id22 = Fn.id(p2);
+        Fn.Ref<?> id2 = Fn.ref(p2);
+        Fn.Ref<?> id22 = Fn.ref(p2);
+        assertSame(p2, id2.presenter());
 
         assertSame(id2, id22);
         assertEquals(id2, id22);
@@ -72,6 +74,6 @@ public class AbstractFXPresenterTest {
         NbTestCase.assertGC("Presenter can disappear", ref1);
 
         AbstractFXPresenter p2Clone = p2.clone();
-        Assert.assertNotEquals(p2.id(), p2Clone.id());
+        Assert.assertNotEquals(p2.reference(), p2Clone.reference());
     }
 }
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
index 02e753f..ed390dd 100644
--- a/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/FnUtils.java
@@ -651,8 +651,8 @@ public final class FnUtils {
             if (name.equals(Fn.Presenter.class.getName())) {
                 return Fn.Presenter.class;
             }
-            if (name.equals(Fn.Identity.class.getName())) {
-                return Fn.Identity.class;
+            if (name.equals(Fn.Ref.class.getName())) {
+                return Fn.Ref.class;
             }
             if (name.equals(Fn.ToJavaScript.class.getName())) {
                 return Fn.ToJavaScript.class;
diff --git a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
index 5e421ae..9344809 100644
--- a/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
+++ b/boot/src/main/java/org/netbeans/html/boot/impl/JavaScriptProcesor.java
@@ -400,18 +400,18 @@ public final class JavaScriptProcesor extends AbstractProcessor {
             source.append("package ").append(pkgName).append(";\n");
             source.append("public final class $JsCallbacks$ {\n");
             source.append("  static final $JsCallbacks$ VM = new $JsCallbacks$(null);\n");
-            source.append("  private final org.netbeans.html.boot.spi.Fn.Identity id;\n");
+            source.append("  private final org.netbeans.html.boot.spi.Fn.Ref<?> ref;\n");
             source.append("  private $JsCallbacks$ next;\n");
             source.append("  private $JsCallbacks$(org.netbeans.html.boot.spi.Fn.Presenter p) {\n");
-            source.append("    this.id = org.netbeans.html.boot.spi.Fn.id(p);\n");
+            source.append("    this.ref = org.netbeans.html.boot.spi.Fn.ref(p);\n");
             source.append("  }\n");
             source.append("  synchronized final $JsCallbacks$ current() {\n");
             source.append("    org.netbeans.html.boot.spi.Fn.Presenter now = org.netbeans.html.boot.spi.Fn.activePresenter();\n");
             source.append("    $JsCallbacks$ thiz = this;\n");
             source.append("    $JsCallbacks$ prev = null;\n");
             source.append("    for (;;) {\n");
-            source.append("      if (thiz.id != null) {\n");
-            source.append("        org.netbeans.html.boot.spi.Fn.Presenter thizPresenter = thiz.id.presenter();\n");
+            source.append("      if (thiz.ref != null) {\n");
+            source.append("        org.netbeans.html.boot.spi.Fn.Presenter thizPresenter = thiz.ref.presenter();\n");
             source.append("        if (thizPresenter == null) {\n");
             source.append("          if (prev != null) {\n");
             source.append("            prev.next = thiz.next;\n");
@@ -497,7 +497,7 @@ public final class JavaScriptProcesor extends AbstractProcessor {
             sep = ", ";
         }
         source.append(") throws Throwable {\n");
-        source.append("    org.netbeans.html.boot.spi.Fn.Presenter p = id.presenter(); \n");
+        source.append("    org.netbeans.html.boot.spi.Fn.Presenter p = ref.presenter(); \n");
         source.append(convert);
         if (useTryResources()) {
             source.append("    try (java.io.Closeable a = org.netbeans.html.boot.spi.Fn.activate(p)) { \n");
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java b/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java
index a1d0cfc..087842c 100644
--- a/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/FallbackIdentity.java
@@ -20,7 +20,7 @@ package org.netbeans.html.boot.spi;
 
 import java.lang.ref.WeakReference;
 
-final class FallbackIdentity extends WeakReference<Fn.Presenter> implements Fn.Identity {
+final class FallbackIdentity extends WeakReference<Fn.Presenter> implements Fn.Ref {
     private final int hashCode;
 
     FallbackIdentity(Fn.Presenter p) {
@@ -29,7 +29,7 @@ final class FallbackIdentity extends WeakReference<Fn.Presenter> implements Fn.I
     }
 
     @Override
-    public Fn.Identity id() {
+    public Fn.Ref reference() {
         return this;
     }
 
diff --git a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
index f98abde..1a613ea 100644
--- a/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
+++ b/boot/src/main/java/org/netbeans/html/boot/spi/Fn.java
@@ -23,6 +23,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.lang.ref.WeakReference;
 import java.net.URL;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -38,7 +39,7 @@ import org.netbeans.html.boot.impl.FnContext;
  * @author Jaroslav Tulach
  */
 public abstract class Fn {
-    private final Identity presenter;
+    private final Ref presenter;
     
     /**
      * @deprecated Ineffective as of 0.6. 
@@ -56,7 +57,7 @@ public abstract class Fn {
      * @since 0.6 
      */
     protected Fn(Presenter presenter) {
-        this.presenter = id(presenter);
+        this.presenter = ref(presenter);
     }
 
     /** True, if currently active presenter is the same as presenter this
@@ -177,12 +178,25 @@ public abstract class Fn {
         return FnContext.activate(p);
     }
 
-    public static Identity id(final Presenter p) {
+    /** Obtains a (usually {@linkplain WeakReference weak}) reference to
+     * the presenter. Such reference is suitable for embedding in various long
+     * living structures with a life-cycle that may outspan the one of presenter.
+     *
+     * @param p the presenter
+     * @return reference to the presenter, {@code null} only if {@code p} is {@code null}
+     * @since 1.6.1
+     * @see Ref
+     */
+    public static Ref<?> ref(final Presenter p) {
         if (p == null) {
             return null;
         }
-        if (p instanceof Identity) {
-            return ((Identity) p).id();
+        if (p instanceof Ref<?>) {
+            Ref<?> r = ((Ref<?>) p).reference();
+            if (r == null) {
+                throw new NullPointerException();
+            }
+            return r;
         }
         return new FallbackIdentity(p);
     }
@@ -332,17 +346,52 @@ public abstract class Fn {
         public Fn defineFn(String code, String[] names, boolean[] keepAlive);
     }
 
-    public interface Identity {
-        public Identity id();
-        public Presenter presenter();
+    /**
+     * Reference to a {@link Presenter}.Each implementation of a {@link Presenter}
+     * may choose a way to reference itself (usually in a {@linkplain WeakReference weak way})
+     * effectively. Various code that needs to hold a reference to a presenter
+     * is then encouraged to obtain such reference via {@link Fn#ref(org.netbeans.html.boot.spi.Fn.Presenter)}
+     * call and hold on to it. Holding a reference to an instance of {@link Presenter}
+     * is discouraged as it may lead to memory leaks.
+     * <p>
+     * Presenters willing to to represent a reference to itself effectively shall
+     * also implement the {@link Ref} interface and return reasonable reference
+     * from the {@link #reference()} method.
+     *
+     * @param <P> the type of the presenter
+     * @see Fn#ref(org.netbeans.html.boot.spi.Fn.Presenter)
+     * @since 1.6.1
+     */
+    public interface Ref<P extends Presenter> {
+        /** Creates a reference to a presenter.
+         * Rather than calling this method directly, call {@link Fn#ref(org.netbeans.html.boot.spi.Fn.Presenter)}.
+         * @return a (weak) reference to the associated presenter
+         */
+        public Ref<P> reference();
+
+        /** The associated presenter.
+         *
+         * @return the presenter or {@code null}, if it has been GCed meanwhile
+         */
+        public P presenter();
+
+        /** Reference must properly implement {@link #hashCode} and {@link #equals}.
+         *
+         * @return proper hashcode
+         */
         @Override
         public int hashCode();
+
+        /** Reference must properly implement {@link #hashCode} and {@link #equals}.
+         *
+         * @return proper equals result
+         */
         @Override
         public boolean equals(Object obj);
     }
 
     private static class Preload extends Fn {
-        private static Map<String, Set<Identity>> LOADED;
+        private static Map<String, Set<Ref>> LOADED;
         private final Fn fn;
         private final String resource;
         private final Class<?> caller;
@@ -367,18 +416,18 @@ public abstract class Fn {
         }
 
         private void loadResource() throws Exception {
-            Identity id = super.presenter;
+            Ref id = super.presenter;
             if (id == null) {
-                id = id(FnContext.currentPresenter(false));
+                id = ref(FnContext.currentPresenter(false));
             }
             Fn.Presenter realPresenter = id == null ? null : id.presenter();
             if (realPresenter != null) {
                 if (LOADED == null) {
-                    LOADED = new HashMap<String, Set<Identity>>();
+                    LOADED = new HashMap<String, Set<Ref>>();
                 }
-                Set<Identity> there = LOADED.get(resource);
+                Set<Ref> there = LOADED.get(resource);
                 if (there == null) {
-                    there = new HashSet<Identity>();
+                    there = new HashSet<Ref>();
                     LOADED.put(resource, there);
                 }
                 if (there.add(id)) {
diff --git a/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java b/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java
index 760531f..6a57522 100644
--- a/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java
+++ b/boot/src/test/java/org/netbeans/html/boot/spi/FallbackIdentityTest.java
@@ -24,10 +24,6 @@ import java.lang.ref.WeakReference;
 import java.net.URL;
 import org.netbeans.junit.NbTestCase;
 import static org.testng.Assert.*;
-import org.testng.annotations.AfterClass;
-import org.testng.annotations.AfterMethod;
-import org.testng.annotations.BeforeClass;
-import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 public class FallbackIdentityTest {
@@ -52,8 +48,8 @@ public class FallbackIdentityTest {
             }
         };
 
-        Fn.Identity id1 = Fn.id(p);
-        Fn.Identity id2 = Fn.id(p);
+        Fn.Ref<?> id1 = Fn.ref(p);
+        Fn.Ref<?> id2 = Fn.ref(p);
 
         assertNotSame(id1, id2);
         assertEquals(id1, id2);
@@ -66,7 +62,7 @@ public class FallbackIdentityTest {
 
     @Test
     public void testPresenterCanProvideItsOwnIdentity() {
-        class IdPresenter implements Fn.Presenter, Fn.Identity {
+        class IdPresenter implements Fn.Presenter, Fn.Ref {
             @Override
             public Fn defineFn(String code, String... names) {
                 return null;
@@ -81,7 +77,7 @@ public class FallbackIdentityTest {
             }
 
             @Override
-            public Fn.Identity id() {
+            public Fn.Ref reference() {
                 return this;
             }
 
@@ -92,6 +88,11 @@ public class FallbackIdentityTest {
         }
         IdPresenter p = new IdPresenter();
 
-        assertSame(p, Fn.id(p));
+        assertSame(p, Fn.ref(p));
+    }
+
+    @Test
+    public void nullYieldsNullReference() {
+        assertNull(Fn.ref(null));
     }
 }
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
index 8b969ff..e1aea1a 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/CacheObjs.java
@@ -25,14 +25,14 @@ final class CacheObjs {
     /* both @GuardedBy CacheObjs.class */
     private static final CacheObjs[] list = new CacheObjs[16];
     private static int listAt = 0;
-    private final Fn.Identity ref;
+    private final Fn.Ref<?> ref;
 
     /* both @GuardedBy presenter single threaded access */
     private Object[] jsObjects;
     private int jsIndex;
 
     private CacheObjs(Fn.Presenter p) {
-        this.ref = Fn.id(p);
+        this.ref = Fn.ref(p);
     }
 
     Fn.Presenter get() {
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
index 883d27b..1e826d8 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java
@@ -155,7 +155,7 @@ final class Knockout  {
             if (obj == null) {
                 return null;
             } else {
-                return ((Fn.Identity) obj).presenter();
+                return ((Fn.Ref) obj).presenter();
             }
         }
     }
diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
index c2b996e..56a56bc 100644
--- a/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
+++ b/ko4j/src/main/java/org/netbeans/html/ko4j/MapObjs.java
@@ -24,7 +24,7 @@ import net.java.html.json.Models;
 import org.netbeans.html.boot.spi.Fn;
 
 final class MapObjs {
-    private static Fn.Identity onlyPresenter;
+    private static Fn.Ref onlyPresenter;
     private static boolean usePresenter;
 
     static {
@@ -38,11 +38,11 @@ final class MapObjs {
 
     private final List<Object> all;
 
-    private MapObjs(Fn.Identity id1, Object js) {
+    private MapObjs(Fn.Ref id1, Object js) {
         this.all = Models.asList(id1, js);
     }
 
-    private MapObjs(Fn.Identity id1, Object js1, Fn.Identity id2, Object js2) {
+    private MapObjs(Fn.Ref id1, Object js1, Fn.Ref id2, Object js2) {
         this.all = Models.asList(id1, js1, id2, js2);
     }
 
@@ -62,9 +62,9 @@ final class MapObjs {
                 }
             }
             if (now == null) {
-                return new MapObjs(Fn.id(key), js);
+                return new MapObjs(Fn.ref(key), js);
             } else {
-                return new MapObjs(onlyPresenter, now, Fn.id(key), js);
+                return new MapObjs(onlyPresenter, now, Fn.ref(key), js);
             }
         }
     }
@@ -101,15 +101,15 @@ final class MapObjs {
                 return this;
             }
         }
-        all.add(Fn.id(key));
+        all.add(Fn.ref(key));
         all.add(js);
         return this;
     }
 
     boolean isSameKey(int index, Fn.Presenter key) {
         Object at = all.get(index);
-        if (at instanceof Fn.Identity) {
-            at = ((Fn.Identity)at).presenter();
+        if (at instanceof Fn.Ref) {
+            at = ((Fn.Ref)at).presenter();
         }
         return at == key;
     }
@@ -138,6 +138,6 @@ final class MapObjs {
     }
 
     private static void setOnlyPresenter(Fn.Presenter p) {
-        onlyPresenter = Fn.id(p);
+        onlyPresenter = Fn.ref(p);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@netbeans.apache.org
For additional commands, e-mail: commits-help@netbeans.apache.org

For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists