Rather than piggybacking on first alert call, use the fact that the server and FX Web View are in the same VM and notify the view that bck2brwsr.js is about to be served from the server.
2 * Back 2 Browser Bytecode Translator
3 * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 2 of the License.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. Look for COPYING file in the top folder.
16 * If not, see http://opensource.org/licenses/GPL-2.0.
18 package org.apidesign.bck2brwsr.launcher.impl;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.UnsupportedEncodingException;
23 import java.lang.reflect.InvocationTargetException;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Modifier;
27 import java.util.Enumeration;
28 import org.apidesign.bck2brwsr.core.JavaScriptBody;
32 * @author Jaroslav Tulach <jtulach@netbeans.org>
34 public class Console {
38 turnAssetionStatusOn();
41 @JavaScriptBody(args = {"id", "attr"}, body =
42 "return window.document.getElementById(id)[attr].toString();")
43 private static native Object getAttr(String id, String attr);
44 @JavaScriptBody(args = {"elem", "attr"}, body =
45 "return elem[attr].toString();")
46 private static native Object getAttr(Object elem, String attr);
48 @JavaScriptBody(args = {"id", "attr", "value"}, body =
49 "window.document.getElementById(id)[attr] = value;")
50 private static native void setAttr(String id, String attr, Object value);
51 @JavaScriptBody(args = {"elem", "attr", "value"}, body =
52 "elem[attr] = value;")
53 private static native void setAttr(Object id, String attr, Object value);
55 @JavaScriptBody(args = {}, body = "return; window.close();")
56 private static native void closeWindow();
58 private static Object textArea;
59 private static Object statusArea;
61 private static void log(String newText) {
62 if (textArea == null) {
65 String attr = "value";
66 setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText);
67 setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight"));
70 private static void beginTest(Case c) {
71 Object[] arr = new Object[2];
72 beginTest(c.getClassName() + "." + c.getMethodName(), c, arr);
77 private static void finishTest(Case c, Object res) {
78 if ("null".equals(res)) {
79 setAttr(statusArea, "innerHTML", "Success");
81 setAttr(statusArea, "innerHTML", "Result " + res);
87 @JavaScriptBody(args = { "test", "c", "arr" }, body =
88 "var ul = window.document.getElementById('bck2brwsr.result');\n"
89 + "var li = window.document.createElement('li');\n"
90 + "var span = window.document.createElement('span');"
91 + "span.innerHTML = test + ' - ';\n"
92 + "var details = window.document.createElement('a');\n"
93 + "details.innerHTML = 'Details';\n"
94 + "details.href = '#';\n"
95 + "var p = window.document.createElement('p');\n"
96 + "var status = window.document.createElement('a');\n"
97 + "status.innerHTML = 'running';"
98 + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n"
99 + "status.onclick = function() { c.again__V_3Ljava_lang_Object_2(arr); }\n"
100 + "var pre = window.document.createElement('textarea');\n"
103 + "li.appendChild(span);\n"
104 + "li.appendChild(status);\n"
105 + "var span = window.document.createElement('span');"
106 + "span.innerHTML = ' ';\n"
107 + "li.appendChild(span);\n"
108 + "li.appendChild(details);\n"
109 + "p.appendChild(pre);\n"
110 + "ul.appendChild(li);\n"
112 + "arr[1] = status;\n"
114 private static native void beginTest(String test, Case c, Object[] arr);
116 @JavaScriptBody(args = { "url", "callback", "arr" }, body = ""
117 + "var request = new XMLHttpRequest();\n"
118 + "request.open('GET', url, true);\n"
119 + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
120 + "request.onreadystatechange = function() {\n"
121 + " if (this.readyState!==4) return;\n"
122 + " arr[0] = this.responseText;\n"
123 + " callback.run__V();\n"
127 private static native void loadText(String url, Runnable callback, String[] arr) throws IOException;
129 public static void harness(String url) throws IOException {
130 log("Connecting to " + url);
131 Request r = new Request(url);
134 private static class Request implements Runnable {
135 private final String[] arr = { null };
136 private final String url;
140 private Request(String url) throws IOException {
142 loadText(url, this, arr);
144 private Request(String url, String u) throws IOException {
146 loadText(u, this, arr);
153 String data = arr[0];
156 log("Some error exiting");
161 if (data.isEmpty()) {
162 log("No data, exiting");
167 c = Case.parseData(data);
169 log("Got \"" + data + "\"");
171 log("Processing \"" + arr[0] + "\" for " + retries + " time");
173 Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest();
174 finishTest(c, result);
176 String u = url + "?request=" + c.getRequestId() + "&result=" + result;
178 } catch (Exception ex) {
179 if (ex instanceof InterruptedException) {
180 log("Re-scheduling in 100ms");
184 log(ex.getClass().getName() + ":" + ex.getMessage());
189 private static String encodeURL(String r) throws UnsupportedEncodingException {
190 final String SPECIAL = "%$&+,/:;=?@";
191 StringBuilder sb = new StringBuilder();
192 byte[] utf8 = r.getBytes("UTF-8");
193 for (int i = 0; i < utf8.length; i++) {
194 int ch = utf8[i] & 0xff;
195 if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) {
196 final String numbers = "0" + Integer.toHexString(ch);
197 sb.append("%").append(numbers.substring(numbers.length() - 2));
206 return sb.toString();
209 static String invoke(String clazz, String method) throws
210 ClassNotFoundException, InvocationTargetException, IllegalAccessException,
211 InstantiationException, InterruptedException {
212 final Object r = new Case(null).invokeMethod(clazz, method);
213 return r == null ? "null" : r.toString().toString();
216 /** Helper method that inspects the classpath and loads given resource
217 * (usually a class file). Used while running tests in Rhino.
219 * @param name resource name to find
220 * @return the array of bytes in the given resource
221 * @throws IOException I/O in case something goes wrong
223 public static byte[] read(String name) throws IOException {
225 Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
226 while (en.hasMoreElements()) {
227 u = en.nextElement();
230 throw new IOException("Can't find " + name);
232 try (InputStream is = u.openStream()) {
234 arr = new byte[is.available()];
236 while (offset < arr.length) {
237 int len = is.read(arr, offset, arr.length - offset);
239 throw new IOException("Can't read " + name);
247 @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;")
248 private static void turnAssetionStatusOn() {
251 @JavaScriptBody(args = {"r", "time"}, body =
252 "return window.setTimeout(function() { r.run__V(); }, time);")
253 private static native Object schedule(Runnable r, int time);
255 private static final class Case {
256 private final Object data;
259 private Case(Object data) {
263 public static Case parseData(String s) {
264 return new Case(toJSON(s));
267 public String getMethodName() {
268 return value("methodName", data);
271 public String getClassName() {
272 return value("className", data);
275 public String getRequestId() {
276 return value("request", data);
279 public String getHtmlFragment() {
280 return value("html", data);
283 void again(Object[] arr) {
287 setAttr(textArea, "value", "");
289 } catch (Exception ex) {
290 log(ex.getClass().getName() + ":" + ex.getMessage());
294 private Object runTest() throws IllegalAccessException,
295 IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException,
296 InvocationTargetException, InstantiationException, InterruptedException {
297 if (this.getHtmlFragment() != null) {
298 setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment());
300 log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId());
301 Object result = invokeMethod(this.getClassName(), this.getMethodName());
302 setAttr("bck2brwsr.fragment", "innerHTML", "");
303 log("Result: " + result);
304 result = encodeURL("" + result);
305 log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
309 private Object invokeMethod(String clazz, String method)
310 throws ClassNotFoundException, InvocationTargetException,
311 InterruptedException, IllegalAccessException, IllegalArgumentException,
312 InstantiationException {
314 Class<?> c = Class.forName(clazz);
315 for (Method m : c.getMethods()) {
316 if (m.getName().equals(method)) {
323 if ((found.getModifiers() & Modifier.STATIC) != 0) {
324 res = found.invoke(null);
327 inst = c.newInstance();
329 res = found.invoke(inst);
331 } catch (Throwable ex) {
332 if (ex instanceof InvocationTargetException) {
333 ex = ((InvocationTargetException) ex).getTargetException();
335 if (ex instanceof InterruptedException) {
336 throw (InterruptedException)ex;
338 res = ex.getClass().getName() + ":" + ex.getMessage();
341 res = "Can't find method " + method + " in " + clazz;
346 @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');")
347 private static native Object toJSON(String s);
349 @JavaScriptBody(args = {"p", "d"}, body =
351 + "if (typeof v === 'undefined') return null;\n"
352 + "return v.toString();"
354 private static native String value(String p, Object d);