1.1 --- a/json-tck/src/main/java/net/java/html/json/tests/Utils.java Sun Aug 25 12:35:32 2013 +0200
1.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/Utils.java Sun Aug 25 14:40:16 2013 +0200
1.3 @@ -21,7 +21,6 @@
1.4 package net.java.html.json.tests;
1.5
1.6 import java.net.URI;
1.7 -import java.net.URL;
1.8 import java.util.Map;
1.9 import java.util.ServiceLoader;
1.10 import net.java.html.BrwsrCtx;
1.11 @@ -85,6 +84,16 @@
1.12 }
1.13 throw new IllegalStateException();
1.14 }
1.15 +
1.16 + static boolean canFailWebSockets(
1.17 + Class<?> clazz) {
1.18 + for (KnockoutTCK tck : ServiceLoader.load(KnockoutTCK.class, cl(clazz))) {
1.19 + if (tck.canFailWebSocketTest()) {
1.20 + return true;
1.21 + }
1.22 + }
1.23 + return false;
1.24 + }
1.25
1.26 private static ClassLoader cl(Class<?> c) {
1.27 try {
2.1 --- a/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java Sun Aug 25 12:35:32 2013 +0200
2.2 +++ b/json-tck/src/main/java/net/java/html/json/tests/WebSocketTest.java Sun Aug 25 14:40:16 2013 +0200
2.3 @@ -140,7 +140,8 @@
2.4 if (js.getFetchedResponse() == null) {
2.5 return false;
2.6 }
2.7 - return js.getFetchedResponse().contains("UnsupportedOperationException");
2.8 + return js.getFetchedResponse().contains("UnsupportedOperationException") &&
2.9 + Utils.canFailWebSockets(WebSocketTest.class);
2.10 }
2.11
2.12 }
3.1 --- a/json-tck/src/main/java/org/apidesign/html/json/tck/KnockoutTCK.java Sun Aug 25 12:35:32 2013 +0200
3.2 +++ b/json-tck/src/main/java/org/apidesign/html/json/tck/KnockoutTCK.java Sun Aug 25 14:40:16 2013 +0200
3.3 @@ -97,5 +97,14 @@
3.4 };
3.5 }
3.6
3.7 + /** Some implementations cannot fully support web sockets and fail.
3.8 + *
3.9 + * @return true, if UnsupportedOperationException reported from a web
3.10 + * socket open operation is acceptable reply
3.11 + */
3.12 + public boolean canFailWebSocketTest() {
3.13 + return false;
3.14 + }
3.15 +
3.16
3.17 }
4.1 --- a/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java Sun Aug 25 12:35:32 2013 +0200
4.2 +++ b/ko-fx/src/test/java/org/apidesign/html/kofx/KnockoutFXTest.java Sun Aug 25 14:40:16 2013 +0200
4.3 @@ -182,4 +182,15 @@
4.4 throw new IllegalStateException(ex);
4.5 }
4.6 }
4.7 +
4.8 + @Override
4.9 + public boolean canFailWebSocketTest() {
4.10 + try {
4.11 + Class.forName("java.util.function.Function");
4.12 + return false;
4.13 + } catch (ClassNotFoundException ex) {
4.14 + // running on JDK7, FX WebView WebSocket impl does not work
4.15 + return true;
4.16 + }
4.17 + }
4.18 }
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/ko-ws-tyrus/pom.xml Sun Aug 25 14:40:16 2013 +0200
5.3 @@ -0,0 +1,143 @@
5.4 +<?xml version="1.0"?>
5.5 +<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
5.6 + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
5.7 + <modelVersion>4.0.0</modelVersion>
5.8 + <parent>
5.9 + <groupId>org.apidesign</groupId>
5.10 + <artifactId>html</artifactId>
5.11 + <version>0.5-SNAPSHOT</version>
5.12 + </parent>
5.13 + <groupId>org.apidesign.html</groupId>
5.14 + <artifactId>ko-ws-tyrus</artifactId>
5.15 + <version>0.5-SNAPSHOT</version>
5.16 + <name>Tyrus Based WebSockets</name>
5.17 + <url>http://maven.apache.org</url>
5.18 + <build>
5.19 + <plugins>
5.20 + <plugin>
5.21 + <groupId>org.apache.maven.plugins</groupId>
5.22 + <artifactId>maven-compiler-plugin</artifactId>
5.23 + <version>2.3.2</version>
5.24 + <configuration>
5.25 + <source>1.7</source>
5.26 + <target>1.7</target>
5.27 + </configuration>
5.28 + </plugin>
5.29 + </plugins>
5.30 + </build>
5.31 + <properties>
5.32 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
5.33 + </properties>
5.34 + <dependencies>
5.35 + <!-- compile only deps -->
5.36 + <dependency>
5.37 + <groupId>org.netbeans.api</groupId>
5.38 + <artifactId>org-openide-util-lookup</artifactId>
5.39 + <type>jar</type>
5.40 + <scope>provided</scope>
5.41 + </dependency>
5.42 +
5.43 + <!-- compile + runtime -->
5.44 + <dependency>
5.45 + <groupId>${project.groupId}</groupId>
5.46 + <artifactId>net.java.html</artifactId>
5.47 + <version>${project.version}</version>
5.48 + <type>jar</type>
5.49 + </dependency>
5.50 + <dependency>
5.51 + <groupId>${project.groupId}</groupId>
5.52 + <artifactId>net.java.html.json</artifactId>
5.53 + <version>${project.version}</version>
5.54 + <type>jar</type>
5.55 + </dependency>
5.56 + <dependency>
5.57 + <groupId>org.json</groupId>
5.58 + <artifactId>json</artifactId>
5.59 + <version>20090211</version>
5.60 + <type>jar</type>
5.61 + </dependency>
5.62 + <dependency>
5.63 + <artifactId>javax.websocket-api</artifactId>
5.64 + <groupId>javax.websocket</groupId>
5.65 + <type>jar</type>
5.66 + <version>1.0</version>
5.67 + </dependency>
5.68 +
5.69 + <!-- tyrus runtime -->
5.70 + <dependency>
5.71 + <groupId>org.glassfish.tyrus</groupId>
5.72 + <artifactId>tyrus-client</artifactId>
5.73 + <version>1.2.1</version>
5.74 + <scope>runtime</scope>
5.75 + </dependency>
5.76 + <dependency>
5.77 + <groupId>org.glassfish.tyrus</groupId>
5.78 + <artifactId>tyrus-container-grizzly</artifactId>
5.79 + <version>1.2.1</version>
5.80 + <scope>runtime</scope>
5.81 + </dependency>
5.82 +
5.83 +
5.84 +
5.85 + <!-- test only deps -->
5.86 + <dependency>
5.87 + <groupId>${project.groupId}</groupId>
5.88 + <artifactId>net.java.html.boot</artifactId>
5.89 + <version>${project.version}</version>
5.90 + <scope>test</scope>
5.91 + <type>jar</type>
5.92 + </dependency>
5.93 + <dependency>
5.94 + <groupId>${project.groupId}</groupId>
5.95 + <artifactId>net.java.html.json.tck</artifactId>
5.96 + <version>${project.version}</version>
5.97 + <scope>test</scope>
5.98 + <type>jar</type>
5.99 + </dependency>
5.100 + <dependency>
5.101 + <groupId>${project.groupId}</groupId>
5.102 + <artifactId>ko-fx</artifactId>
5.103 + <version>${project.version}</version>
5.104 + <scope>test</scope>
5.105 + <type>jar</type>
5.106 + </dependency>
5.107 + <dependency>
5.108 + <groupId>org.glassfish.grizzly</groupId>
5.109 + <artifactId>grizzly-http-server-core</artifactId>
5.110 + <version>2.3.3</version>
5.111 + <scope>test</scope>
5.112 + <type>jar</type>
5.113 + </dependency>
5.114 + <dependency>
5.115 + <groupId>org.glassfish.grizzly</groupId>
5.116 + <artifactId>grizzly-websockets-server</artifactId>
5.117 + <version>2.3.3</version>
5.118 + <scope>test</scope>
5.119 + <type>jar</type>
5.120 + </dependency>
5.121 + <dependency>
5.122 + <groupId>${project.groupId}</groupId>
5.123 + <artifactId>boot-fx</artifactId>
5.124 + <version>${project.version}</version>
5.125 + <scope>test</scope>
5.126 + </dependency>
5.127 + <dependency>
5.128 + <groupId>org.glassfish.grizzly</groupId>
5.129 + <artifactId>grizzly-http-server</artifactId>
5.130 + <version>2.3.3</version>
5.131 + <scope>test</scope>
5.132 + </dependency>
5.133 + <dependency>
5.134 + <groupId>org.glassfish.grizzly</groupId>
5.135 + <artifactId>grizzly-http-servlet</artifactId>
5.136 + <version>2.3.3</version>
5.137 + <scope>test</scope>
5.138 + </dependency>
5.139 + <dependency>
5.140 + <groupId>javax.servlet</groupId>
5.141 + <artifactId>javax.servlet-api</artifactId>
5.142 + <scope>test</scope>
5.143 + <version>3.1.0</version>
5.144 + </dependency>
5.145 + </dependencies>
5.146 +</project>
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/ko-ws-tyrus/src/main/java/org/apidesign/html/wstyrus/TyrusContext.java Sun Aug 25 14:40:16 2013 +0200
6.3 @@ -0,0 +1,159 @@
6.4 +/**
6.5 + * HTML via Java(tm) Language Bindings
6.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.7 + *
6.8 + * This program is free software: you can redistribute it and/or modify
6.9 + * it under the terms of the GNU General Public License as published by
6.10 + * the Free Software Foundation, version 2 of the License.
6.11 + *
6.12 + * This program is distributed in the hope that it will be useful,
6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.15 + * GNU General Public License for more details. apidesign.org
6.16 + * designates this particular file as subject to the
6.17 + * "Classpath" exception as provided by apidesign.org
6.18 + * in the License file that accompanied this code.
6.19 + *
6.20 + * You should have received a copy of the GNU General Public License
6.21 + * along with this program. Look for COPYING file in the top folder.
6.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
6.23 + */
6.24 +package org.apidesign.html.wstyrus;
6.25 +
6.26 +import java.io.IOException;
6.27 +import java.net.URI;
6.28 +import java.net.URISyntaxException;
6.29 +import java.util.Iterator;
6.30 +import javafx.application.Platform;
6.31 +import javax.websocket.ClientEndpoint;
6.32 +import javax.websocket.ContainerProvider;
6.33 +import javax.websocket.DeploymentException;
6.34 +import javax.websocket.OnClose;
6.35 +import javax.websocket.OnError;
6.36 +import javax.websocket.OnMessage;
6.37 +import javax.websocket.OnOpen;
6.38 +import javax.websocket.Session;
6.39 +import javax.websocket.WebSocketContainer;
6.40 +import org.apidesign.html.context.spi.Contexts;
6.41 +import org.apidesign.html.json.spi.JSONCall;
6.42 +import org.apidesign.html.json.spi.WSTransfer;
6.43 +import org.apidesign.html.wstyrus.TyrusContext.Comm;
6.44 +import org.json.JSONArray;
6.45 +import org.json.JSONException;
6.46 +import org.json.JSONObject;
6.47 +import org.json.JSONTokener;
6.48 +import org.openide.util.lookup.ServiceProvider;
6.49 +
6.50 +/**
6.51 + *
6.52 + * @author Jaroslav Tulach <jtulach@netbeans.org>
6.53 + */
6.54 +@ServiceProvider(service = Contexts.Provider.class)
6.55 +public final class TyrusContext implements Contexts.Provider, WSTransfer<Comm> {
6.56 + @Override
6.57 + public void fillContext(Contexts.Builder context, Class<?> requestor) {
6.58 + // default WebSocket transfer implementation is registered
6.59 + // in ko-fx module with 100, provide this one as a fallback only
6.60 + context.register(WSTransfer.class, this, 1000);
6.61 + }
6.62 +
6.63 + @Override
6.64 + public Comm open(String url, JSONCall callback) {
6.65 + try {
6.66 + return new Comm(new URI(url), callback);
6.67 + } catch (URISyntaxException ex) {
6.68 + throw new IllegalStateException(ex);
6.69 + }
6.70 + }
6.71 +
6.72 + @Override
6.73 + public void send(Comm socket, JSONCall data) {
6.74 + socket.session.getAsyncRemote().sendText(data.getMessage());
6.75 + }
6.76 +
6.77 + @Override
6.78 + public void close(Comm socket) {
6.79 + try {
6.80 + socket.session.close();
6.81 + } catch (IOException ex) {
6.82 + socket.callback.notifyError(ex);
6.83 + }
6.84 + }
6.85 +
6.86 + @ClientEndpoint
6.87 + public static final class Comm {
6.88 + private final JSONCall callback;
6.89 + private Session session;
6.90 +
6.91 + Comm(final URI url, JSONCall callback) {
6.92 + this.callback = callback;
6.93 + try {
6.94 + final WebSocketContainer c = ContainerProvider.getWebSocketContainer();
6.95 + c.connectToServer(Comm.this, url);
6.96 + } catch (DeploymentException | IOException ex) {
6.97 + wasAnError(ex);
6.98 + }
6.99 + }
6.100 +
6.101 + @OnOpen
6.102 + public synchronized void open(Session s) {
6.103 + this.session = s;
6.104 + callback.notifySuccess(null);
6.105 + }
6.106 +
6.107 + @OnClose
6.108 + public void close() {
6.109 + this.session = null;
6.110 + callback.notifyError(null);
6.111 + }
6.112 +
6.113 + @OnMessage
6.114 + public void message(final String orig, Session s) {
6.115 + class R implements Runnable {
6.116 + Object json;
6.117 + public R() {
6.118 + String data = orig.trim();
6.119 + try {
6.120 + JSONTokener tok = new JSONTokener(data);
6.121 + Object obj = data.startsWith("[") ? new JSONArray(tok) : new JSONObject(tok);
6.122 + json = convertToArray(obj);
6.123 + } catch (JSONException ex) {
6.124 + json = data;
6.125 + }
6.126 + }
6.127 + @Override
6.128 + public void run() {
6.129 + callback.notifySuccess(json);
6.130 + }
6.131 + }
6.132 + Platform.runLater(new R());
6.133 + }
6.134 +
6.135 + @OnError
6.136 + public void wasAnError(Throwable t) {
6.137 + callback.notifyError(t);
6.138 + }
6.139 +
6.140 + static Object convertToArray(Object o) throws JSONException {
6.141 + if (o instanceof JSONArray) {
6.142 + JSONArray ja = (JSONArray) o;
6.143 + Object[] arr = new Object[ja.length()];
6.144 + for (int i = 0; i < arr.length; i++) {
6.145 + arr[i] = convertToArray(ja.get(i));
6.146 + }
6.147 + return arr;
6.148 + } else if (o instanceof JSONObject) {
6.149 + JSONObject obj = (JSONObject) o;
6.150 + Iterator it = obj.keys();
6.151 + while (it.hasNext()) {
6.152 + String key = (String) it.next();
6.153 + obj.put(key, convertToArray(obj.get(key)));
6.154 + }
6.155 + return obj;
6.156 + } else {
6.157 + return o;
6.158 + }
6.159 + }
6.160 +
6.161 + } // end of Comm
6.162 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusDynamicHTTP.java Sun Aug 25 14:40:16 2013 +0200
7.3 @@ -0,0 +1,238 @@
7.4 +/**
7.5 + * HTML via Java(tm) Language Bindings
7.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
7.7 + *
7.8 + * This program is free software: you can redistribute it and/or modify
7.9 + * it under the terms of the GNU General Public License as published by
7.10 + * the Free Software Foundation, version 2 of the License.
7.11 + *
7.12 + * This program is distributed in the hope that it will be useful,
7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.15 + * GNU General Public License for more details. apidesign.org
7.16 + * designates this particular file as subject to the
7.17 + * "Classpath" exception as provided by apidesign.org
7.18 + * in the License file that accompanied this code.
7.19 + *
7.20 + * You should have received a copy of the GNU General Public License
7.21 + * along with this program. Look for COPYING file in the top folder.
7.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
7.23 + */
7.24 +package org.apidesign.html.wstyrus;
7.25 +
7.26 +import java.io.ByteArrayInputStream;
7.27 +import java.io.ByteArrayOutputStream;
7.28 +import java.io.IOException;
7.29 +import java.io.InputStream;
7.30 +import java.io.OutputStream;
7.31 +import java.io.Reader;
7.32 +import java.net.URI;
7.33 +import java.net.URISyntaxException;
7.34 +import java.util.ArrayList;
7.35 +import java.util.List;
7.36 +import java.util.logging.Level;
7.37 +import java.util.logging.Logger;
7.38 +import org.glassfish.grizzly.PortRange;
7.39 +import org.glassfish.grizzly.http.server.HttpHandler;
7.40 +import org.glassfish.grizzly.http.server.HttpServer;
7.41 +import org.glassfish.grizzly.http.server.NetworkListener;
7.42 +import org.glassfish.grizzly.http.server.Request;
7.43 +import org.glassfish.grizzly.http.server.Response;
7.44 +import org.glassfish.grizzly.http.server.ServerConfiguration;
7.45 +import org.glassfish.grizzly.websockets.WebSocket;
7.46 +import org.glassfish.grizzly.websockets.WebSocketAddOn;
7.47 +import org.glassfish.grizzly.websockets.WebSocketApplication;
7.48 +import org.glassfish.grizzly.websockets.WebSocketEngine;
7.49 +
7.50 +/**
7.51 + *
7.52 + * @author Jaroslav Tulach <jtulach@netbeans.org>
7.53 + */
7.54 +final class TyrusDynamicHTTP extends HttpHandler {
7.55 + private static int resourcesCount;
7.56 + private static List<Resource> resources;
7.57 + private static ServerConfiguration conf;
7.58 + private static HttpServer server;
7.59 +
7.60 + private TyrusDynamicHTTP() {
7.61 + }
7.62 +
7.63 + static URI initServer() throws Exception {
7.64 + server = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
7.65 + final WebSocketAddOn addon = new WebSocketAddOn();
7.66 + for (NetworkListener listener : server.getListeners()) {
7.67 + listener.registerAddOn(addon);
7.68 + }
7.69 + resources = new ArrayList<Resource>();
7.70 +
7.71 + conf = server.getServerConfiguration();
7.72 + final TyrusDynamicHTTP dh = new TyrusDynamicHTTP();
7.73 +
7.74 + conf.addHttpHandler(dh, "/");
7.75 +
7.76 + server.start();
7.77 +
7.78 + return pageURL("http", server, "/test.html");
7.79 + }
7.80 +
7.81 + @Override
7.82 + public void service(Request request, Response response) throws Exception {
7.83 + if ("/test.html".equals(request.getRequestURI())) {
7.84 + response.setContentType("text/html");
7.85 + final InputStream is = TyrusDynamicHTTP.class.getResourceAsStream("test.html");
7.86 + copyStream(is, response.getOutputStream(), null);
7.87 + return;
7.88 + }
7.89 + if ("/dynamic".equals(request.getRequestURI())) {
7.90 + String mimeType = request.getParameter("mimeType");
7.91 + List<String> params = new ArrayList<String>();
7.92 + boolean webSocket = false;
7.93 + for (int i = 0;; i++) {
7.94 + String p = request.getParameter("param" + i);
7.95 + if (p == null) {
7.96 + break;
7.97 + }
7.98 + if ("protocol:ws".equals(p)) {
7.99 + webSocket = true;
7.100 + continue;
7.101 + }
7.102 + params.add(p);
7.103 + }
7.104 + final String cnt = request.getParameter("content");
7.105 + String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
7.106 + ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
7.107 + URI url;
7.108 + final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
7.109 + if (webSocket) {
7.110 + url = registerWebSocket(res);
7.111 + } else {
7.112 + url = registerResource(res);
7.113 + }
7.114 + response.getWriter().write(url.toString());
7.115 + response.getWriter().write("\n");
7.116 + return;
7.117 + }
7.118 +
7.119 + for (Resource r : resources) {
7.120 + if (r.httpPath.equals(request.getRequestURI())) {
7.121 + response.setContentType(r.httpType);
7.122 + r.httpContent.reset();
7.123 + String[] params = null;
7.124 + if (r.parameters.length != 0) {
7.125 + params = new String[r.parameters.length];
7.126 + for (int i = 0; i < r.parameters.length; i++) {
7.127 + params[i] = request.getParameter(r.parameters[i]);
7.128 + if (params[i] == null) {
7.129 + if ("http.method".equals(r.parameters[i])) {
7.130 + params[i] = request.getMethod().toString();
7.131 + } else if ("http.requestBody".equals(r.parameters[i])) {
7.132 + Reader rdr = request.getReader();
7.133 + StringBuilder sb = new StringBuilder();
7.134 + for (;;) {
7.135 + int ch = rdr.read();
7.136 + if (ch == -1) {
7.137 + break;
7.138 + }
7.139 + sb.append((char) ch);
7.140 + }
7.141 + params[i] = sb.toString();
7.142 + }
7.143 + }
7.144 + if (params[i] == null) {
7.145 + params[i] = "null";
7.146 + }
7.147 + }
7.148 + }
7.149 +
7.150 + copyStream(r.httpContent, response.getOutputStream(), null, params);
7.151 + }
7.152 + }
7.153 + }
7.154 +
7.155 + private URI registerWebSocket(Resource r) {
7.156 + WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
7.157 + return pageURL("ws", server, r.httpPath);
7.158 + }
7.159 +
7.160 + private URI registerResource(Resource r) {
7.161 + if (!resources.contains(r)) {
7.162 + resources.add(r);
7.163 + conf.addHttpHandler(this, r.httpPath);
7.164 + }
7.165 + return pageURL("http", server, r.httpPath);
7.166 + }
7.167 +
7.168 + private static URI pageURL(String proto, HttpServer server, final String page) {
7.169 + NetworkListener listener = server.getListeners().iterator().next();
7.170 + int port = listener.getPort();
7.171 + try {
7.172 + return new URI(proto + "://localhost:" + port + page);
7.173 + } catch (URISyntaxException ex) {
7.174 + throw new IllegalStateException(ex);
7.175 + }
7.176 + }
7.177 +
7.178 + static final class Resource {
7.179 +
7.180 + final InputStream httpContent;
7.181 + final String httpType;
7.182 + final String httpPath;
7.183 + final String[] parameters;
7.184 +
7.185 + Resource(InputStream httpContent, String httpType, String httpPath,
7.186 + String[] parameters) {
7.187 + httpContent.mark(Integer.MAX_VALUE);
7.188 + this.httpContent = httpContent;
7.189 + this.httpType = httpType;
7.190 + this.httpPath = httpPath;
7.191 + this.parameters = parameters;
7.192 + }
7.193 + }
7.194 +
7.195 + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
7.196 + for (;;) {
7.197 + int ch = is.read();
7.198 + if (ch == -1) {
7.199 + break;
7.200 + }
7.201 + if (ch == '$' && params.length > 0) {
7.202 + int cnt = is.read() - '0';
7.203 + if (baseURL != null && cnt == 'U' - '0') {
7.204 + os.write(baseURL.getBytes("UTF-8"));
7.205 + } else {
7.206 + if (cnt >= 0 && cnt < params.length) {
7.207 + os.write(params[cnt].getBytes("UTF-8"));
7.208 + } else {
7.209 + os.write('$');
7.210 + os.write(cnt + '0');
7.211 + }
7.212 + }
7.213 + } else {
7.214 + os.write(ch);
7.215 + }
7.216 + }
7.217 + }
7.218 +
7.219 + private static class WS extends WebSocketApplication {
7.220 + private final Resource r;
7.221 +
7.222 + private WS(Resource r) {
7.223 + this.r = r;
7.224 + }
7.225 +
7.226 + @Override
7.227 + public void onMessage(WebSocket socket, String text) {
7.228 + try {
7.229 + r.httpContent.reset();
7.230 + ByteArrayOutputStream out = new ByteArrayOutputStream();
7.231 + copyStream(r.httpContent, out, null, text);
7.232 + String s = new String(out.toByteArray(), "UTF-8");
7.233 + socket.send(s);
7.234 + } catch (IOException ex) {
7.235 + LOG.log(Level.WARNING, null, ex);
7.236 + }
7.237 + }
7.238 + private static final Logger LOG = Logger.getLogger(WS.class.getName());
7.239 +
7.240 + }
7.241 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusFX.java Sun Aug 25 14:40:16 2013 +0200
8.3 @@ -0,0 +1,92 @@
8.4 +/**
8.5 + * HTML via Java(tm) Language Bindings
8.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
8.7 + *
8.8 + * This program is free software: you can redistribute it and/or modify
8.9 + * it under the terms of the GNU General Public License as published by
8.10 + * the Free Software Foundation, version 2 of the License.
8.11 + *
8.12 + * This program is distributed in the hope that it will be useful,
8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.15 + * GNU General Public License for more details. apidesign.org
8.16 + * designates this particular file as subject to the
8.17 + * "Classpath" exception as provided by apidesign.org
8.18 + * in the License file that accompanied this code.
8.19 + *
8.20 + * You should have received a copy of the GNU General Public License
8.21 + * along with this program. Look for COPYING file in the top folder.
8.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
8.23 + */
8.24 +package org.apidesign.html.wstyrus;
8.25 +
8.26 +import java.lang.reflect.InvocationTargetException;
8.27 +import java.lang.reflect.Method;
8.28 +import javafx.application.Platform;
8.29 +import org.testng.ITest;
8.30 +import org.testng.annotations.Test;
8.31 +
8.32 +/**
8.33 + *
8.34 + * @author Jaroslav Tulach <jtulach@netbeans.org>
8.35 + */
8.36 +public final class TyrusFX implements ITest, Runnable {
8.37 + private final Method m;
8.38 + private Object result;
8.39 + private Object inst;
8.40 + private int count;
8.41 +
8.42 + TyrusFX(Method m) {
8.43 + this.m = m;
8.44 + }
8.45 +
8.46 + @Override
8.47 + public String getTestName() {
8.48 + return m.getName();
8.49 + }
8.50 +
8.51 + @Test
8.52 + public synchronized void executeTest() throws Exception {
8.53 + if (result == null) {
8.54 + Platform.runLater(this);
8.55 + wait();
8.56 + }
8.57 + if (result instanceof Exception) {
8.58 + throw (Exception)result;
8.59 + }
8.60 + if (result instanceof Error) {
8.61 + throw (Error)result;
8.62 + }
8.63 + }
8.64 +
8.65 + @Override
8.66 + public synchronized void run() {
8.67 + boolean notify = true;
8.68 + try {
8.69 + if (inst == null) {
8.70 + inst = m.getDeclaringClass().newInstance();
8.71 + }
8.72 + result = m.invoke(inst);
8.73 + if (result == null) {
8.74 + result = this;
8.75 + }
8.76 + } catch (InvocationTargetException ex) {
8.77 + Throwable r = ex.getTargetException();
8.78 + if (r instanceof InterruptedException) {
8.79 + if (count++ < 10000) {
8.80 + notify = false;
8.81 + Platform.runLater(this);
8.82 + return;
8.83 + }
8.84 + }
8.85 + result = r;
8.86 + } catch (Exception ex) {
8.87 + result = ex;
8.88 + } finally {
8.89 + if (notify) {
8.90 + notifyAll();
8.91 + }
8.92 + }
8.93 + }
8.94 +
8.95 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/ko-ws-tyrus/src/test/java/org/apidesign/html/wstyrus/TyrusKnockoutTest.java Sun Aug 25 14:40:16 2013 +0200
9.3 @@ -0,0 +1,185 @@
9.4 +/**
9.5 + * HTML via Java(tm) Language Bindings
9.6 + * Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
9.7 + *
9.8 + * This program is free software: you can redistribute it and/or modify
9.9 + * it under the terms of the GNU General Public License as published by
9.10 + * the Free Software Foundation, version 2 of the License.
9.11 + *
9.12 + * This program is distributed in the hope that it will be useful,
9.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
9.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9.15 + * GNU General Public License for more details. apidesign.org
9.16 + * designates this particular file as subject to the
9.17 + * "Classpath" exception as provided by apidesign.org
9.18 + * in the License file that accompanied this code.
9.19 + *
9.20 + * You should have received a copy of the GNU General Public License
9.21 + * along with this program. Look for COPYING file in the top folder.
9.22 + * If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
9.23 + */
9.24 +package org.apidesign.html.wstyrus;
9.25 +
9.26 +import java.io.BufferedReader;
9.27 +import java.io.IOException;
9.28 +import java.io.InputStreamReader;
9.29 +import java.lang.annotation.Annotation;
9.30 +import java.lang.reflect.Method;
9.31 +import java.net.URI;
9.32 +import java.net.URISyntaxException;
9.33 +import java.net.URL;
9.34 +import java.net.URLConnection;
9.35 +import java.util.ArrayList;
9.36 +import java.util.List;
9.37 +import java.util.Map;
9.38 +import java.util.concurrent.Executors;
9.39 +import net.java.html.BrwsrCtx;
9.40 +import net.java.html.boot.BrowserBuilder;
9.41 +import net.java.html.js.JavaScriptBody;
9.42 +import org.apidesign.html.context.spi.Contexts;
9.43 +import org.apidesign.html.json.spi.Technology;
9.44 +import org.apidesign.html.json.spi.Transfer;
9.45 +import org.apidesign.html.json.spi.WSTransfer;
9.46 +import org.apidesign.html.json.tck.KOTest;
9.47 +import org.apidesign.html.json.tck.KnockoutTCK;
9.48 +import org.apidesign.html.kofx.FXContext;
9.49 +import org.json.JSONException;
9.50 +import org.json.JSONObject;
9.51 +import org.openide.util.lookup.ServiceProvider;
9.52 +import org.testng.annotations.Factory;
9.53 +import static org.testng.Assert.*;
9.54 +
9.55 +/**
9.56 + *
9.57 + * @author Jaroslav Tulach <jtulach@netbeans.org>
9.58 + */
9.59 +@ServiceProvider(service = KnockoutTCK.class)
9.60 +public final class TyrusKnockoutTest extends KnockoutTCK {
9.61 + private static Class<?> browserClass;
9.62 +
9.63 + public TyrusKnockoutTest() {
9.64 + }
9.65 +
9.66 + @Factory public static Object[] compatibilityTests() throws Exception {
9.67 + Class[] arr = testClasses();
9.68 + for (int i = 0; i < arr.length; i++) {
9.69 + assertEquals(
9.70 + arr[i].getClassLoader(),
9.71 + TyrusKnockoutTest.class.getClassLoader(),
9.72 + "All classes loaded by the same classloader"
9.73 + );
9.74 + }
9.75 +
9.76 + URI uri = TyrusDynamicHTTP.initServer();
9.77 +
9.78 + final BrowserBuilder bb = BrowserBuilder.newBrowser().loadClass(TyrusKnockoutTest.class).
9.79 + loadPage(uri.toString()).
9.80 + invoke("initialized");
9.81 +
9.82 + Executors.newSingleThreadExecutor().submit(new Runnable() {
9.83 + @Override
9.84 + public void run() {
9.85 + bb.showAndWait();
9.86 + }
9.87 + });
9.88 +
9.89 + ClassLoader l = getClassLoader();
9.90 + List<Object> res = new ArrayList<Object>();
9.91 + for (int i = 0; i < arr.length; i++) {
9.92 + Class<?> c = Class.forName(arr[i].getName(), true, l);
9.93 + Class<? extends Annotation> koTest =
9.94 + c.getClassLoader().loadClass(KOTest.class.getName()).
9.95 + asSubclass(Annotation.class);
9.96 + for (Method m : c.getMethods()) {
9.97 + if (m.getAnnotation(koTest) != null) {
9.98 + res.add(new TyrusFX(m));
9.99 + }
9.100 + }
9.101 + }
9.102 + return res.toArray();
9.103 + }
9.104 +
9.105 + static synchronized ClassLoader getClassLoader() throws InterruptedException {
9.106 + while (browserClass == null) {
9.107 + TyrusKnockoutTest.class.wait();
9.108 + }
9.109 + return browserClass.getClassLoader();
9.110 + }
9.111 +
9.112 + public static synchronized void initialized(Class<?> browserCls) throws Exception {
9.113 + browserClass = browserCls;
9.114 + TyrusKnockoutTest.class.notifyAll();
9.115 + }
9.116 +
9.117 + public static void initialized() throws Exception {
9.118 + Class<?> classpathClass = ClassLoader.getSystemClassLoader().loadClass(TyrusKnockoutTest.class.getName());
9.119 + Method m = classpathClass.getMethod("initialized", Class.class);
9.120 + m.invoke(null, TyrusKnockoutTest.class);
9.121 + }
9.122 +
9.123 + @Override
9.124 + public BrwsrCtx createContext() {
9.125 + FXContext fx = new FXContext();
9.126 + TyrusContext tc = new TyrusContext();
9.127 + Contexts.Builder cb = Contexts.newBuilder().
9.128 + register(Technology.class, fx, 10).
9.129 + register(Transfer.class, fx, 10).
9.130 + register(WSTransfer.class, tc, 10);
9.131 + return cb.build();
9.132 + }
9.133 +
9.134 + @Override
9.135 + public Object createJSON(Map<String, Object> values) {
9.136 + JSONObject json = new JSONObject();
9.137 + for (Map.Entry<String, Object> entry : values.entrySet()) {
9.138 + try {
9.139 + json.put(entry.getKey(), entry.getValue());
9.140 + } catch (JSONException ex) {
9.141 + throw new IllegalStateException(ex);
9.142 + }
9.143 + }
9.144 + return json;
9.145 + }
9.146 +
9.147 + @Override
9.148 + @JavaScriptBody(args = { "s", "args" }, body = ""
9.149 + + "var f = new Function(s); "
9.150 + + "return f.apply(null, args);"
9.151 + )
9.152 + public native Object executeScript(String script, Object[] arguments);
9.153 +
9.154 + @JavaScriptBody(args = { }, body =
9.155 + "var h;"
9.156 + + "if (!!window && !!window.location && !!window.location.href)\n"
9.157 + + " h = window.location.href;\n"
9.158 + + "else "
9.159 + + " h = null;"
9.160 + + "return h;\n"
9.161 + )
9.162 + private static native String findBaseURL();
9.163 +
9.164 + @Override
9.165 + public URI prepareURL(String content, String mimeType, String[] parameters) {
9.166 + try {
9.167 + final URL baseURL = new URL(findBaseURL());
9.168 + StringBuilder sb = new StringBuilder();
9.169 + sb.append("/dynamic?mimeType=").append(mimeType);
9.170 + for (int i = 0; i < parameters.length; i++) {
9.171 + sb.append("¶m" + i).append("=").append(parameters[i]);
9.172 + }
9.173 + String mangle = content.replace("\n", "%0a")
9.174 + .replace("\"", "\\\"").replace(" ", "%20");
9.175 + sb.append("&content=").append(mangle);
9.176 +
9.177 + URL query = new URL(baseURL, sb.toString());
9.178 + URLConnection c = query.openConnection();
9.179 + BufferedReader br = new BufferedReader(new InputStreamReader(c.getInputStream()));
9.180 + URI connectTo = new URI(br.readLine());
9.181 + return connectTo;
9.182 + } catch (IOException ex) {
9.183 + throw new IllegalStateException(ex);
9.184 + } catch (URISyntaxException ex) {
9.185 + throw new IllegalStateException(ex);
9.186 + }
9.187 + }
9.188 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/ko-ws-tyrus/src/test/resources/org/apidesign/html/wstyrus/test.html Sun Aug 25 14:40:16 2013 +0200
10.3 @@ -0,0 +1,34 @@
10.4 +<!--
10.5 +
10.6 + HTML via Java(tm) Language Bindings
10.7 + Copyright (C) 2013 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
10.8 +
10.9 + This program is free software: you can redistribute it and/or modify
10.10 + it under the terms of the GNU General Public License as published by
10.11 + the Free Software Foundation, version 2 of the License.
10.12 +
10.13 + This program is distributed in the hope that it will be useful,
10.14 + but WITHOUT ANY WARRANTY; without even the implied warranty of
10.15 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10.16 + GNU General Public License for more details. apidesign.org
10.17 + designates this particular file as subject to the
10.18 + "Classpath" exception as provided by apidesign.org
10.19 + in the License file that accompanied this code.
10.20 +
10.21 + You should have received a copy of the GNU General Public License
10.22 + along with this program. Look for COPYING file in the top folder.
10.23 + If not, see http://wiki.apidesign.org/wiki/GPLwithClassPathException
10.24 +
10.25 +-->
10.26 +<!DOCTYPE html>
10.27 +<html>
10.28 + <head>
10.29 + <title>Tyrus WebSockets Execution Harness</title>
10.30 + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
10.31 + <meta name="viewport" content="width=device-width">
10.32 + </head>
10.33 + <body>
10.34 + <h1>Tyrus WebSockets Execution Harness</h1>
10.35 + </body>
10.36 + <script></script>
10.37 +</html>
11.1 --- a/pom.xml Sun Aug 25 12:35:32 2013 +0200
11.2 +++ b/pom.xml Sun Aug 25 14:40:16 2013 +0200
11.3 @@ -27,6 +27,7 @@
11.4 <module>boot</module>
11.5 <module>boot-fx</module>
11.6 <module>geo</module>
11.7 + <module>ko-ws-tyrus</module>
11.8 </modules>
11.9 <licenses>
11.10 <license>