# HG changeset patch # User Jaroslav Tulach # Date 1359647907 -3600 # Node ID a07253cf2ca4da85eb873865dedccd355fd862a6 # Parent 6daa39b1756390f3c2e8441fce2d85bd4c53ec90 Better (more extensible and documented) API for the Launcher diff -r 6daa39b17563 -r a07253cf2ca4 launcher/pom.xml --- a/launcher/pom.xml Thu Jan 31 16:13:04 2013 +0100 +++ b/launcher/pom.xml Thu Jan 31 16:58:27 2013 +0100 @@ -23,6 +23,14 @@ 1.7 + + org.apache.maven.plugins + maven-javadoc-plugin + 2.8.1 + + org.apidesign.bck2brwsr.launcher.impl + + diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Thu Jan 31 16:13:04 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java Thu Jan 31 16:58:27 2013 +0100 @@ -55,9 +55,9 @@ */ final class Bck2BrwsrLauncher extends Launcher implements Closeable { private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName()); - private static final MethodInvocation END = new MethodInvocation(null, null, null); - private Set loaders = new LinkedHashSet<>(); - private BlockingQueue methods = new LinkedBlockingQueue<>(); + private static final InvocationContext END = new InvocationContext(null, null, null); + private final Set loaders = new LinkedHashSet<>(); + private final BlockingQueue methods = new LinkedBlockingQueue<>(); private long timeOut; private final Res resources = new Res(); private final String cmd; @@ -70,9 +70,8 @@ } @Override - MethodInvocation addMethod(Class clazz, String method, String html) throws IOException { - loaders.add(clazz.getClassLoader()); - MethodInvocation c = new MethodInvocation(clazz.getName(), method, html); + InvocationContext runMethod(InvocationContext c) throws IOException { + loaders.add(c.clazz.getClassLoader()); methods.add(c); try { c.await(timeOut); @@ -154,7 +153,7 @@ ), "/execute"); conf.addHttpHandler(new HttpHandler() { int cnt; - List cases = new ArrayList<>(); + List cases = new ArrayList<>(); @Override public void service(Request request, Response response) throws Exception { String id = request.getParameter("request"); @@ -166,7 +165,7 @@ cases.get(Integer.parseInt(id)).result(value, null); } - MethodInvocation mi = methods.take(); + InvocationContext mi = methods.take(); if (mi == END) { response.getWriter().write(""); wait.countDown(); @@ -176,7 +175,7 @@ } cases.add(mi); - final String cn = mi.className; + final String cn = mi.clazz.getName(); final String mn = mi.methodName; LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{cnt, cn, mn}); response.getWriter().write("{" diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Console.java Thu Jan 31 16:13:04 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,253 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.net.URL; -import java.util.Enumeration; -import org.apidesign.bck2brwsr.core.JavaScriptBody; - -/** - * - * @author Jaroslav Tulach - */ -public class Console { - static { - turnAssetionStatusOn(); - } - - @JavaScriptBody(args = {"id", "attr"}, body = - "return window.document.getElementById(id)[attr].toString();") - private static native Object getAttr(String id, String attr); - - @JavaScriptBody(args = {"id", "attr", "value"}, body = - "window.document.getElementById(id)[attr] = value;") - private static native void setAttr(String id, String attr, Object value); - - @JavaScriptBody(args = {}, body = "return; window.close();") - private static native void closeWindow(); - - private static void log(String newText) { - String id = "bck2brwsr.result"; - String attr = "value"; - setAttr(id, attr, getAttr(id, attr) + "\n" + newText); - setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); - } - - public static void execute() throws Exception { - String clazz = (String) getAttr("clazz", "value"); - String method = (String) getAttr("method", "value"); - Object res = invokeMethod(clazz, method); - setAttr("bck2brwsr.result", "value", res); - } - - @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" - + "var request = new XMLHttpRequest();\n" - + "request.open('GET', url, true);\n" - + "request.onreadystatechange = function() {\n" - + " if (this.readyState!==4) return;\n" - + " arr[0] = this.responseText;\n" - + " callback.run__V();\n" - + "};" - + "request.send();" - ) - private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; - - public static void harness(String url) throws IOException { - log("Connecting to " + url); - Request r = new Request(url); - } - - private static class Request implements Runnable { - private final String[] arr = { null }; - private final String url; - - private Request(String url) throws IOException { - this.url = url; - loadText(url, this, arr); - } - - @Override - public void run() { - try { - String data = arr[0]; - log("\nGot \"" + data + "\""); - - if (data == null) { - log("Some error exiting"); - closeWindow(); - return; - } - - if (data.isEmpty()) { - log("No data, exiting"); - closeWindow(); - return; - } - - Case c = Case.parseData(data); - if (c.getHtmlFragment() != null) { - setAttr("bck2brwsr.fragment", "innerHTML", c.getHtmlFragment()); - } - log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); - - Object result = invokeMethod(c.getClassName(), c.getMethodName()); - - setAttr("bck2brwsr.fragment", "innerHTML", ""); - log("Result: " + result); - - result = encodeURL("" + result); - - log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); - String u = url + "?request=" + c.getRequestId() + "&result=" + result; - - loadText(u, this, arr); - - } catch (Exception ex) { - log(ex.getMessage()); - } - } - } - - private static String encodeURL(String r) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < r.length(); i++) { - int ch = r.charAt(i); - if (ch < 32 || ch == '%' || ch == '+') { - sb.append("%").append(("0" + Integer.toHexString(ch)).substring(0, 2)); - } else { - if (ch == 32) { - sb.append("+"); - } else { - sb.append((char)ch); - } - } - } - return sb.toString(); - } - - static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { - final Object r = invokeMethod(clazz, method); - return r == null ? "null" : r.toString().toString(); - } - - /** Helper method that inspects the classpath and loads given resource - * (usually a class file). Used while running tests in Rhino. - * - * @param name resource name to find - * @return the array of bytes in the given resource - * @throws IOException I/O in case something goes wrong - */ - public static byte[] read(String name) throws IOException { - URL u = null; - Enumeration en = Console.class.getClassLoader().getResources(name); - while (en.hasMoreElements()) { - u = en.nextElement(); - } - if (u == null) { - throw new IOException("Can't find " + name); - } - try (InputStream is = u.openStream()) { - byte[] arr; - arr = new byte[is.available()]; - int offset = 0; - while (offset < arr.length) { - int len = is.read(arr, offset, arr.length - offset); - if (len == -1) { - throw new IOException("Can't read " + name); - } - offset += len; - } - return arr; - } - } - - private static Object invokeMethod(String clazz, String method) - throws ClassNotFoundException, InvocationTargetException, - SecurityException, IllegalAccessException, IllegalArgumentException, - InstantiationException { - Method found = null; - Class c = Class.forName(clazz); - for (Method m : c.getMethods()) { - if (m.getName().equals(method)) { - found = m; - } - } - Object res; - if (found != null) { - try { - if ((found.getModifiers() & Modifier.STATIC) != 0) { - res = found.invoke(null); - } else { - res = found.invoke(c.newInstance()); - } - } catch (Throwable ex) { - res = ex.getClass().getName() + ":" + ex.getMessage(); - } - } else { - res = "Can't find method " + method + " in " + clazz; - } - return res; - } - - @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") - private static void turnAssetionStatusOn() { - } - - private static final class Case { - private final Object data; - - private Case(Object data) { - this.data = data; - } - - public static Case parseData(String s) { - return new Case(toJSON(s)); - } - - public String getMethodName() { - return value("methodName", data); - } - - public String getClassName() { - return value("className", data); - } - - public String getRequestId() { - return value("request", data); - } - - public String getHtmlFragment() { - return value("html", data); - } - - @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") - private static native Object toJSON(String s); - - @JavaScriptBody(args = {"p", "d"}, body = - "var v = d[p];\n" - + "if (typeof v === 'undefined') return null;\n" - + "return v.toString();" - ) - private static native String value(String p, Object d); - } -} diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java Thu Jan 31 16:58:27 2013 +0100 @@ -0,0 +1,97 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** Represents individual method invocation, its context and its result. + * + * @author Jaroslav Tulach + */ +public final class InvocationContext { + final CountDownLatch wait = new CountDownLatch(1); + final Class clazz; + final String methodName; + private final Launcher launcher; + private String result; + private Throwable exception; + String html; + String httpContent; + String httpType; + String httpPath; + + InvocationContext(Launcher launcher, Class clazz, String methodName) { + this.launcher = launcher; + this.clazz = clazz; + this.methodName = methodName; + } + + /** An HTML fragment to be available for the execution. Useful primarily when + * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}. + * @param html the html fragment + */ + public void setHtmlFragment(String html) { + this.html = html; + } + + /** HTTP resource to be available during execution. An invocation may + * perform an HTTP query and obtain a resource relative to the page. + */ + public void setHttpResource(String relativePath, String mimeType, String content) { + this.httpPath = relativePath; + this.httpType = mimeType; + this.httpContent = content; + } + + /** Invokes the associated method. + * @return the textual result of the invocation + */ + public String invoke() throws IOException { + launcher.runMethod(this); + return toString(); + } + + /** Obtains textual result of the invocation. + * @return text representing the exception or result value + */ + @Override + public String toString() { + if (exception != null) { + return exception.toString(); + } + return result; + } + + /** + * @param timeOut + * @throws InterruptedException + */ + void await(long timeOut) throws InterruptedException { + wait.await(timeOut, TimeUnit.MILLISECONDS); + } + + void result(String r, Throwable e) { + this.result = r; + this.exception = e; + wait.countDown(); + } + + +} diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Thu Jan 31 16:13:04 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java Thu Jan 31 16:58:27 2013 +0100 @@ -17,6 +17,7 @@ */ package org.apidesign.bck2brwsr.launcher; +import org.apidesign.bck2brwsr.launcher.impl.Console; import java.io.IOException; import java.io.InputStream; import java.net.URL; @@ -43,18 +44,17 @@ private Object console; - @Override MethodInvocation addMethod(Class clazz, String method, String html) { - loaders.add(clazz.getClassLoader()); - MethodInvocation mi = new MethodInvocation(clazz.getName(), method, html); + @Override InvocationContext runMethod(InvocationContext mi) { + loaders.add(mi.clazz.getClassLoader()); try { long time = System.currentTimeMillis(); - LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.className, mi.methodName}); + LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName}); String res = code.invokeMethod( console, "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2", - mi.className, mi.methodName).toString(); + mi.clazz.getName(), mi.methodName).toString(); time = System.currentTimeMillis() - time; - LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.className, mi.methodName, res, time}); + LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time}); mi.result(res, null); } catch (ScriptException | NoSuchMethodException ex) { mi.result(null, ex); @@ -89,7 +89,7 @@ ScriptEngine mach = sem.getEngineByExtension("js"); sb.append( - "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.Console.read);" + "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.impl.Console.read);" + "\nfunction initVM() { return vm; };" + "\n"); diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Thu Jan 31 16:13:04 2013 +0100 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Thu Jan 31 16:58:27 2013 +0100 @@ -24,7 +24,8 @@ import org.apidesign.vm4brwsr.Bck2Brwsr; /** An abstraction for executing tests in a Bck2Brwsr virtual machine. - * Either in JavaScript engine, or in external browser. + * Either in {@linkm Launcher#createJavaScript JavaScript engine}, + * or in {@linkm Launcher#createBrowser external browser}. * * @author Jaroslav Tulach */ @@ -32,39 +33,83 @@ Launcher() { } + + /** Initializes the launcher. This may mean starting a web browser or + * initializing execution engine. + * @throws IOException if something goes wrong + */ + public abstract void initialize() throws IOException; - abstract MethodInvocation addMethod(Class clazz, String method, String html) throws IOException; - - public abstract void initialize() throws IOException; + /** Shuts down the launcher. + * @throws IOException if something goes wrong + */ public abstract void shutdown() throws IOException; - public MethodInvocation invokeMethod(Class clazz, String method, String html) throws IOException { - return addMethod(clazz, method, html); + + + /** Builds an invocation context. The context can later be customized + * and {@link InvocationContext#invoke() invoked}. + * + * @param clazz the class to execute method from + * @param method the method to execute + * @return the context pointing to the selected method + */ + public InvocationContext createInvocation(Class clazz, String method) { + return new InvocationContext(this, clazz, method); } - + /** Creates launcher that uses internal JavaScript engine (Rhino). + * @return the launcher + */ public static Launcher createJavaScript() { final JSLauncher l = new JSLauncher(); l.addClassLoader(Bck2Brwsr.class.getClassLoader()); return l; } + /** Creates launcher that is using external browser. + * + * @param cmd null to use java.awt.Desktop to show the launcher + * or a string to execute in an external process (with a parameter to the URL) + * @return launcher executing in external browser. + */ public static Launcher createBrowser(String cmd) { final Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(cmd); l.addClassLoader(Bck2Brwsr.class.getClassLoader()); l.setTimeout(180000); return l; } + + /** Starts an HTTP server which provides access to classes and resources + * available in the classes URL and shows a start page + * available as {@link ClassLoader#getResource(java.lang.String)} from the + * provide classloader. Opens a browser with URL showing the start page. + * + * @param classes classloader offering access to classes and resources + * @param startpage page to show in the browser + * @return interface that allows one to stop the server + * @throws IOException if something goes wrong + */ public static Closeable showURL(URLClassLoader classes, String startpage) throws IOException { Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); l.addClassLoader(classes); l.showURL(startpage); return l; } + /** Starts an HTTP server which provides access to certain directory. + * The startpage should be relative location inside the root + * driecotry + * Opens a browser with URL showing the start page. + * + * @param directory the root directory on disk + * @praam startpage relative path from the root to the page + * @exception IOException if something goes wrong. + */ public static Closeable showDir(File directory, String startpage) throws IOException { Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null); l.showDirectory(directory, startpage); return l; } + abstract InvocationContext runMethod(InvocationContext c) throws IOException; } diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/MethodInvocation.java Thu Jan 31 16:13:04 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,59 +0,0 @@ -/** - * Back 2 Browser Bytecode Translator - * Copyright (C) 2012 Jaroslav Tulach - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. Look for COPYING file in the top folder. - * If not, see http://opensource.org/licenses/GPL-2.0. - */ -package org.apidesign.bck2brwsr.launcher; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - -/** - * - * @author Jaroslav Tulach - */ -public final class MethodInvocation { - final CountDownLatch wait = new CountDownLatch(1); - final String className; - final String methodName; - final String html; - private String result; - private Throwable exception; - - MethodInvocation(String className, String methodName, String html) { - this.className = className; - this.methodName = methodName; - this.html = html; - } - - void await(long timeOut) throws InterruptedException { - wait.await(timeOut, TimeUnit.MILLISECONDS); - } - - void result(String r, Throwable e) { - this.result = r; - this.exception = e; - wait.countDown(); - } - - @Override - public String toString() { - if (exception != null) { - return exception.toString(); - } - return result; - } - -} diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java Thu Jan 31 16:58:27 2013 +0100 @@ -0,0 +1,255 @@ +/** + * Back 2 Browser Bytecode Translator + * Copyright (C) 2012 Jaroslav Tulach + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. Look for COPYING file in the top folder. + * If not, see http://opensource.org/licenses/GPL-2.0. + */ +package org.apidesign.bck2brwsr.launcher.impl; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.net.URL; +import java.util.Enumeration; +import org.apidesign.bck2brwsr.core.JavaScriptBody; + +/** + * + * @author Jaroslav Tulach + */ +public class Console { + private Console() { + } + static { + turnAssetionStatusOn(); + } + + @JavaScriptBody(args = {"id", "attr"}, body = + "return window.document.getElementById(id)[attr].toString();") + private static native Object getAttr(String id, String attr); + + @JavaScriptBody(args = {"id", "attr", "value"}, body = + "window.document.getElementById(id)[attr] = value;") + private static native void setAttr(String id, String attr, Object value); + + @JavaScriptBody(args = {}, body = "return; window.close();") + private static native void closeWindow(); + + private static void log(String newText) { + String id = "bck2brwsr.result"; + String attr = "value"; + setAttr(id, attr, getAttr(id, attr) + "\n" + newText); + setAttr(id, "scrollTop", getAttr(id, "scrollHeight")); + } + + public static void execute() throws Exception { + String clazz = (String) getAttr("clazz", "value"); + String method = (String) getAttr("method", "value"); + Object res = invokeMethod(clazz, method); + setAttr("bck2brwsr.result", "value", res); + } + + @JavaScriptBody(args = { "url", "callback", "arr" }, body = "" + + "var request = new XMLHttpRequest();\n" + + "request.open('GET', url, true);\n" + + "request.onreadystatechange = function() {\n" + + " if (this.readyState!==4) return;\n" + + " arr[0] = this.responseText;\n" + + " callback.run__V();\n" + + "};" + + "request.send();" + ) + private static native void loadText(String url, Runnable callback, String[] arr) throws IOException; + + public static void harness(String url) throws IOException { + log("Connecting to " + url); + Request r = new Request(url); + } + + private static class Request implements Runnable { + private final String[] arr = { null }; + private final String url; + + private Request(String url) throws IOException { + this.url = url; + loadText(url, this, arr); + } + + @Override + public void run() { + try { + String data = arr[0]; + log("\nGot \"" + data + "\""); + + if (data == null) { + log("Some error exiting"); + closeWindow(); + return; + } + + if (data.isEmpty()) { + log("No data, exiting"); + closeWindow(); + return; + } + + Case c = Case.parseData(data); + if (c.getHtmlFragment() != null) { + setAttr("bck2brwsr.fragment", "innerHTML", c.getHtmlFragment()); + } + log("Invoking " + c.getClassName() + '.' + c.getMethodName() + " as request: " + c.getRequestId()); + + Object result = invokeMethod(c.getClassName(), c.getMethodName()); + + setAttr("bck2brwsr.fragment", "innerHTML", ""); + log("Result: " + result); + + result = encodeURL("" + result); + + log("Sending back: " + url + "?request=" + c.getRequestId() + "&result=" + result); + String u = url + "?request=" + c.getRequestId() + "&result=" + result; + + loadText(u, this, arr); + + } catch (Exception ex) { + log(ex.getMessage()); + } + } + } + + private static String encodeURL(String r) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < r.length(); i++) { + int ch = r.charAt(i); + if (ch < 32 || ch == '%' || ch == '+') { + sb.append("%").append(("0" + Integer.toHexString(ch)).substring(0, 2)); + } else { + if (ch == 32) { + sb.append("+"); + } else { + sb.append((char)ch); + } + } + } + return sb.toString(); + } + + static String invoke(String clazz, String method) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException, InstantiationException { + final Object r = invokeMethod(clazz, method); + return r == null ? "null" : r.toString().toString(); + } + + /** Helper method that inspects the classpath and loads given resource + * (usually a class file). Used while running tests in Rhino. + * + * @param name resource name to find + * @return the array of bytes in the given resource + * @throws IOException I/O in case something goes wrong + */ + public static byte[] read(String name) throws IOException { + URL u = null; + Enumeration en = Console.class.getClassLoader().getResources(name); + while (en.hasMoreElements()) { + u = en.nextElement(); + } + if (u == null) { + throw new IOException("Can't find " + name); + } + try (InputStream is = u.openStream()) { + byte[] arr; + arr = new byte[is.available()]; + int offset = 0; + while (offset < arr.length) { + int len = is.read(arr, offset, arr.length - offset); + if (len == -1) { + throw new IOException("Can't read " + name); + } + offset += len; + } + return arr; + } + } + + private static Object invokeMethod(String clazz, String method) + throws ClassNotFoundException, InvocationTargetException, + SecurityException, IllegalAccessException, IllegalArgumentException, + InstantiationException { + Method found = null; + Class c = Class.forName(clazz); + for (Method m : c.getMethods()) { + if (m.getName().equals(method)) { + found = m; + } + } + Object res; + if (found != null) { + try { + if ((found.getModifiers() & Modifier.STATIC) != 0) { + res = found.invoke(null); + } else { + res = found.invoke(c.newInstance()); + } + } catch (Throwable ex) { + res = ex.getClass().getName() + ":" + ex.getMessage(); + } + } else { + res = "Can't find method " + method + " in " + clazz; + } + return res; + } + + @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;") + private static void turnAssetionStatusOn() { + } + + private static final class Case { + private final Object data; + + private Case(Object data) { + this.data = data; + } + + public static Case parseData(String s) { + return new Case(toJSON(s)); + } + + public String getMethodName() { + return value("methodName", data); + } + + public String getClassName() { + return value("className", data); + } + + public String getRequestId() { + return value("request", data); + } + + public String getHtmlFragment() { + return value("html", data); + } + + @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');") + private static native Object toJSON(String s); + + @JavaScriptBody(args = {"p", "d"}, body = + "var v = d[p];\n" + + "if (typeof v === 'undefined') return null;\n" + + "return v.toString();" + ) + private static native String value(String p, Object d); + } +} diff -r 6daa39b17563 -r a07253cf2ca4 launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml --- a/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Thu Jan 31 16:13:04 2013 +0100 +++ b/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml Thu Jan 31 16:58:27 2013 +0100 @@ -34,7 +34,7 @@
diff -r 6daa39b17563 -r a07253cf2ca4 vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java --- a/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Thu Jan 31 16:13:04 2013 +0100 +++ b/vmtest/src/main/java/org/apidesign/bck2brwsr/vmtest/impl/Bck2BrwsrCase.java Thu Jan 31 16:58:27 2013 +0100 @@ -24,7 +24,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import org.apidesign.bck2brwsr.launcher.Launcher; -import org.apidesign.bck2brwsr.launcher.MethodInvocation; +import org.apidesign.bck2brwsr.launcher.InvocationContext; import org.testng.ITest; import org.testng.annotations.Test; @@ -51,8 +51,11 @@ @Test(groups = "run") public void executeCode() throws Throwable { if (l != null) { - MethodInvocation c = l.invokeMethod(m.getDeclaringClass(), m.getName(), html); - String res = c.toString(); + InvocationContext c = l.createInvocation(m.getDeclaringClass(), m.getName()); + if (html != null) { + c.setHtmlFragment(html); + } + String res = c.invoke(); value = res; if (fail) { int idx = res.indexOf(':');