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/incubator-netbeans-html4j.git
commit 246d0733a636def7bd4c951895e3b5d7cd73ce65 Author: Jaroslav Tulach <[email protected]> AuthorDate: Thu Oct 26 10:24:07 2017 +0200 NETBEANS-99: Let @Model classes point directly to Knockout structures and only indirectly to JavaScript representation of ko objects. Stress test by FXGCPresenter --- .../java/net/java/html/boot/fx/FXBrowsers.java | 71 ++--------------- .../netbeans/html/boot/fx/AbstractFXPresenter.java | 34 +++++--- .../java/org/netbeans/html/boot/fx/FXBrwsr.java | 4 +- .../fx/{FXPresenter.java => FXGCPresenter.java} | 57 +++++++++++--- .../org/netbeans/html/boot/fx/FXPresenter.java | 6 +- .../netbeans/html/boot/fx/InitializeWebView.java | 90 ++++++++++++++++++++++ .../java/org/netbeans/html/json/impl/Bindings.java | 6 +- .../java/org/netbeans/html/json/impl/JSON.java | 2 +- .../org/netbeans/html/json/spi/Technology.java | 36 +++++++-- .../netbeans/html/wstyrus/TyrusKnockoutTest.java | 10 +-- .../main/java/org/netbeans/html/ko4j/KOTech.java | 41 ++++++---- .../main/java/org/netbeans/html/ko4j/Knockout.java | 4 + .../org/netbeans/html/ko4j/KnockoutFXTest.java | 3 +- src/main/javadoc/overview.html | 3 + .../org/netbeans/html/xhr4j/JsonKnockoutTest.java | 3 +- 15 files changed, 247 insertions(+), 123 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 8ceae82..4ae8909 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 @@ -18,16 +18,13 @@ */ package net.java.html.boot.fx; +import org.netbeans.html.boot.fx.InitializeWebView; import java.net.URL; import javafx.application.Platform; -import javafx.beans.value.ChangeListener; -import javafx.beans.value.ObservableValue; -import javafx.concurrent.Worker; 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.fx.AbstractFXPresenter; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.context.spi.Contexts.Id; @@ -89,7 +86,7 @@ public final class FXBrowsers { ) { Object[] context = new Object[args.length + 1]; System.arraycopy(args, 0, context, 1, args.length); - final Load load = new Load(webView, null); + final InitializeWebView load = new InitializeWebView(webView, null); context[0] = load; BrowserBuilder.newBrowser(context). loadPage(url.toExternalForm()). @@ -172,7 +169,7 @@ public final class FXBrowsers { ) { Object[] newCtx = new Object[context.length + 1]; System.arraycopy(context, 0, newCtx, 1, context.length); - final Load load = new Load(webView, onPageLoad); + final InitializeWebView load = new InitializeWebView(webView, onPageLoad); newCtx[0] = load; BrowserBuilder.newBrowser(newCtx). loadPage(url.toExternalForm()). @@ -203,67 +200,9 @@ public final class FXBrowsers { */ public static void runInBrowser(WebView webView, Runnable code) { Object ud = webView.getUserData(); - if (!(ud instanceof Load)) { + if (!(ud instanceof InitializeWebView)) { throw new IllegalArgumentException(); } - ((Load)ud).ctx.execute(code); + ((InitializeWebView)ud).runInContext(code); } - - private static class Load extends AbstractFXPresenter implements Runnable { - private final WebView webView; - private final Runnable myLoad; - private BrwsrCtx ctx; - - public Load(WebView webView, Runnable onLoad) { - this.webView = webView; - this.myLoad = onLoad; - webView.setUserData(this); - } - - public void run() { - ctx = BrwsrCtx.findDefault(Load.class); - if (myLoad != null) { - myLoad.run(); - } - } - - @Override - protected void waitFinished() { - // don't wait - } - - @Override - protected 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; - } - }); - - return webView; - } - } - } 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 08590c1..53dce08 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 @@ -22,6 +22,7 @@ import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; import java.io.Reader; +import java.lang.ref.Reference; import java.lang.ref.WeakReference; import java.lang.reflect.Array; import java.net.URL; @@ -182,9 +183,9 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { waitFinished(); } - protected abstract void waitFinished(); + abstract void waitFinished(); - protected abstract WebView findView(final URL resource); + abstract WebView findView(final URL resource); final JSObject convertArrays(Object[] arr) { for (int i = 0; i < arr.length; i++) { @@ -569,8 +570,9 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { Object java = obj.getMember("fxBrwsrId"); if (java instanceof JSObject) { for (;;) { - int resultHash; - int resultId; + final int resultHash; + final int resultId; + final NavigableSet<Ref> refs; synchronized (this) { this.hash = -1; this.id = -1; @@ -579,28 +581,38 @@ Fn.KeepAlive, Fn.ToJavaScript, Fn.FromJavaScript, Executor, Cloneable { assert this.id != -1; resultHash = this.hash; resultId = this.id; + refs = values.get(resultHash); + if (refs == null) { + return null; + } } - final NavigableSet<Ref> refs = values.get(resultHash); Iterator<Ref> it = refs.iterator(); while (it.hasNext()) { Ref next = it.next(); - Object pojo = next.value(); - if (next.id() == resultId) { - return pojo; - } - if (pojo == null) { + Object[] pojo = { next.value() }; + if (pojo[0] == null) { it.remove(); + continue; + } + if (next.id() == resultId) { + return emitJavaObject(pojo, resultHash, resultId); } } if (refs.isEmpty()) { - values.remove(resultHash); + synchronized (this) { + values.remove(resultHash); + } } } } return obj; } + } + Object emitJavaObject(Object[] pojo, int hash, int id) { + return pojo[0]; + } } diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java index 5fd6039..a6f96f7 100644 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXBrwsr.java @@ -65,7 +65,7 @@ public class FXBrwsr extends Application { private static final CountDownLatch FINISHED = new CountDownLatch(1); private BorderPane root; - public static synchronized WebView findWebView(final URL url, final FXPresenter onLoad) { + public static synchronized WebView findWebView(final URL url, final AbstractFXPresenter onLoad) { if (INSTANCE == null) { final String callee = findCalleeClassName(); Executors.newFixedThreadPool(1).submit(new Runnable() { @@ -211,7 +211,7 @@ public class FXBrwsr extends Application { return arr; } - private WebView newView(final URL url, final FXPresenter onLoad) { + private WebView newView(final URL url, final AbstractFXPresenter onLoad) { final WebView view = new WebView(); view.setContextMenuEnabled(false); Stage newStage; diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXGCPresenter.java similarity index 53% copy from boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java copy to boot-fx/src/main/java/org/netbeans/html/boot/fx/FXGCPresenter.java index 3be269e..330b13a 100644 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXGCPresenter.java @@ -19,22 +19,22 @@ package org.netbeans.html.boot.fx; import java.io.File; +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; +import java.util.logging.Level; import javafx.scene.web.WebView; -import net.java.html.boot.BrowserBuilder; -import org.netbeans.html.boot.spi.Fn; -import org.openide.util.lookup.ServiceProvider; -/** This is an implementation class, use {@link BrowserBuilder} API. Just - * include this JAR on classpath and the {@link BrowserBuilder} API will find - * this implementation automatically. +/** Presenter for stress testing. It tries to force few GC cycles + * before returning a Java object from {@link WebView} to simulate the + * fact that in JDK8 newer than build 112 the Java objects exposed to + * {@link WebView} are held by weak references. * * @author Jaroslav Tulach */ -@ServiceProvider(service = Fn.Presenter.class) -public final class FXPresenter extends AbstractFXPresenter { +public final class FXGCPresenter extends AbstractFXPresenter { static { try { try { @@ -54,11 +54,48 @@ public final class FXPresenter extends AbstractFXPresenter { } } - protected void waitFinished() { + @Override + void waitFinished() { FXBrwsr.waitFinished(); } - protected WebView findView(final URL resource) { + @Override + WebView findView(final URL resource) { return FXBrwsr.findWebView(resource, this); } + + @Override + Object emitJavaObject(Object[] pojo, int hash, int id) { + Reference<Object> ref = new WeakReference<Object>(pojo[0]); + boolean nonNull = ref.get() != null; + assertGC(ref); + Object r; + if ((r = ref.get()) == null && nonNull) { + throw new NullPointerException("Value has been GCed to null for " + hash + " and " + id); + } + return r; + } + + private static boolean isGone(Reference<?> ref) { + return ref.get() == null; + } + + private static void assertGC(Reference<Object> ref) { + long l = System.currentTimeMillis(); + for (int i = 0; i < 3; i++) { + if (isGone(ref)) { + return; + } + + try { + System.gc(); + System.runFinalization(); + } catch (Error err) { + LOG.log(Level.INFO, "Problems during GCing attempt of " + ref.get(), err); + } + } + final long took = System.currentTimeMillis() - l; + LOG.log(Level.FINE, "Good: No GC of {1} for {0} ms.", new Object[]{took, ref.get()}); + } + } diff --git a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java index 3be269e..1554113 100644 --- a/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/FXPresenter.java @@ -54,11 +54,13 @@ public final class FXPresenter extends AbstractFXPresenter { } } - protected void waitFinished() { + @Override + void waitFinished() { FXBrwsr.waitFinished(); } - protected WebView findView(final URL resource) { + @Override + WebView findView(final URL resource) { return FXBrwsr.findWebView(resource, this); } } 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 new file mode 100644 index 0000000..b093565 --- /dev/null +++ b/boot-fx/src/main/java/org/netbeans/html/boot/fx/InitializeWebView.java @@ -0,0 +1,90 @@ +/** + * 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.net.URL; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.concurrent.Worker; +import javafx.scene.web.WebView; +import net.java.html.BrwsrCtx; +import org.netbeans.html.boot.fx.AbstractFXPresenter; + +public final class InitializeWebView extends AbstractFXPresenter implements Runnable { + + private final WebView webView; + private final Runnable myLoad; + BrwsrCtx ctx; + + public InitializeWebView(WebView webView, Runnable onLoad) { + this.webView = webView; + this.myLoad = onLoad; + webView.setUserData(this); + } + + @Override + public void run() { + ctx = BrwsrCtx.findDefault(InitializeWebView.class); + if (myLoad != null) { + myLoad.run(); + } + } + + @Override + void waitFinished() { + // don't wait + } + + @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; + } + }); + return webView; + } + + public final void runInContext(Runnable r) { + ctx.execute(r); + } + +} diff --git a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java index fdd1b57..3b0e062 100644 --- a/json/src/main/java/org/netbeans/html/json/impl/Bindings.java +++ b/json/src/main/java/org/netbeans/html/json/impl/Bindings.java @@ -69,7 +69,11 @@ public final class Bindings<Data> { } - public Data koData() { + final Object jsObj() { + if (bp instanceof Technology.ToJavaScript) { + Technology.ToJavaScript<Data> toJS = (Technology.ToJavaScript<Data>) bp; + return toJS.toJavaScript(data); + } return data; } diff --git a/json/src/main/java/org/netbeans/html/json/impl/JSON.java b/json/src/main/java/org/netbeans/html/json/impl/JSON.java index 1745c0c..09c691f 100644 --- a/json/src/main/java/org/netbeans/html/json/impl/JSON.java +++ b/json/src/main/java/org/netbeans/html/json/impl/JSON.java @@ -267,7 +267,7 @@ public final class JSON { return null; } final Bindings b = PropertyBindingAccessor.getBindings(proto, true, null); - return b == null ? null : b.koData(); + return b == null ? null : b.jsObj(); } private static Proto findProto(Object object) { diff --git a/json/src/main/java/org/netbeans/html/json/spi/Technology.java b/json/src/main/java/org/netbeans/html/json/spi/Technology.java index 2e0a206..b138dbe 100644 --- a/json/src/main/java/org/netbeans/html/json/spi/Technology.java +++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java @@ -23,16 +23,19 @@ import net.java.html.json.Model; import net.java.html.json.Models; import org.netbeans.html.context.spi.Contexts.Id; -/** An implementation of a binding between model classes (see {@link Model}) +/** * An implementation of a binding between model classes (see {@link Model}) * and particular technology like <a href="http://knockoutjs.com">knockout.js</a> - * in a browser window, etc. - * Since introduction of {@link Id technology identifiers} one can choose between - * different background implementations to handle the conversion and - * communication requests. The currently known provider is - * <code>org.netbeans.html:ko4j</code> module which registers + * in a browser window, etc.Since introduction of {@link Id technology identifiers} one can choose between + different background implementations to handle the conversion and + communication requests. + * The currently known provider is + <code>org.netbeans.html:ko4j</code> module which registers * a <a href="http://knockoutjs.com" target="_blank">knockout.js</a> * implementation called <b>ko4j</b>. * + * @param <Data> technology internal type that keeps internal data for each + * instance of {@linkplains Model model class}. + * * @author Jaroslav Tulach */ public interface Technology<Data> { @@ -189,4 +192,25 @@ public interface Technology<Data> { */ public D wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr); } + + /** Convertor of the internal data type to object suitable as a JavaScript + * representation. Certain technologies need to keep some data in Java + * and only part of them in JavaScript-ready object. With the help of + * {@code ToJavaScript} interface, they can parametrize their + * {@link Technology} with the Java type and implement + * {@link #toJavaScript(java.lang.Object)} + * method to extract the proper JavaScript part from that object. + * + * @param <D> the internal data type + * @since 1.5.1 + */ + public static interface ToJavaScript<D> extends Technology<D> { + /** Extracts JavaScript ready representation. + * + * @param data technology's internal data structure + * @return object ready to represent the data in JavaScript + * @since 1.5.1 + */ + public Object toJavaScript(D data); + } } diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java index 0a17692..c651346 100644 --- a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java +++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusKnockoutTest.java @@ -35,6 +35,9 @@ import java.util.concurrent.Executors; import net.java.html.BrwsrCtx; import net.java.html.boot.BrowserBuilder; import net.java.html.js.JavaScriptBody; +import org.json.JSONException; +import org.json.JSONObject; +import org.netbeans.html.boot.fx.FXGCPresenter; import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.json.spi.Technology; @@ -42,13 +45,10 @@ import org.netbeans.html.json.spi.Transfer; import org.netbeans.html.json.spi.WSTransfer; import org.netbeans.html.json.tck.KOTest; import org.netbeans.html.json.tck.KnockoutTCK; -import org.json.JSONException; -import org.json.JSONObject; -import org.netbeans.html.boot.impl.FnContext; import org.netbeans.html.ko4j.KO4J; import org.openide.util.lookup.ServiceProvider; import org.testng.Assert; -import static org.testng.Assert.*; +import static org.testng.Assert.assertEquals; import org.testng.annotations.Factory; /** @@ -75,7 +75,7 @@ public final class TyrusKnockoutTest extends KnockoutTCK { URI uri = TyrusDynamicHTTP.initServer(); - final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(TyrusKnockoutTest.class). + final BrowserBuilder bb = BrowserBuilder.newBrowser(new FXGCPresenter()).loadClass(TyrusKnockoutTest.class). loadPage(uri.toString()). invoke("initialized"); diff --git a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java index c94e5ae..32f0ea9 100644 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java @@ -34,7 +34,8 @@ import org.netbeans.html.json.spi.Technology; */ @Contexts.Id("ko4j") final class KOTech -implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Technology.ApplyId<Object> { +implements Technology.BatchCopy<Knockout>, Technology.ValueMutated<Knockout>, +Technology.ApplyId<Knockout>, Technology.ToJavaScript<Knockout> { private Object[] jsObjects; private int jsIndex; @@ -42,11 +43,11 @@ implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Techno } @Override - public Object wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr) { + public Knockout wrapModel(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr) { return createKO(model, copyFrom, propArr, funcArr, null); } - final Object createKO(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr, Knockout[] ko) { + final Knockout createKO(Object model, Object copyFrom, PropertyBinding[] propArr, FunctionBinding[] funcArr, Knockout[] ko) { String[] propNames = new String[propArr.length]; Number[] propInfo = new Number[propArr.length]; Object[] propValues = new Object[propArr.length]; @@ -76,7 +77,7 @@ implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Techno ret, copyFrom, propNames, propInfo, propValues, funcNames ); - return ret; + return newKO; } private Object getJSObject() { @@ -95,42 +96,43 @@ implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Techno } @Override - public Object wrapModel(Object model) { + public Knockout wrapModel(Object model) { throw new UnsupportedOperationException(); } @Override - public void bind(PropertyBinding b, Object model, Object data) { + public void bind(PropertyBinding b, Object model, Knockout data) { throw new UnsupportedOperationException(); } @Override - public void valueHasMutated(Object data, String propertyName) { - Knockout.cleanUp(); - Knockout.valueHasMutated(data, propertyName, null, null); + public void valueHasMutated(Knockout data, String propertyName) { + valueHasMutated(data, propertyName, null, null); } @Override - public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) { + public void valueHasMutated(Knockout data, String propertyName, Object oldValue, Object newValue) { Knockout.cleanUp(); - if (newValue instanceof Enum) { - newValue = newValue.toString(); + if (data != null) { + if (newValue instanceof Enum) { + newValue = newValue.toString(); + } + Knockout.valueHasMutated(data.js(), propertyName, oldValue, newValue); } - Knockout.valueHasMutated(data, propertyName, oldValue, newValue); } @Override - public void expose(FunctionBinding fb, Object model, Object d) { + public void expose(FunctionBinding fb, Object model, Knockout data) { throw new UnsupportedOperationException(); } @Override - public void applyBindings(Object data) { + public void applyBindings(Knockout data) { applyBindings(null, data); } @Override - public void applyBindings(String id, Object data) { - Object ko = Knockout.applyBindings(id, data); + public void applyBindings(String id, Knockout data) { + Object ko = Knockout.applyBindings(id, data.js()); if (ko instanceof Knockout) { ((Knockout)ko).hold(); applied.add((Knockout) ko); @@ -153,4 +155,9 @@ implements Technology.BatchCopy<Object>, Technology.ValueMutated<Object>, Techno public <M> M toModel(Class<M> modelClass, Object data) { return modelClass.cast(Knockout.toModel(data)); } + + @Override + public Object toJavaScript(Knockout data) { + return data.js(); + } } 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 7500715..ae52c39 100644 --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java @@ -71,6 +71,10 @@ final class Knockout { } } + final Object js() { + return js; + } + static void cleanUp() { for (;;) { Knockout ko = null; diff --git a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java index 4306983..8bde691 100644 --- a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java @@ -34,6 +34,7 @@ import java.util.concurrent.Executors; import net.java.html.BrwsrCtx; import net.java.html.boot.BrowserBuilder; import net.java.html.js.JavaScriptBody; +import org.netbeans.html.boot.fx.FXGCPresenter; import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.json.spi.Technology; @@ -70,7 +71,7 @@ public final class KnockoutFXTest extends KnockoutTCK { URI uri = DynamicHTTP.initServer(); - final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutFXTest.class). + final BrowserBuilder bb = BrowserBuilder.newBrowser(new FXGCPresenter()).loadClass(KnockoutFXTest.class). loadPage(uri.toString()). invoke("initialized"); diff --git a/src/main/javadoc/overview.html b/src/main/javadoc/overview.html index 9bdbc8b..b9f066a 100644 --- a/src/main/javadoc/overview.html +++ b/src/main/javadoc/overview.html @@ -60,6 +60,9 @@ {@link net.java.html.json.ComputedProperty computing a property}. Regular subclassing of {@link org.netbeans.html.json.spi.Proto.Type} is possible. + Bugfix <a target="_blank" href="https://issues.apache.org/jira/browse/NETBEANS-99">#99</a> + - better garbage collector related behavior of <b>ko4j</b> instances thanks + to introduction of {@link org.netbeans.html.json.spi.Technology.ToJavaScript}. <h3>New in version 1.5</h3> diff --git a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java index b148218..168de1d 100644 --- a/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java +++ b/xhr4j/src/test/java/org/netbeans/html/xhr4j/JsonKnockoutTest.java @@ -35,6 +35,7 @@ import java.util.concurrent.Executors; import net.java.html.BrwsrCtx; import net.java.html.boot.BrowserBuilder; import net.java.html.js.JavaScriptBody; +import org.netbeans.html.boot.fx.FXGCPresenter; import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.json.spi.Technology; @@ -69,7 +70,7 @@ public final class JsonKnockoutTest extends KnockoutTCK { URI uri = JsonDynamicHTTP.initServer(); - final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(JsonKnockoutTest.class). + final BrowserBuilder bb = BrowserBuilder.newBrowser(new FXGCPresenter()).loadClass(JsonKnockoutTest.class). loadPage(uri.toString()). invoke("initialized"); -- To stop receiving notification emails like this one, please contact "[email protected]" <[email protected]>.
