# HG changeset patch # User Jaroslav Tulach # Date 1417681315 -3600 # Node ID 88d62267a0b56c2d9cfa39ae9a5f9c03cf734184 # Parent 6e20d36f1a1368c98dbf71e55f36b0e0c737eeb0 #248918: Introducing technology identifiers diff -r 6e20d36f1a13 -r 88d62267a0b5 boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java --- a/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java Wed Dec 03 11:41:24 2014 +0100 +++ b/boot-fx/src/main/java/net/java/html/boot/fx/FXBrowsers.java Thu Dec 04 09:21:55 2014 +0100 @@ -52,6 +52,8 @@ 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; /** Utility methods to use {@link WebView} and {@link JavaScriptBody} code * in existing JavaFX applications. @@ -82,6 +84,16 @@ *

* This method sets {@link WebView#getUserData()} and {@link #runInBrowser(javafx.scene.web.WebView, java.lang.Runnable)} * relies on the value. Please don't alter it. + *

+ * Since introduction of {@link Id technology identifiers} the + * provided args strings are also passed to the + * {@link BrwsrCtx context} when it is being + * {@link Contexts#newBuilder(java.lang.Object...) created} + * and can influence the selection + * of available technologies + * (like {@link org.netbeans.html.json.spi.Technology}, + * {@link org.netbeans.html.json.spi.Transfer} or + * {@link org.netbeans.html.json.spi.WSTransfer}). * * @param webView the instance of Web View to tweak * @param url the URL of the HTML page to load into the view @@ -94,7 +106,10 @@ Class onPageLoad, String methodName, String... args ) { - BrowserBuilder.newBrowser(new Load(webView)). + Object[] context = new Object[args.length + 1]; + System.arraycopy(args, 0, context, 1, args.length); + context[0] = new Load(webView); + BrowserBuilder.newBrowser(context). loadPage(url.toExternalForm()). loadClass(onPageLoad). invoke(methodName, args). diff -r 6e20d36f1a13 -r 88d62267a0b5 boot/src/main/java/net/java/html/boot/BrowserBuilder.java --- a/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Wed Dec 03 11:41:24 2014 +0100 +++ b/boot/src/main/java/net/java/html/boot/BrowserBuilder.java Thu Dec 04 09:21:55 2014 +0100 @@ -65,6 +65,7 @@ import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.boot.spi.Fn.Presenter; import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; import org.netbeans.html.boot.impl.FindResources; import org.netbeans.html.boot.impl.FnContext; import org.netbeans.html.boot.impl.FnUtils; @@ -121,6 +122,15 @@ /** Entry method to obtain a new browser builder. Follow by calling * its instance methods like {@link #loadClass(java.lang.Class)} and * {@link #loadPage(java.lang.String)}. + * Since introduction of {@link Id technology identifiers} the + * provided context objects are also passed to the + * {@link BrwsrCtx context} when it is being + * {@link Contexts#newBuilder(java.lang.Object...) created} + * and can influence the selection + * of available technologies + * (like {@link org.netbeans.html.json.spi.Technology}, + * {@link org.netbeans.html.json.spi.Transfer} or + * {@link org.netbeans.html.json.spi.WSTransfer}) by name. * * @param context any instances that should be available to the builder - * implementation dependant @@ -301,7 +311,7 @@ if (browserClass != null) { browserClass[0] = newClazz; } - Contexts.Builder cb = Contexts.newBuilder(); + Contexts.Builder cb = Contexts.newBuilder(context); if (!Contexts.fillInByProviders(newClazz, cb)) { LOG.log(Level.WARNING, "Using empty technology for {0}", newClazz); } diff -r 6e20d36f1a13 -r 88d62267a0b5 context/src/main/java/net/java/html/BrwsrCtx.java --- a/context/src/main/java/net/java/html/BrwsrCtx.java Wed Dec 03 11:41:24 2014 +0100 +++ b/context/src/main/java/net/java/html/BrwsrCtx.java Thu Dec 04 09:21:55 2014 +0100 @@ -47,6 +47,7 @@ import org.netbeans.html.context.impl.CtxAccssr; import org.netbeans.html.context.impl.CtxImpl; import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.context.spi.Contexts.Id; /** Represents context where the net.java.html.json.Model * and other objects @@ -54,7 +55,12 @@ * The context is also associated with the actual HTML technology * in the HTML page - there is likely to be different context for * knockout.js and different one - * for angular. + * for angular. Since version 1.1 + * the content of contexts can be selected by registering + * implementations under specific + * {@link Id technology identifiers} and requesting them during + * {@link Contexts#newBuilder(java.lang.Object...) construction} of the + * context. * * @author Jaroslav Tulach */ diff -r 6e20d36f1a13 -r 88d62267a0b5 context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java --- a/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java Wed Dec 03 11:41:24 2014 +0100 +++ b/context/src/main/java/org/netbeans/html/context/impl/CtxImpl.java Thu Dec 04 09:21:55 2014 +0100 @@ -44,8 +44,10 @@ import java.util.ArrayList; import java.util.Collections; +import java.util.Comparator; import java.util.List; import net.java.html.BrwsrCtx; +import org.netbeans.html.context.spi.Contexts; /** Implementation detail. Holds list of technologies for particular * {@link BrwsrCtx}. @@ -54,13 +56,15 @@ */ public final class CtxImpl { private final List> techs; + private final Object[] context; - public CtxImpl() { - techs = new ArrayList>(); + public CtxImpl(Object[] context) { + this(context, new ArrayList>()); } - private CtxImpl(List> techs) { + private CtxImpl(Object[] context, List> techs) { this.techs = techs; + this.context = context; } public static Tech find(BrwsrCtx context, Class technology) { @@ -74,9 +78,9 @@ } public BrwsrCtx build() { - Collections.sort(techs); + Collections.sort(techs, new BindCompare()); final List> arr = Collections.unmodifiableList(techs); - CtxImpl impl = new CtxImpl(arr); + CtxImpl impl = new CtxImpl(context, arr); BrwsrCtx ctx = CtxAccssr.getDefault().newContext(impl); return ctx; } @@ -85,7 +89,7 @@ techs.add(new Bind(type, impl, priority)); } - private static final class Bind implements Comparable> { + private static final class Bind { private final Class clazz; private final Tech impl; private final int priority; @@ -97,16 +101,39 @@ } @Override - public int compareTo(Bind o) { - if (priority != o.priority) { - return priority - o.priority; - } - return clazz.getName().compareTo(o.clazz.getName()); - } - - @Override public String toString() { return "Bind{" + "clazz=" + clazz + "@" + clazz.getClassLoader() + ", impl=" + impl + ", priority=" + priority + '}'; } } + + private final class BindCompare implements Comparator> { + boolean isPrefered(Bind b) { + final Class implClazz = b.impl.getClass(); + Contexts.Id id = implClazz.getAnnotation(Contexts.Id.class); + if (id == null) { + return false; + } + for (String v : id.value()) { + for (Object c : context) { + if (v.equals(c)) { + return true; + } + } + } + return false; + } + + @Override + public int compare(Bind o1, Bind o2) { + boolean p1 = isPrefered(o1); + boolean p2 = isPrefered(o2); + if (p1 != p2) { + return p1 ? -1 : 1; + } + if (o1.priority != o2.priority) { + return o1.priority - o2.priority; + } + return o1.clazz.getName().compareTo(o2.clazz.getName()); + } + } // end of BindCompare } diff -r 6e20d36f1a13 -r 88d62267a0b5 context/src/main/java/org/netbeans/html/context/spi/Contexts.java --- a/context/src/main/java/org/netbeans/html/context/spi/Contexts.java Wed Dec 03 11:41:24 2014 +0100 +++ b/context/src/main/java/org/netbeans/html/context/spi/Contexts.java Thu Dec 04 09:21:55 2014 +0100 @@ -42,6 +42,10 @@ */ package org.netbeans.html.context.spi; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ServiceLoader; import net.java.html.BrwsrCtx; import org.netbeans.html.context.impl.CtxImpl; @@ -59,11 +63,23 @@ /** Creates new, empty builder for creation of {@link BrwsrCtx}. At the * end call the {@link Builder#build()} method to generate the context. - * + * + * @param context instances of various classes or names of {@link Id technologies} + * to be preferred and used in the built {@link BrwsrCtx context}. + * @return new instance of the builder + * @since 1.1 + */ + public static Builder newBuilder(Object... context) { + return new Builder(context); + } + /** Creates new, empty builder for creation of {@link BrwsrCtx}. At the + * end call the {@link Builder#build()} method to generate the context. + * Simply calls {@link #newBuilder(java.lang.Object...) newBuilder(new Object[0])}. + * * @return new instance of the builder */ public static Builder newBuilder() { - return new Builder(); + return newBuilder(new Object[0]); } /** Seeks for the specified technology in the provided context. @@ -118,6 +134,23 @@ } return found; } + + /** Identifies the technologies passed to {@link Builder context builder} + * by a name. Each implementation of a technology + * {@link Builder#register(java.lang.Class, java.lang.Object, int) registered into a context} + * can be annotated with a name (or multiple names). Such implementation + * will later be + * {@link Contexts#fillInByProviders(java.lang.Class, org.netbeans.html.context.spi.Contexts.Builder) preferred during lookup} + * if their name(s) has been requested in when + * {@link Contexts#newBuilder(java.lang.Object...) creating a context}. + * @since 1.1 + */ + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface Id { + /** Identifier(s) for the implementation. */ + public String[] value(); + } /** Implementors of various HTML technologies should * register their implementation via org.openide.util.lookup.ServiceProvider, so @@ -152,9 +185,10 @@ * @author Jaroslav Tulach */ public static final class Builder { - private final CtxImpl impl = new CtxImpl(); + private final CtxImpl impl; - Builder() { + public Builder(Object[] context) { + this.impl = new CtxImpl(context); } /** Registers new technology into the context. Each technology is diff -r 6e20d36f1a13 -r 88d62267a0b5 context/src/test/java/net/java/html/BrwsrCtxTest.java --- a/context/src/test/java/net/java/html/BrwsrCtxTest.java Wed Dec 03 11:41:24 2014 +0100 +++ b/context/src/test/java/net/java/html/BrwsrCtxTest.java Thu Dec 04 09:21:55 2014 +0100 @@ -44,6 +44,7 @@ import org.netbeans.html.context.spi.Contexts; import static org.testng.Assert.*; +import org.testng.annotations.Test; /** * @@ -72,4 +73,48 @@ assertTrue(arr[0], "Runnable was executed"); } + + @Test public void defaultOrderOfRegistrations() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder()); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R1.class, "R1 is registered at value 10"); + } + + @Test public void preferOne() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder("one")); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R1.class, "R1 is registered at value 10"); + } + + @Test public void preferTwo() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder("two")); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R2.class, "R2 is preferred"); + } + + @Test public void preferBoth() { + BrwsrCtx ctx = registerRs(Contexts.newBuilder("one", "two")); + Class clazz = Contexts.find(ctx, Runnable.class).getClass(); + assertEquals(clazz, R1.class, "R1 is registered at value 10"); + } + + private static BrwsrCtx registerRs(Contexts.Builder b) { + b.register(Runnable.class, new R1(), 10); + b.register(Runnable.class, new R2(), 20); + return b.build(); + } + + @Contexts.Id("one") + static final class R1 implements Runnable { + @Override + public void run() { + } + } + @Contexts.Id("two") + static final class R2 implements Runnable { + @Override + public void run() { + } + } + } \ No newline at end of file diff -r 6e20d36f1a13 -r 88d62267a0b5 json/src/main/java/org/netbeans/html/json/spi/Technology.java --- a/json/src/main/java/org/netbeans/html/json/spi/Technology.java Wed Dec 03 11:41:24 2014 +0100 +++ b/json/src/main/java/org/netbeans/html/json/spi/Technology.java Thu Dec 04 09:21:55 2014 +0100 @@ -45,10 +45,17 @@ import net.java.html.BrwsrCtx; 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}) * and particular technology like knockout.js * 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 + * org.netbeans.html:ko4j module which registers + * a knockout.js + * implementation called ko4j. * * @author Jaroslav Tulach */ diff -r 6e20d36f1a13 -r 88d62267a0b5 json/src/main/java/org/netbeans/html/json/spi/Transfer.java --- a/json/src/main/java/org/netbeans/html/json/spi/Transfer.java Wed Dec 03 11:41:24 2014 +0100 +++ b/json/src/main/java/org/netbeans/html/json/spi/Transfer.java Thu Dec 04 09:21:55 2014 +0100 @@ -45,9 +45,17 @@ import java.io.IOException; import java.io.InputStream; import org.netbeans.html.context.spi.Contexts.Builder; +import org.netbeans.html.context.spi.Contexts.Id; /** A {@link Builder service provider interface} responsible for * conversion of JSON objects to Java ones and vice-versa. + * Since introduction of {@link Id technology identifiers} one can choose between + * different background implementations to handle the conversion and + * communication requests. The known providers include + * org.netbeans.html:ko4j module which registers + * a native browser implementation called xhr, and a + * org.netbeans.html:ko-ws-tyrus module which registers + * Java based implementation named tyrus. * * @author Jaroslav Tulach */ diff -r 6e20d36f1a13 -r 88d62267a0b5 json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java --- a/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java Wed Dec 03 11:41:24 2014 +0100 +++ b/json/src/main/java/org/netbeans/html/json/spi/WSTransfer.java Thu Dec 04 09:21:55 2014 +0100 @@ -44,9 +44,17 @@ import net.java.html.BrwsrCtx; import org.netbeans.html.context.spi.Contexts.Provider; +import org.netbeans.html.context.spi.Contexts.Id; /** Interface for providers of WebSocket protocol. Register into a - * {@link BrwsrCtx context} in your own {@link Provider} + * {@link BrwsrCtx context} in your own {@link Provider}. + * Since introduction of {@link Id technology identifiers} one can choose between + * different background implementations to handle the conversion and + * communication requests. The known providers include + * org.netbeans.html:ko4j module which registers + * a native browser implementation called websocket, and a + * org.netbeans.html:ko-ws-tyrus module which registers + * Java based implementation named tyrus. * * @author Jaroslav Tulach * @param internal implementation type representing the socket diff -r 6e20d36f1a13 -r 88d62267a0b5 ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java --- a/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java Wed Dec 03 11:41:24 2014 +0100 +++ b/ko-felix-test/src/main/java/org/netbeans/html/ko/felix/test/KnockoutFelixTCKImpl.java Thu Dec 04 09:21:55 2014 +0100 @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; @@ -140,16 +141,9 @@ @Override public BrwsrCtx createContext() { try { - Class fxCls = loadOSGiClass( - "org.netbeans.html.ko4j.FXContext", - FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class).getBundleContext() - ); - final Constructor cnstr = fxCls.getConstructor(Fn.Presenter.class); - cnstr.setAccessible(true); - Object fx = cnstr.newInstance(browserContext); Contexts.Builder cb = Contexts.newBuilder(). - register(Technology.class, (Technology)fx, 10). - register(Transfer.class, (Transfer)fx, 10). + register(Technology.class, (Technology)osgiInstance("KOTech"), 10). + register(Transfer.class, (Transfer)osgiInstance("KOTransfer"), 10). register(Executor.class, (Executor)browserContext, 10); // if (fx.areWebSocketsSupported()) { // cb.register(WSTransfer.class, fx, 10); @@ -160,6 +154,17 @@ } } + private Object osgiInstance(String simpleName) throws IllegalAccessException, SecurityException, IllegalArgumentException, Exception, NoSuchMethodException, InstantiationException, InvocationTargetException { + Class fxCls = loadOSGiClass( + "org.netbeans.html.ko4j." + simpleName, + FrameworkUtil.getBundle(KnockoutFelixTCKImpl.class).getBundleContext() + ); + final Constructor cnstr = fxCls.getDeclaredConstructor(); + cnstr.setAccessible(true); + Object fx = cnstr.newInstance(); + return fx; + } + @Override public Object createJSON(Map values) { JSONObject json = new JSONObject(); diff -r 6e20d36f1a13 -r 88d62267a0b5 ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java --- a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java Wed Dec 03 11:41:24 2014 +0100 +++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java Thu Dec 04 09:21:55 2014 +0100 @@ -46,6 +46,7 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; @@ -135,16 +136,9 @@ @Override public BrwsrCtx createContext() { try { - Class fxCls = loadOSGiClass( - "org.netbeans.html.ko4j.FXContext", - FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext() - ); - final Constructor cnstr = fxCls.getConstructor(Fn.Presenter.class); - cnstr.setAccessible(true); - Object fx = cnstr.newInstance(browserContext); Contexts.Builder cb = Contexts.newBuilder(). - register(Technology.class, (Technology)fx, 10). - register(Transfer.class, (Transfer)fx, 10). + register(Technology.class, (Technology)osgiInstance("KOTech"), 10). + register(Transfer.class, (Transfer)osgiInstance("KOTransfer"), 10). register(Executor.class, (Executor)browserContext, 10); // if (fx.areWebSocketsSupported()) { // cb.register(WSTransfer.class, fx, 10); @@ -154,6 +148,16 @@ throw new IllegalStateException(ex); } } + private Object osgiInstance(String simpleName) throws IllegalAccessException, SecurityException, IllegalArgumentException, Exception, NoSuchMethodException, InstantiationException, InvocationTargetException { + Class fxCls = loadOSGiClass( + "org.netbeans.html.ko4j." + simpleName, + FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class).getBundleContext() + ); + final Constructor cnstr = fxCls.getDeclaredConstructor(); + cnstr.setAccessible(true); + Object fx = cnstr.newInstance(); + return fx; + } @Override public Object createJSON(Map values) { diff -r 6e20d36f1a13 -r 88d62267a0b5 ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java --- a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java Wed Dec 03 11:41:24 2014 +0100 +++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java Thu Dec 04 09:21:55 2014 +0100 @@ -82,6 +82,7 @@ * * @author Jaroslav Tulach */ +@Contexts.Id("tyrus") @ServiceProvider(service = Contexts.Provider.class) public final class TyrusContext implements Contexts.Provider, WSTransfer, Transfer { diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/FXContext.java Wed Dec 03 11:41:24 2014 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,233 +0,0 @@ -/** - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. - * - * Oracle and Java are registered trademarks of Oracle and/or its affiliates. - * Other names may be trademarks of their respective owners. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * The Original Software is NetBeans. The Initial Developer of the Original - * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. - * - * If you wish your version of this file to be governed by only the CDDL - * or only the GPL Version 2, indicate your decision by adding - * "[Contributor] elects to include this software in this distribution - * under the [CDDL or GPL Version 2] license." If you do not indicate a - * single choice of license, a recipient has the option to distribute - * your version of this file under either the CDDL, the GPL Version 2 or - * to extend the choice of license to its licensees as provided above. - * However, if you add GPL Version 2 code and therefore, elected the GPL - * Version 2 license, then the option applies only if the new code is - * made subject to such option by the copyright holder. - */ -package org.netbeans.html.ko4j; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; -import java.util.logging.Logger; -import org.netbeans.html.boot.spi.Fn; -import org.netbeans.html.json.spi.FunctionBinding; -import org.netbeans.html.json.spi.JSONCall; -import org.netbeans.html.json.spi.PropertyBinding; -import org.netbeans.html.json.spi.Technology; -import org.netbeans.html.json.spi.Transfer; -import org.netbeans.html.json.spi.WSTransfer; - -/** This is an implementation package - just - * include its JAR on classpath and use official {@link Context} API - * to access the functionality. - *

- * - * @author Jaroslav Tulach - */ -final class FXContext -implements Technology.BatchInit, Technology.ValueMutated, -Transfer, WSTransfer { - static final Logger LOG = Logger.getLogger(FXContext.class.getName()); - private Object[] jsObjects; - private int jsIndex; - - public FXContext(Fn.Presenter browserContext) { - } - - @Override - public Object wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) { - String[] propNames = new String[propArr.length]; - boolean[] propReadOnly = new boolean[propArr.length]; - Object[] propValues = new Object[propArr.length]; - for (int i = 0; i < propNames.length; i++) { - propNames[i] = propArr[i].getPropertyName(); - propReadOnly[i] = propArr[i].isReadOnly(); - propValues[i] = propArr[i].getValue(); - } - String[] funcNames = new String[funcArr.length]; - for (int i = 0; i < funcNames.length; i++) { - funcNames[i] = funcArr[i].getFunctionName(); - } - Object ret = getJSObject(); - Knockout.wrapModel(ret, model, - propNames, propReadOnly, propValues, propArr, - funcNames, funcArr - ); - return ret; - } - - private Object getJSObject() { - int len = 64; - if (jsObjects != null && jsIndex < (len = jsObjects.length)) { - return jsObjects[jsIndex++]; - } - jsObjects = Knockout.allocJS(len * 2); - jsIndex = 1; - return jsObjects[0]; - } - - @Override - public Object wrapModel(Object model) { - throw new UnsupportedOperationException(); - } - - @Override - public void bind(PropertyBinding b, Object model, Object data) { - throw new UnsupportedOperationException(); - } - - @Override - public void valueHasMutated(Object data, String propertyName) { - Knockout.valueHasMutated(data, propertyName, null, null); - } - - @Override - public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) { - Knockout.valueHasMutated(data, propertyName, oldValue, newValue); - } - - @Override - public void expose(FunctionBinding fb, Object model, Object d) { - throw new UnsupportedOperationException(); - } - - @Override - public void applyBindings(Object data) { - Knockout.applyBindings(data); - } - - @Override - public Object wrapArray(Object[] arr) { - return arr; - } - - @Override - public void extract(Object obj, String[] props, Object[] values) { - LoadJSON.extractJSON(obj, props, values); - } - - @Override - public void loadJSON(final JSONCall call) { - if (call.isJSONP()) { - String me = LoadJSON.createJSONP(call); - LoadJSON.loadJSONP(call.composeURL(me), me); - } else { - String data = null; - if (call.isDoOutput()) { - try { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - call.writeData(bos); - data = new String(bos.toByteArray(), "UTF-8"); - } catch (IOException ex) { - call.notifyError(ex); - } - } - LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data); - } - } - - @Override - public M toModel(Class modelClass, Object data) { - return modelClass.cast(Knockout.toModel(data)); - } - - @Override - public Object toJSON(InputStream is) throws IOException { - StringBuilder sb = new StringBuilder(); - InputStreamReader r = new InputStreamReader(is); - for (;;) { - int ch = r.read(); - if (ch == -1) { - break; - } - sb.append((char)ch); - } - return LoadJSON.parse(sb.toString()); - } - - @Override - public void runSafe(final Runnable r) { - LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!"); - r.run(); - } - - @Override - public LoadWS open(String url, JSONCall onReply) { - return new LoadWS(onReply, url); - } - - @Override - public void send(LoadWS socket, JSONCall data) { - socket.send(data); - } - - @Override - public void close(LoadWS socket) { - socket.close(); - } - - boolean areWebSocketsSupported() { - return Knockout.areWebSocketsSupported(); - } - - private static final class TrueFn extends Fn implements Fn.Presenter { - @Override - public Object invoke(Object thiz, Object... args) throws Exception { - return true; - } - - @Override - public Fn defineFn(String code, String... names) { - return this; - } - - @Override - public void displayPage(URL page, Runnable onPageLoad) { - } - - @Override - public void loadScript(Reader code) throws Exception { - } - } // end of TrueFn -} diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java Wed Dec 03 11:41:24 2014 +0100 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KO4J.java Thu Dec 04 09:21:55 2014 +0100 @@ -42,7 +42,9 @@ */ package org.netbeans.html.ko4j; +import java.util.logging.Logger; import net.java.html.json.Model; +import net.java.html.json.OnReceive; import org.netbeans.html.boot.spi.Fn; import org.netbeans.html.context.spi.Contexts; import org.netbeans.html.context.spi.Contexts.Provider; @@ -54,35 +56,49 @@ /** Support for knockout.js * and its Java binding via {@link Model model classes}. * Registers {@link Provider}, so {@link java.util.ServiceLoader} can find it. + * The provider registers following technologies: + *
    + *
  • ko4j - bindings for knockout.js + * and the classes generated by the {@link Model} annotation. + *
  • + *
  • xhr - XMLHttpRequest + * based implementation for REST calls + * (GET, PUT, POST, DELETE methods) + * for {@link OnReceive} annotation. + *
  • + *
  • websocket - + * native browser websockets + * based implementation for {@link OnReceive} annotation and its WebSocket + * subprotocol. + *
  • + *
* * @author Jaroslav Tulach * @since 0.7 */ @ServiceProvider(service = Provider.class) public final class KO4J implements Provider { - private final Fn.Presenter presenter; - private FXContext c; + static final Logger LOG = Logger.getLogger(KOSockets.class.getName()); + private KOTech ko4j; + private KOTransfer trans; + private KOSockets socks; public KO4J() { this(null); } - + + @Deprecated public KO4J(Fn.Presenter presenter) { - this.presenter = presenter; - } - - private FXContext getKO() { - if (c == null) { - c = new FXContext(presenter == null ? Fn.activePresenter() : presenter); - } - return c; } /** Return instance of the knockout.js for Java technology. * @return non-null instance */ public Technology knockout() { - return getKO(); + if (ko4j == null) { + ko4j = new KOTech(); + } + return ko4j; } /** Browser based implementation of transfer interface. Uses @@ -91,7 +107,10 @@ * @return non-null instance */ public Transfer transfer() { - return getKO(); + if (trans == null) { + trans = new KOTransfer(); + } + return trans; } /** Returns browser based implementation of websocket transfer. @@ -102,7 +121,13 @@ * WebSocket object in the browser */ public WSTransfer websockets() { - return getKO().areWebSocketsSupported() ? getKO() : null; + if (!KOSockets.areWebSocketsSupported()) { + return null; + } + if (socks == null) { + socks = new KOSockets(); + } + return socks; } /** Registers technologies at position 100: diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOSockets.java Thu Dec 04 09:21:55 2014 +0100 @@ -0,0 +1,82 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.ko4j; + +import net.java.html.js.JavaScriptBody; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.json.spi.JSONCall; +import org.netbeans.html.json.spi.WSTransfer; + +/** This is an implementation package - just + * include its JAR on classpath and use official {@link Context} API + * to access the functionality. + *

+ * + * @author Jaroslav Tulach + */ +@Contexts.Id("websocket") +final class KOSockets +implements WSTransfer { + KOSockets() { + } + + @Override + public LoadWS open(String url, JSONCall onReply) { + return new LoadWS(onReply, url); + } + + @Override + public void send(LoadWS socket, JSONCall data) { + socket.send(data); + } + + @Override + public void close(LoadWS socket) { + socket.close(); + } + + @JavaScriptBody(args = {}, body = "if (window['WebSocket']) return true; else return false;") + static final boolean areWebSocketsSupported() { + return false; + } +} diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTech.java Thu Dec 04 09:21:55 2014 +0100 @@ -0,0 +1,144 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.ko4j; + +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.json.spi.FunctionBinding; +import org.netbeans.html.json.spi.PropertyBinding; +import org.netbeans.html.json.spi.Technology; +import static org.netbeans.html.ko4j.KO4J.LOG; + +/** This is an implementation package - just + * include its JAR on classpath and use official {@link Context} API + * to access the functionality. + *

+ * + * @author Jaroslav Tulach + */ +@Contexts.Id("ko4j") +final class KOTech +implements Technology.BatchInit, Technology.ValueMutated { + private Object[] jsObjects; + private int jsIndex; + + public KOTech() { + } + + @Override + public Object wrapModel(Object model, PropertyBinding[] propArr, FunctionBinding[] funcArr) { + String[] propNames = new String[propArr.length]; + boolean[] propReadOnly = new boolean[propArr.length]; + Object[] propValues = new Object[propArr.length]; + for (int i = 0; i < propNames.length; i++) { + propNames[i] = propArr[i].getPropertyName(); + propReadOnly[i] = propArr[i].isReadOnly(); + propValues[i] = propArr[i].getValue(); + } + String[] funcNames = new String[funcArr.length]; + for (int i = 0; i < funcNames.length; i++) { + funcNames[i] = funcArr[i].getFunctionName(); + } + Object ret = getJSObject(); + Knockout.wrapModel(ret, model, + propNames, propReadOnly, propValues, propArr, + funcNames, funcArr + ); + return ret; + } + + private Object getJSObject() { + int len = 64; + if (jsObjects != null && jsIndex < (len = jsObjects.length)) { + return jsObjects[jsIndex++]; + } + jsObjects = Knockout.allocJS(len * 2); + jsIndex = 1; + return jsObjects[0]; + } + + @Override + public Object wrapModel(Object model) { + throw new UnsupportedOperationException(); + } + + @Override + public void bind(PropertyBinding b, Object model, Object data) { + throw new UnsupportedOperationException(); + } + + @Override + public void valueHasMutated(Object data, String propertyName) { + Knockout.valueHasMutated(data, propertyName, null, null); + } + + @Override + public void valueHasMutated(Object data, String propertyName, Object oldValue, Object newValue) { + Knockout.valueHasMutated(data, propertyName, oldValue, newValue); + } + + @Override + public void expose(FunctionBinding fb, Object model, Object d) { + throw new UnsupportedOperationException(); + } + + @Override + public void applyBindings(Object data) { + Knockout.applyBindings(data); + } + + @Override + public Object wrapArray(Object[] arr) { + return arr; + } + + @Override + public void runSafe(final Runnable r) { + LOG.warning("Technology.runSafe has been deprecated. Use BrwsrCtx.execute!"); + r.run(); + } + + @Override + public M toModel(Class modelClass, Object data) { + return modelClass.cast(Knockout.toModel(data)); + } +} diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/KOTransfer.java Thu Dec 04 09:21:55 2014 +0100 @@ -0,0 +1,104 @@ +/** + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2013-2014 Oracle and/or its affiliates. All rights reserved. + * + * Oracle and Java are registered trademarks of Oracle and/or its affiliates. + * Other names may be trademarks of their respective owners. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Oracle. Portions Copyright 2013-2014 Oracle. All Rights Reserved. + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + */ +package org.netbeans.html.ko4j; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import org.netbeans.html.context.spi.Contexts; +import org.netbeans.html.json.spi.JSONCall; +import org.netbeans.html.json.spi.Transfer; + +/** This is an implementation package - just + * include its JAR on classpath and use official {@link Context} API + * to access the functionality. + *

+ * + * @author Jaroslav Tulach + */ +@Contexts.Id("xhr") +final class KOTransfer +implements Transfer { + KOTransfer() { + } + + @Override + public void extract(Object obj, String[] props, Object[] values) { + LoadJSON.extractJSON(obj, props, values); + } + + @Override + public void loadJSON(final JSONCall call) { + if (call.isJSONP()) { + String me = LoadJSON.createJSONP(call); + LoadJSON.loadJSONP(call.composeURL(me), me); + } else { + String data = null; + if (call.isDoOutput()) { + try { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + call.writeData(bos); + data = new String(bos.toByteArray(), "UTF-8"); + } catch (IOException ex) { + call.notifyError(ex); + } + } + LoadJSON.loadJSON(call.composeURL(null), call, call.getMethod(), data); + } + } + + @Override + public Object toJSON(InputStream is) throws IOException { + StringBuilder sb = new StringBuilder(); + InputStreamReader r = new InputStreamReader(is); + for (;;) { + int ch = r.read(); + if (ch == -1) { + break; + } + sb.append((char)ch); + } + return LoadJSON.parse(sb.toString()); + } +} diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java --- a/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Wed Dec 03 11:41:24 2014 +0100 +++ b/ko4j/src/main/java/org/netbeans/html/ko4j/Knockout.java Thu Dec 04 09:21:55 2014 +0100 @@ -153,9 +153,4 @@ static Object toModel(Object wrapper) { return toModelImpl(wrapper); } - - @JavaScriptBody(args = {}, body = "if (window['WebSocket']) return true; else return false;") - static final boolean areWebSocketsSupported() { - return false; - } } diff -r 6e20d36f1a13 -r 88d62267a0b5 ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java --- a/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java Wed Dec 03 11:41:24 2014 +0100 +++ b/ko4j/src/test/java/org/netbeans/html/ko4j/KnockoutFXTest.java Thu Dec 04 09:21:55 2014 +0100 @@ -66,7 +66,6 @@ import org.netbeans.html.json.spi.WSTransfer; import org.netbeans.html.json.tck.KOTest; import org.netbeans.html.json.tck.KnockoutTCK; -import org.netbeans.html.boot.impl.FnContext; import org.openide.util.lookup.ServiceProvider; import org.testng.Assert; import static org.testng.Assert.*; @@ -154,12 +153,12 @@ @Override public BrwsrCtx createContext() { - FXContext fx = new FXContext(browserContext); + KO4J ko4j = new KO4J(); Contexts.Builder cb = Contexts.newBuilder(). - register(Technology.class, fx, 10). - register(Transfer.class, fx, 10); - if (fx.areWebSocketsSupported()) { - cb.register(WSTransfer.class, fx, 10); + register(Technology.class, ko4j.knockout(), 10). + register(Transfer.class, ko4j.transfer(), 10); + if (ko4j.websockets() != null) { + cb.register(WSTransfer.class, ko4j.websockets(), 10); } cb.register(Executor.class, (Executor)browserContext, 10); cb.register(Fn.Presenter.class, browserContext, 10); diff -r 6e20d36f1a13 -r 88d62267a0b5 src/main/javadoc/overview.html --- a/src/main/javadoc/overview.html Wed Dec 03 11:41:24 2014 +0100 +++ b/src/main/javadoc/overview.html Thu Dec 04 09:21:55 2014 +0100 @@ -75,6 +75,27 @@ yet the application code can be written in Java.

+

What's New in Version 1.1?

+ +

+ The content of a {@link net.java.html.BrwsrCtx context} + can be selected by registering implementations under specific + {@link org.netbeans.html.context.spi.Contexts.Id technology identifiers} + and requesting them during + {@link org.netbeans.html.context.spi.Contexts#newBuilder(java.lang.Object...) construction} + of the context. org.netbeans.html:ko4j module's implementation + offers ko4j, xhr and websocket identifiers + for its registered services + (e.g. {@link org.netbeans.html.json.spi.Technology}, + {@link org.netbeans.html.json.spi.Transfer} and + {@link org.netbeans.html.json.spi.WSTransfer}). + org.netbeans.html:ko-ws-tyrus + module registers its + {@link org.netbeans.html.json.spi.Transfer Java based JSON} and + {@link org.netbeans.html.json.spi.WSTransfer WebSocket} implementations + under the name tyrus. +

+

What's New in Version 1.0?