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 2017/09/03 12:48:37 UTC

[04/24] incubator-netbeans-html4j git commit: [INFRA-15006] Initial donation of HTML/Java NetBeans API. Equivalent to the content of html4j-donation-review.zip donated as part of ApacheNetBeansDonation1.zip with SHA256 being 7f2ca0f61953a190613c9a0fbcc1b

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/pom.xml
----------------------------------------------------------------------
diff --git a/ko-osgi-test/pom.xml b/ko-osgi-test/pom.xml
new file mode 100644
index 0000000..2af6772
--- /dev/null
+++ b/ko-osgi-test/pom.xml
@@ -0,0 +1,181 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 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-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.netbeans.html</groupId>
+        <artifactId>pom</artifactId>
+        <version>2.0-SNAPSHOT</version>
+    </parent>
+    <name>KO Tests in Equinox OSGi Container</name>
+    <artifactId>ko-osgi-test</artifactId>
+    <packaging>bundle</packaging>
+    <description>Runs the TCK for Knockout in Equinox OSGi Container</description>
+    <properties>
+        <netbeans.compile.on.save>none</netbeans.compile.on.save>
+    </properties>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-deploy-plugin</artifactId>
+                <configuration>
+                    <skip>true</skip>
+                </configuration>
+            </plugin>
+            <plugin>
+                <artifactId>maven-failsafe-plugin</artifactId>
+                <configuration>
+                    <additionalClasspathElements>
+                        <additionalClasspathElement>${project.build.directory}/${project.build.finalName}.jar</additionalClasspathElement>
+                    </additionalClasspathElements>
+                </configuration>
+                <executions>
+                    <execution>
+                        <goals>
+                            <goal>integration-test</goal>
+                            <goal>verify</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+    <dependencies>
+        <dependency>
+            <groupId>com.oracle</groupId>
+            <artifactId>javafx</artifactId>
+            <version>2.2</version>
+            <scope>system</scope>
+            <systemPath>${jfxrt.jar}</systemPath>
+        </dependency>
+        <dependency>
+            <groupId>de.twentyeleven.skysail</groupId>
+            <artifactId>org.json-osgi</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.json</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.testng</groupId>
+            <artifactId>testng</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.json.tck</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.api</groupId>
+            <artifactId>org-openide-util-lookup</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.netbeans.html</groupId>
+            <artifactId>net.java.html.boot</artifactId>
+            <version>${project.version}</version>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>ko4j</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>net.java.html.boot.fx</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-websockets-server</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+            <type>jar</type>
+        </dependency>
+        <dependency>
+            <groupId>org.glassfish.grizzly</groupId>
+            <artifactId>grizzly-http-servlet</artifactId>
+            <version>${grizzly.version}</version>
+            <scope>test</scope>
+        </dependency>    
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+            <scope>test</scope>
+            <version>3.1.0</version>
+        </dependency>
+        <dependency>
+            <groupId>org.eclipse</groupId>
+            <artifactId>org.eclipse.osgi</artifactId>
+            <version>3.8.0.v20120529-1548</version>
+        </dependency>
+        <dependency>
+            <groupId>${project.groupId}</groupId>
+            <artifactId>equinox-agentclass-hook</artifactId>
+            <version>${project.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
new file mode 100644
index 0000000..1ff2564
--- /dev/null
+++ b/ko-osgi-test/src/main/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxTCKImpl.java
@@ -0,0 +1,222 @@
+/**
+ * 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-2016 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.ko.osgi.test;
+
+import java.io.BufferedReader;
+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;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executor;
+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.spi.Fn;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.Technology;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.openide.util.lookup.ServiceProvider;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+@ServiceProvider(service = KnockoutTCK.class)
+public class KnockoutEquinoxTCKImpl extends KnockoutTCK implements Callable<Class[]> {
+    
+    private static Fn.Presenter browserContext;
+
+    public static Class loadOSGiClass(String name, BundleContext ctx) throws Exception {
+        for (Bundle b : ctx.getBundles()) {
+            try {
+                Class<?> osgiClass = b.loadClass(name);
+                if (osgiClass != null && osgiClass.getClassLoader() != ClassLoader.getSystemClassLoader()) {
+                    return osgiClass;
+                }
+            } catch (ClassNotFoundException cnfe) {
+                // go on
+            }
+        }
+        throw new IllegalStateException("Cannot load " + name + " from the OSGi container!");
+    }
+
+    @Override
+    public Class[] call() throws Exception {
+        return testClasses();
+    }
+    
+    public static void start(URI server) throws Exception {
+        final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(KnockoutEquinoxTCKImpl.class).
+            loadPage(server.toString()).
+            invoke("initialized");
+
+        Executors.newSingleThreadExecutor().submit(new Runnable() {
+            @Override
+            public void run() {
+                try {
+                    final ClassLoader osgiClassLoader = BrowserBuilder.class.getClassLoader();
+                    bb.classloader(osgiClassLoader);
+                    bb.showAndWait();
+                } catch (Throwable t) {
+                    t.printStackTrace();
+                }
+            }
+        });
+    }
+
+    public static void initialized() throws Exception {
+        Bundle bundle = FrameworkUtil.getBundle(KnockoutEquinoxTCKImpl.class);
+        if (bundle == null) {
+            throw new IllegalStateException(
+                "Should be loaded from a bundle. But was: " + KnockoutEquinoxTCKImpl.class.getClassLoader()
+            );
+        }
+        Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(
+            "org.netbeans.html.ko.osgi.test.KnockoutEquinoxIT"
+        );
+        Method m = classpathClass.getMethod("initialized", Class.class, Object.class);
+        browserContext = Fn.activePresenter();
+        m.invoke(null, KnockoutEquinoxTCKImpl.class, browserContext);
+    }
+    
+    @Override
+    public BrwsrCtx createContext() {
+        try {
+            Contexts.Builder cb = Contexts.newBuilder().
+                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);
+//        }
+            return cb.build();
+        } catch (Exception ex) {
+            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<String, Object> values) {
+        Object json = createObj();
+        for (Map.Entry<String, Object> entry : values.entrySet()) {
+            putObj(json, entry.getKey(), entry.getValue());
+        }
+        return json;
+    }
+
+    @JavaScriptBody(args = {}, body = "return {};")
+    private static native Object createObj();
+
+    @JavaScriptBody(args = {"obj", "prop", "val"}, body = "obj[prop] = val;")
+    private static native void putObj(Object obj, String prop, Object val);
+
+    @Override
+    @JavaScriptBody(args = { "s", "args" }, body = ""
+        + "var f = new Function(s); "
+        + "return f.apply(null, args);"
+    )
+    public native Object executeScript(String script, Object[] arguments);
+
+    @JavaScriptBody(args = {  }, body = 
+          "var h;"
+        + "if (!!window && !!window.location && !!window.location.href)\n"
+        + "  h = window.location.href;\n"
+        + "else "
+        + "  h = null;"
+        + "return h;\n"
+    )
+    private static native String findBaseURL();
+    
+    @Override
+    public URI prepareURL(String content, String mimeType, String[] parameters) {
+        try {
+            final URL baseURL = new URL(findBaseURL());
+            StringBuilder sb = new StringBuilder();
+            sb.append("/dynamic?mimeType=").append(mimeType);
+            for (int i = 0; i < parameters.length; i++) {
+                sb.append("&param" + i).append("=").append(parameters[i]);
+            }
+            String mangle = content.replace("\n", "%0a")
+                .replace("\"", "\\\"").replace(" ", "%20");
+            sb.append("&content=").append(mangle);
+
+            URL query = new URL(baseURL, sb.toString());
+            URLConnection c = query.openConnection();
+            BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
+            URI connectTo = new URI(br.readLine());
+            return connectTo;
+        } catch (IOException ex) {
+            throw new IllegalStateException(ex);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public boolean canFailWebSocketTest() {
+        return true;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
new file mode 100644
index 0000000..3c70570
--- /dev/null
+++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/DynamicHTTP.java
@@ -0,0 +1,261 @@
+/**
+ * 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-2016 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.ko.osgi.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class DynamicHTTP extends HttpHandler {
+    private static final Logger LOG = Logger.getLogger(DynamicHTTP.class.getName());
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private DynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final DynamicHTTP dh = new DynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = DynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, "Error processing message " + text, ex);
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java
new file mode 100644
index 0000000..686a930
--- /dev/null
+++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KOFx.java
@@ -0,0 +1,130 @@
+/**
+ * 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-2016 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.ko.osgi.test;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import javafx.application.Platform;
+import org.testng.ITest;
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public final class KOFx implements ITest, Runnable {
+    private final Object p;
+    private final Method m;
+    private Object result;
+    private Object inst;
+    private int count;
+
+    KOFx(Object p, Method m) {
+        this.p = p;
+        this.m = m;
+    }
+
+    @Override
+    public String getTestName() {
+        return m.getName();
+    }
+
+    @Test
+    public synchronized void executeTest() throws Exception {
+        if (result == null) {
+            Platform.runLater(this);
+            wait();
+        }
+        if (result instanceof Exception) {
+            throw (Exception)result;
+        }
+        if (result instanceof Error) {
+            throw (Error)result;
+        }
+    }
+
+    @Override
+    public synchronized void run() {
+        boolean notify = true;
+        Closeable a = null;
+        try {
+            a = KnockoutEquinoxIT.activateInOSGi(p);
+            if (inst == null) {
+                inst = m.getDeclaringClass().newInstance();
+            }
+            result = m.invoke(inst);
+            if (result == null) {
+                result = this;
+            }
+        } catch (InvocationTargetException ex) {
+            Throwable r = ex.getTargetException();
+            if (r instanceof InterruptedException) {
+                if (count++ < 10000) {
+                    notify = false;
+                    try {
+                        Thread.sleep(100);
+                    } catch (Exception ex1) {
+                        // ignore and continue
+                    }
+                    Platform.runLater(this);
+                    return;
+                }
+            }
+            result = r;
+        } catch (Exception ex) {
+            result = ex;
+        } finally {
+            if (notify) {
+                notifyAll();
+            }
+            try {
+                if (a != null) a.close();
+            } catch (IOException ex) {
+                throw new IllegalStateException(ex);
+            }
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java
new file mode 100644
index 0000000..6c57f82
--- /dev/null
+++ b/ko-osgi-test/src/test/java/org/netbeans/html/ko/osgi/test/KnockoutEquinoxIT.java
@@ -0,0 +1,245 @@
+/**
+ * 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-2016 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.ko.osgi.test;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.concurrent.Callable;
+import java.util.jar.JarFile;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.html.boot.spi.Fn;
+import org.netbeans.html.json.tck.KOTest;
+import org.netbeans.html.json.tck.KnockoutTCK;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.launch.FrameworkFactory;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.fail;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.Factory;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+public class KnockoutEquinoxIT {
+    private static final Logger LOG = Logger.getLogger(KnockoutEquinoxIT.class.getName());
+    private static Framework framework;
+    private static File dir;
+    static Framework framework() throws Exception {
+        if (framework != null) {
+            return framework;
+        }
+        for (FrameworkFactory ff : ServiceLoader.load(FrameworkFactory.class)) {
+            
+            String basedir = System.getProperty("basedir");
+            assertNotNull("basedir preperty provided", basedir);
+            File target = new File(basedir, "target");
+            dir = new File(target, "osgi");
+            dir.mkdirs();
+            
+            Map<String,String> config = new HashMap<String, String>();
+            config.put(Constants.FRAMEWORK_STORAGE, dir.getPath());
+            config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
+            config.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "sun.misc,"
+                + "javafx.application,"
+                + "javafx.beans,"
+                + "javafx.beans.property,"
+                + "javafx.beans.value,"
+                + "javafx.collections,"
+                + "javafx.concurrent,"
+                + "javafx.event,"
+                + "javafx.geometry,"
+                + "javafx.scene,"
+                + "javafx.scene.control,"
+                + "javafx.scene.image,"
+                + "javafx.scene.layout,"
+                + "javafx.scene.text,"
+                + "javafx.scene.web,"
+                + "javafx.stage,"
+                + "javafx.util,"
+                + "netscape.javascript"
+            );
+            config.put("osgi.hook.configurators.include", "org.netbeans.html.equinox.agentclass.AgentHook");
+            framework = ff.newFramework(config);
+            framework.init();
+            loadClassPathBundles(framework);
+            framework.start();
+            for (Bundle b : framework.getBundleContext().getBundles()) {
+                try {
+                    if (b.getSymbolicName().contains("equinox-agentclass-hook")) {
+                        continue;
+                    }
+                    if (b.getSymbolicName().contains("glassfish.grizzly")) {
+                        continue;
+                    }
+                    b.start();
+                    LOG.log(Level.INFO, "Started {0}", b.getSymbolicName());
+                } catch (BundleException ex) {
+                    throw new IllegalStateException("Cannot start bundle " + b.getSymbolicName(), ex);
+                }
+            }
+            return framework;
+        }
+        fail("No OSGi framework in the path");
+        return null;
+    }
+    
+    @AfterClass public static void cleanUp() throws Exception {
+        if (framework != null) framework.stop();
+        clearUpDir(dir);
+    }
+    private static void clearUpDir(File dir) {
+        if (dir.isDirectory()) {
+            for (File f : dir.listFiles()) {
+                clearUpDir(f);
+            }
+        }
+        dir.delete();
+    }
+    
+    
+
+    private static void loadClassPathBundles(Framework f) throws IOException, BundleException {
+        for (String jar : System.getProperty("java.class.path").split(File.pathSeparator)) {
+            File file = new File(jar);
+            if (!file.isFile()) {
+                LOG.info("Not loading " + file);
+                continue;
+            }
+            JarFile jf = new JarFile(file);
+            final String name = jf.getManifest().getMainAttributes().getValue("Bundle-SymbolicName");
+            jf.close();
+            if (name != null) {
+                if (name.contains("org.eclipse.osgi")) {
+                    continue;
+                }
+                if (name.contains("testng")) {
+                    continue;
+                }
+                final String path = "reference:" + file.toURI().toString();
+                try {
+                    Bundle b = f.getBundleContext().installBundle(path);
+                } catch (BundleException ex) {
+                    LOG.log(Level.WARNING, "Cannot install " + file, ex);
+                }
+            }
+        }
+    }
+    
+    private static Class<?> loadOSGiClass(Class<?> c) throws Exception {
+        return KnockoutEquinoxTCKImpl.loadOSGiClass(c.getName(), KnockoutEquinoxIT.framework().getBundleContext());
+    }
+    
+    private static Class<?> browserClass;
+    private static Object browserContext;
+    
+    @Factory public static Object[] compatibilityTests() throws Exception {
+        Class<?> tck = loadOSGiClass(KnockoutTCK.class);
+        Class<?> peer = loadOSGiClass(KnockoutEquinoxTCKImpl.class);
+        // initialize the TCK
+        Callable<Class[]> inst = (Callable<Class[]>) peer.newInstance();
+        
+        Class[] arr = inst.call();
+        for (int i = 0; i < arr.length; i++) {
+            if (arr[i].getClassLoader() == ClassLoader.getSystemClassLoader()) {
+                fail("Should be an OSGi class: " + arr[i]);
+            }
+        }
+        
+        URI uri = DynamicHTTP.initServer();
+
+        Method start = peer.getMethod("start", URI.class);
+        start.invoke(null, uri);
+        
+        ClassLoader l = getClassLoader();
+        List<Object> res = new ArrayList<Object>();
+        for (int i = 0; i < arr.length; i++) {
+            seekKOTests(arr[i], res);
+        }
+        return res.toArray();
+    }
+
+    private static void seekKOTests(Class<?> c, List<Object> res) throws SecurityException, ClassNotFoundException {
+        Class<? extends Annotation> koTest =
+            c.getClassLoader().loadClass(KOTest.class.getName()).
+            asSubclass(Annotation.class);
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(koTest) != null) {
+                res.add(new KOFx(browserContext, m));
+            }
+        }
+    }
+
+    static synchronized ClassLoader getClassLoader() throws InterruptedException {
+        while (browserClass == null) {
+            KnockoutEquinoxIT.class.wait();
+        }
+        return browserClass.getClassLoader();
+    }
+    
+    public static synchronized void initialized(Class<?> browserCls, Object presenter) throws Exception {
+        browserClass = browserCls;
+        browserContext = presenter;
+        KnockoutEquinoxIT.class.notifyAll();
+    }
+
+    static Closeable activateInOSGi(Object presenter) throws Exception {
+        Class<?> presenterClass = loadOSGiClass(Fn.Presenter.class);
+        Class<?> fnClass = loadOSGiClass(Fn.class);
+        Method m = fnClass.getMethod("activate", presenterClass);
+        return (Closeable) m.invoke(null, presenter);
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html
----------------------------------------------------------------------
diff --git a/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html b/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html
new file mode 100644
index 0000000..226c9f5
--- /dev/null
+++ b/ko-osgi-test/src/test/resources/org/netbeans/html/ko/osgi/test/test.html
@@ -0,0 +1,56 @@
+<!--
+
+    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-2016 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.
+
+-->
+<!DOCTYPE html>
+<html>
+    <head>
+        <title>Knockout.fx Execution Harness</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+        <meta name="viewport" content="width=device-width">
+    </head>
+    <body>
+        <h1>Knockout.fx in Equinox Execution Harness</h1>
+    </body>
+    <script></script>
+</html>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/pom.xml
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/pom.xml b/ko-ws-tyrus/pom.xml
new file mode 100644
index 0000000..9415d83
--- /dev/null
+++ b/ko-ws-tyrus/pom.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
+
+    Copyright 2013-2016 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-2016 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.
+
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.netbeans.html</groupId>
+    <artifactId>pom</artifactId>
+    <version>2.0-SNAPSHOT</version>
+  </parent>
+  <groupId>org.netbeans.html</groupId>
+  <artifactId>ko-ws-tyrus</artifactId>
+  <version>2.0-SNAPSHOT</version>
+  <packaging>bundle</packaging>
+  <name>Tyrus Based WebSockets</name>
+  <url>http://maven.apache.org</url>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.netbeans.html</groupId>
+                <artifactId>html4j-maven-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <skip>false</skip>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <properties>
+    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+    <bundleSymbolicName>org.netbeans.html.ko-ws-tyrus</bundleSymbolicName>
+  </properties>
+  <dependencies>
+    <!-- compile only deps -->
+    <dependency>
+      <groupId>org.netbeans.api</groupId>
+      <artifactId>org-openide-util-lookup</artifactId>
+      <type>jar</type>
+      <scope>provided</scope>
+    </dependency>
+
+    <!-- compile + runtime -->      
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json</artifactId>
+      <version>${project.version}</version>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>de.twentyeleven.skysail</groupId>
+        <artifactId>org.json-osgi</artifactId>
+    </dependency>
+    <dependency>
+      <artifactId>javax.websocket-api</artifactId>
+      <groupId>javax.websocket</groupId>
+      <type>jar</type>
+      <version>1.0</version>
+    </dependency>
+
+    <!-- tyrus runtime -->    
+    <dependency>
+        <groupId>org.glassfish.tyrus</groupId>
+        <artifactId>tyrus-client</artifactId>
+        <version>1.3.1</version>
+        <scope>runtime</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.tyrus</groupId>
+        <artifactId>tyrus-container-grizzly-client</artifactId>
+        <version>1.3.1</version>
+        <scope>runtime</scope>
+    </dependency>
+    
+    
+    
+    <!-- test only deps -->
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.boot</artifactId>
+      <version>${project.version}</version>
+      <scope>provided</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>net.java.html.json.tck</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>${project.groupId}</groupId>
+      <artifactId>ko4j</artifactId>
+      <version>${project.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server-core</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-websockets-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+      <type>jar</type>
+    </dependency>
+    <dependency>
+        <groupId>${project.groupId}</groupId>
+        <artifactId>net.java.html.boot.fx</artifactId>
+        <version>${project.version}</version>
+        <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.glassfish.grizzly</groupId>
+      <artifactId>grizzly-http-server</artifactId>
+      <version>${grizzly.version}</version>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+        <groupId>org.glassfish.grizzly</groupId>
+        <artifactId>grizzly-http-servlet</artifactId>
+        <version>${grizzly.version}</version>
+        <scope>test</scope>
+    </dependency>    
+    <dependency>
+        <groupId>javax.servlet</groupId>
+        <artifactId>javax.servlet-api</artifactId>
+        <scope>test</scope>
+        <version>3.1.0</version>
+    </dependency>
+  </dependencies>
+    <description>An implementation module that provides support for WebSocket protocol on JDK7. 
+No need to use it when running on JDK8 with FX WebView supporting WebSocket directly.</description>
+</project>

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
new file mode 100644
index 0000000..c91f6aa
--- /dev/null
+++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/LoadJSON.java
@@ -0,0 +1,301 @@
+/**
+ * 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-2016 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.wstyrus;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PushbackInputStream;
+import java.io.Reader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.logging.Logger;
+import net.java.html.js.JavaScriptBody;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.netbeans.html.json.spi.JSONCall;
+
+/** 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 LoadJSON implements Runnable {
+    private static final Logger LOG = Logger.getLogger(LoadJSON.class.getName());
+    private static final Executor REQ = Executors.newCachedThreadPool(new ThreadFactory() {
+        @Override
+        public Thread newThread(Runnable runnable) {
+            Thread thread = Executors.defaultThreadFactory().newThread(runnable);
+            thread.setDaemon(true);
+            return thread;
+        }
+    });
+
+    private final JSONCall call;
+    private final URL base;
+
+
+    private LoadJSON(JSONCall call) {
+        this.call = call;
+        this.base = null;
+    }
+
+    public static void loadJSON(JSONCall call) {
+        assert !"WebSocket".equals(call.getMethod());
+        REQ.execute(new LoadJSON((call)));
+    }
+
+    @Override
+    public void run() {
+        final String url;
+        Throwable error = null;
+        Object json = null;
+
+        if (call.isJSONP()) {
+            url = call.composeURL("dummy");
+        } else {
+            url = call.composeURL(null);
+        }
+        try {
+            final URL u = new URL(base, url.replace(" ", "%20"));
+            URLConnection conn = u.openConnection();
+            if (call.isDoOutput()) {
+                conn.setDoOutput(true);
+            }
+            String h = call.getHeaders();
+            if (h != null) {
+                int pos = 0;
+                while (pos < h.length()) {
+                    int tagEnd = h.indexOf(':', pos);
+                    if (tagEnd == -1) {
+                        break;
+                    }
+                    int r = h.indexOf('\r', tagEnd);
+                    int n = h.indexOf('\n', tagEnd);
+                    if (r == -1) {
+                        r = h.length();
+                    }
+                    if (n == -1) {
+                        n = h.length();
+                    }
+                    String key = h.substring(pos, tagEnd).trim();
+                    String val = h.substring(tagEnd + 1, Math.min(r, n)).trim();
+                    conn.setRequestProperty(key, val);;
+                    pos = Math.max(r, n);
+                }
+            }
+            if (call.getMethod() != null && conn instanceof HttpURLConnection) {
+                ((HttpURLConnection) conn).setRequestMethod(call.getMethod());
+            }
+            if (call.isDoOutput()) {
+                final OutputStream os = conn.getOutputStream();
+                call.writeData(os);
+                os.flush();
+            }
+            final PushbackInputStream is = new PushbackInputStream(
+                conn.getInputStream(), 1
+            );
+            boolean[] arrayOrString = { false, false };
+            detectJSONType(call.isJSONP(), is, arrayOrString);
+            try {
+                if (arrayOrString[1]) {
+                    throw new JSONException("");
+                }
+                JSONTokener tok = createTokener(is);
+                Object obj;
+                obj = arrayOrString[0] ? new JSONArray(tok) : new JSONObject(tok);
+                json = convertToArray(obj);
+            } catch (JSONException ex) {
+                Reader r = new InputStreamReader(is, "UTF-8");
+                StringBuilder sb = new StringBuilder();
+                for (;;) {
+                    int ch = r.read();
+                    if (ch == -1) {
+                        break;
+                    }
+                    sb.append((char)ch);
+                }
+                json = sb.toString();
+            }
+        } catch (IOException ex) {
+            error = ex;
+        } finally {
+            if (error != null) {
+                call.notifyError(error);
+            } else {
+                call.notifySuccess(json);
+            }
+        }
+    }
+
+    private static void detectJSONType(boolean skipAnything, final PushbackInputStream is, boolean[] arrayOrString) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                arrayOrString[1] = true;
+                break;
+            }
+            if (Character.isWhitespace(ch)) {
+                continue;
+            }
+
+            if (ch == '[') {
+                is.unread(ch);
+                arrayOrString[0] = true;
+                break;
+            }
+            if (ch == '{') {
+                is.unread(ch);
+                break;
+            }
+            if (!skipAnything) {
+                is.unread(ch);
+                arrayOrString[1] = true;
+                break;
+            }
+        }
+    }
+
+    private static JSONTokener createTokener(InputStream is) throws IOException {
+        Reader r = new InputStreamReader(is, "UTF-8");
+        try {
+            return new JSONTokener(r);
+        } catch (LinkageError ex) {
+            // phones may carry outdated version of JSONTokener
+            StringBuilder sb = new StringBuilder();
+            for (;;) {
+                int ch = r.read();
+                if (ch == -1) {
+                    break;
+                }
+                sb.append((char)ch);
+            }
+            return new JSONTokener(sb.toString());
+        }
+    }
+
+    static Object convertToArray(Object o) throws JSONException {
+        if (o instanceof JSONArray) {
+            JSONArray ja = (JSONArray)o;
+            Object[] arr = new Object[ja.length()];
+            for (int i = 0; i < arr.length; i++) {
+                arr[i] = convertToArray(ja.get(i));
+            }
+            return arr;
+        } else if (o instanceof JSONObject) {
+            JSONObject obj = (JSONObject)o;
+            Iterator it = obj.keys();
+            List<Object> collect = new ArrayList<Object>();
+            while (it.hasNext()) {
+                String key = (String)it.next();
+                final Object val = obj.get(key);
+                final Object newVal = convertToArray(val);
+                if (val != newVal) {
+                    collect.add(key);
+                    collect.add(newVal);
+                }
+            }
+            int size = collect.size();
+            for (int i = 0; i < size; i += 2) {
+                obj.put((String) collect.get(i), collect.get(i + 1));
+            }
+            return obj;
+        } else if (o == JSONObject.NULL) {
+            return null;
+        } else {
+            return o;
+        }
+    }
+
+    public static void extractJSON(Object jsonObject, String[] props, Object[] values) {
+        if (jsonObject instanceof JSONObject) {
+            JSONObject obj = (JSONObject)jsonObject;
+            for (int i = 0; i < props.length; i++) {
+                Object val = obj.opt(props[i]);
+                if (val == JSONObject.NULL) {
+                    val = null;
+                }
+                values[i] = val;
+            }
+            return;
+        }
+        for (int i = 0; i < props.length; i++) {
+            values[i] = getProperty(jsonObject, props[i]);
+        }
+    }
+
+    @JavaScriptBody(args = {"object", "property"}, body =
+        "var ret;\n" + 
+        "if (property === null) ret = object;\n" + 
+        "else if (object === null) ret = null;\n" + 
+        "else ret = object[property];\n" + 
+        "return ret ? (typeof ko === 'undefined' ? ret : ko.utils.unwrapObservable(ret)) : null;"
+    )
+    private static Object getProperty(Object object, String property) {
+        return null;
+    }
+
+    public static Object parse(InputStream is) throws IOException {
+        try {
+            PushbackInputStream push = new PushbackInputStream(is, 1);
+            boolean[] arrayOrString = { false, false };
+            detectJSONType(false, push, arrayOrString);
+            JSONTokener t = createTokener(push);
+            Object obj = arrayOrString[0] ? new JSONArray(t) : new JSONObject(t);
+            return convertToArray(obj);
+        } catch (JSONException ex) {
+            throw new IOException(ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
new file mode 100644
index 0000000..498da94
--- /dev/null
+++ b/ko-ws-tyrus/src/main/java/org/netbeans/html/wstyrus/TyrusContext.java
@@ -0,0 +1,209 @@
+/**
+ * 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-2016 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.wstyrus;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Iterator;
+import javax.websocket.ClientEndpoint;
+import javax.websocket.ContainerProvider;
+import javax.websocket.OnClose;
+import javax.websocket.OnError;
+import javax.websocket.OnMessage;
+import javax.websocket.OnOpen;
+import javax.websocket.Session;
+import javax.websocket.WebSocketContainer;
+import net.java.html.json.OnReceive;
+import org.netbeans.html.context.spi.Contexts;
+import org.netbeans.html.json.spi.JSONCall;
+import org.netbeans.html.json.spi.Transfer;
+import org.netbeans.html.json.spi.WSTransfer;
+import org.netbeans.html.wstyrus.TyrusContext.Comm;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+import org.openide.util.lookup.ServiceProvider;
+
+/** This is an implementation module that provides support for
+ * WebSocket protocol for {@link OnReceive} communication end point for
+ * JDK7.
+ * <p>
+ * Don't deal with this module directly, rather use the 
+ * {@link OnReceive @OnReceive(url="ws://...", ...)} API to establish your
+ * WebSocket connection.
+ * <p>
+ * There is no need to include this module in your application if you are
+ * running on JDK8. JDK8 WebView provides its own implementation of the
+ * WebSocket API based on WebSocket object inside a browser. This is included
+ * in the <code>org.netbeans.html:ko4j:1.0</code> module.
+ *
+ * @author Jaroslav Tulach
+ */
+@Contexts.Id("tyrus")
+@ServiceProvider(service = Contexts.Provider.class)
+public final class TyrusContext 
+implements Contexts.Provider, WSTransfer<Comm>, Transfer {
+    @Override
+    public void fillContext(Contexts.Builder context, Class<?> requestor) {
+        // default WebSocket transfer implementation is registered
+        // in ko-fx module with 100, provide this one as a fallback only
+        context.register(WSTransfer.class, this, 1000);
+        context.register(Transfer.class, this, 1000);
+    }
+
+    @Override
+    public Comm open(String url, JSONCall callback) {
+        try {
+            return new Comm(new URI(url), callback);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    @Override
+    public void send(Comm socket, JSONCall data) {
+        socket.session.getAsyncRemote().sendText(data.getMessage());
+    }
+
+    @Override
+    public void close(Comm socket) {
+        try {
+            final Session s = socket.session;
+            if (s != null) {
+                s.close();
+            }
+        } catch (IOException ex) {
+            socket.callback.notifyError(ex);
+        }
+    }
+
+    @Override
+    public void extract(Object obj, String[] props, Object[] values) {
+        LoadJSON.extractJSON(obj, props, values);
+    }
+
+    @Override
+    public Object toJSON(InputStream is) throws IOException {
+        return LoadJSON.parse(is);
+    }
+
+    @Override
+    public void loadJSON(JSONCall call) {
+        LoadJSON.loadJSON(call);
+    }
+    
+    /** Implementation class in an implementation. Represents a {@link ClientEndpoint} of the
+     * WebSocket channel. You are unlikely to get on hold of it.
+     */
+    @ClientEndpoint
+    public static final class Comm {
+        private final JSONCall callback;
+        private Session session;
+
+        Comm(final URI url, JSONCall callback) {
+            this.callback = callback;
+            try {
+                final WebSocketContainer c = ContainerProvider.getWebSocketContainer();
+                c.connectToServer(Comm.this, url);
+            } catch (Exception ex) {
+                wasAnError(ex);
+            }
+        }
+
+        @OnOpen
+        public synchronized void open(Session s) {
+            this.session = s;
+            callback.notifySuccess(null);
+        }
+
+        @OnClose
+        public void close() {
+            this.session = null;
+            callback.notifyError(null);
+        }
+
+        @OnMessage
+        public void message(final String orig, Session s) {
+            Object json;
+            String data = orig.trim();
+            try {
+                JSONTokener tok = new JSONTokener(data);
+                Object obj = data.startsWith("[") ? new JSONArray(tok) : new JSONObject(tok);
+                json = convertToArray(obj);
+            } catch (JSONException ex) {
+                json = data;
+            }
+            callback.notifySuccess(json);
+        }
+
+        @OnError
+        public void wasAnError(Throwable t) {
+            callback.notifyError(t);
+        }
+
+        static Object convertToArray(Object o) throws JSONException {
+            if (o instanceof JSONArray) {
+                JSONArray ja = (JSONArray) o;
+                Object[] arr = new Object[ja.length()];
+                for (int i = 0; i < arr.length; i++) {
+                    arr[i] = convertToArray(ja.get(i));
+                }
+                return arr;
+            } else if (o instanceof JSONObject) {
+                JSONObject obj = (JSONObject) o;
+                Iterator it = obj.keys();
+                while (it.hasNext()) {
+                    String key = (String) it.next();
+                    obj.put(key, convertToArray(obj.get(key)));
+                }
+                return obj;
+            } else {
+                return o;
+            }
+        }
+        
+    } // end of Comm
+}

http://git-wip-us.apache.org/repos/asf/incubator-netbeans-html4j/blob/226089a5/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
----------------------------------------------------------------------
diff --git a/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
new file mode 100644
index 0000000..96b41cd
--- /dev/null
+++ b/ko-ws-tyrus/src/test/java/org/netbeans/html/wstyrus/TyrusDynamicHTTP.java
@@ -0,0 +1,262 @@
+/**
+ * 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-2016 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.wstyrus;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.glassfish.grizzly.PortRange;
+import org.glassfish.grizzly.http.server.HttpHandler;
+import org.glassfish.grizzly.http.server.HttpServer;
+import org.glassfish.grizzly.http.server.NetworkListener;
+import org.glassfish.grizzly.http.server.Request;
+import org.glassfish.grizzly.http.server.Response;
+import org.glassfish.grizzly.http.server.ServerConfiguration;
+import org.glassfish.grizzly.websockets.WebSocket;
+import org.glassfish.grizzly.websockets.WebSocketAddOn;
+import org.glassfish.grizzly.websockets.WebSocketApplication;
+import org.glassfish.grizzly.websockets.WebSocketEngine;
+
+/**
+ *
+ * @author Jaroslav Tulach
+ */
+final class TyrusDynamicHTTP extends HttpHandler {
+    private static int resourcesCount;
+    private static List<Resource> resources;
+    private static ServerConfiguration conf;
+    private static HttpServer server;
+
+    private TyrusDynamicHTTP() {
+    }
+
+    static URI initServer() throws Exception {
+        server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
+        final WebSocketAddOn addon = new WebSocketAddOn();
+        for (NetworkListener listener : server.getListeners()) {
+            listener.registerAddOn(addon);
+        }
+        resources = new ArrayList<Resource>();
+
+        conf = server.getServerConfiguration();
+        final TyrusDynamicHTTP dh = new TyrusDynamicHTTP();
+
+        conf.addHttpHandler(dh, "/");
+
+        server.start();
+
+        return pageURL("http", server, "/test.html");
+    }
+
+    @Override
+    public void service(Request request, Response response) throws Exception {
+        if ("/test.html".equals(request.getRequestURI())) {
+            response.setContentType("text/html");
+            final InputStream is = TyrusDynamicHTTP.class.getResourceAsStream("test.html");
+            copyStream(is, response.getOutputStream(), null);
+            return;
+        }
+        if ("/dynamic".equals(request.getRequestURI())) {
+            String mimeType = request.getParameter("mimeType");
+            List<String> params = new ArrayList<String>();
+            boolean webSocket = false;
+            for (int i = 0;; i++) {
+                String p = request.getParameter("param" + i);
+                if (p == null) {
+                    break;
+                }
+                if ("protocol:ws".equals(p)) {
+                    webSocket = true;
+                    continue;
+                }
+                params.add(p);
+            }
+            final String cnt = request.getParameter("content");
+            String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
+            ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
+            URI url;
+            final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
+            if (webSocket) {
+                url = registerWebSocket(res);
+            } else {
+                url = registerResource(res);
+            }
+            response.getWriter().write(url.toString());
+            response.getWriter().write("\n");
+            return;
+        }
+
+        for (Resource r : resources) {
+            if (r.httpPath.equals(request.getRequestURI())) {
+                response.setContentType(r.httpType);
+                r.httpContent.reset();
+                String[] params = null;
+                if (r.parameters.length != 0) {
+                    params = new String[r.parameters.length];
+                    for (int i = 0; i < r.parameters.length; i++) {
+                        params[i] = request.getParameter(r.parameters[i]);
+                        if (params[i] == null) {
+                            if ("http.method".equals(r.parameters[i])) {
+                                params[i] = request.getMethod().toString();
+                            } else if ("http.requestBody".equals(r.parameters[i])) {
+                                Reader rdr = request.getReader();
+                                StringBuilder sb = new StringBuilder();
+                                for (;;) {
+                                    int ch = rdr.read();
+                                    if (ch == -1) {
+                                        break;
+                                    }
+                                    sb.append((char) ch);
+                                }
+                                params[i] = sb.toString();
+                            } else if (r.parameters[i].startsWith("http.header.")) {
+                                params[i] = request.getHeader(r.parameters[i].substring(12));
+                            }
+                        }
+                        if (params[i] == null) {
+                            params[i] = "null";
+                        }
+                    }
+                }
+
+                copyStream(r.httpContent, response.getOutputStream(), null, params);
+            }
+        }
+    }
+
+    private URI registerWebSocket(Resource r) {
+        WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
+        return pageURL("ws", server, r.httpPath);
+    }
+
+    private URI registerResource(Resource r) {
+        if (!resources.contains(r)) {
+            resources.add(r);
+            conf.addHttpHandler(this, r.httpPath);
+        }
+        return pageURL("http", server, r.httpPath);
+    }
+
+    private static URI pageURL(String proto, HttpServer server, final String page) {
+        NetworkListener listener = server.getListeners().iterator().next();
+        int port = listener.getPort();
+        try {
+            return new URI(proto + "://localhost:" + port + page);
+        } catch (URISyntaxException ex) {
+            throw new IllegalStateException(ex);
+        }
+    }
+
+    static final class Resource {
+
+        final InputStream httpContent;
+        final String httpType;
+        final String httpPath;
+        final String[] parameters;
+
+        Resource(InputStream httpContent, String httpType, String httpPath,
+            String[] parameters) {
+            httpContent.mark(Integer.MAX_VALUE);
+            this.httpContent = httpContent;
+            this.httpType = httpType;
+            this.httpPath = httpPath;
+            this.parameters = parameters;
+        }
+    }
+
+    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
+        for (;;) {
+            int ch = is.read();
+            if (ch == -1) {
+                break;
+            }
+            if (ch == '$' && params.length > 0) {
+                int cnt = is.read() - '0';
+                if (baseURL != null && cnt == 'U' - '0') {
+                    os.write(baseURL.getBytes("UTF-8"));
+                } else {
+                    if (cnt >= 0 && cnt < params.length) {
+                        os.write(params[cnt].getBytes("UTF-8"));
+                    } else {
+                        os.write('$');
+                        os.write(cnt + '0');
+                    }
+                }
+            } else {
+                os.write(ch);
+            }
+        }
+    }
+
+    private static class WS extends WebSocketApplication {
+        private final Resource r;
+
+        private WS(Resource r) {
+            this.r = r;
+        }
+
+        @Override
+        public void onMessage(WebSocket socket, String text) {
+            try {
+                r.httpContent.reset();
+                ByteArrayOutputStream out = new ByteArrayOutputStream();
+                copyStream(r.httpContent, out, null, text);
+                String s = new String(out.toByteArray(), "UTF-8");
+                socket.send(s);
+            } catch (IOException ex) {
+                LOG.log(Level.WARNING, null, ex);
+            }
+        }
+        private static final Logger LOG = Logger.getLogger(WS.class.getName());
+
+    }
+}