Splitting launcher API and its implementation based on Bck2Brwsr model
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 28 Apr 2013 10:14:31 +0200
branchmodel
changeset 1033b8773b7b9ecd
parent 1031 660187048c64
child 1034 28dc692f3b11
Splitting launcher API and its implementation based on Bck2Brwsr
launcher/api/pom.xml
launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java
launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java
launcher/http/pom.xml
launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java
launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java
launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java
launcher/http/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml
launcher/pom.xml
rt/launcher/pom.xml
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java
rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java
rt/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml
rt/pom.xml
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/launcher/api/pom.xml	Sun Apr 28 10:14:31 2013 +0200
     1.3 @@ -0,0 +1,20 @@
     1.4 +<?xml version="1.0"?>
     1.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"
     1.6 +    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
     1.7 +  <modelVersion>4.0.0</modelVersion>
     1.8 +  <parent>
     1.9 +    <groupId>org.apidesign.bck2brwsr</groupId>
    1.10 +    <artifactId>launcher-pom</artifactId>
    1.11 +    <version>0.7-SNAPSHOT</version>
    1.12 +  </parent>
    1.13 +  <groupId>org.apidesign.bck2brwsr</groupId>
    1.14 +  <artifactId>launcher</artifactId>
    1.15 +  <version>0.7-SNAPSHOT</version>
    1.16 +  <name>Launcher API</name>
    1.17 +  <url>http://maven.apache.org</url>
    1.18 +  <properties>
    1.19 +    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    1.20 +  </properties>
    1.21 +  <dependencies>
    1.22 +  </dependencies>
    1.23 +</project>
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java	Sun Apr 28 10:14:31 2013 +0200
     2.3 @@ -0,0 +1,115 @@
     2.4 +/**
     2.5 + * Back 2 Browser Bytecode Translator
     2.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     2.7 + *
     2.8 + * This program is free software: you can redistribute it and/or modify
     2.9 + * it under the terms of the GNU General Public License as published by
    2.10 + * the Free Software Foundation, version 2 of the License.
    2.11 + *
    2.12 + * This program is distributed in the hope that it will be useful,
    2.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    2.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    2.15 + * GNU General Public License for more details.
    2.16 + *
    2.17 + * You should have received a copy of the GNU General Public License
    2.18 + * along with this program. Look for COPYING file in the top folder.
    2.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    2.20 + */
    2.21 +package org.apidesign.bck2brwsr.launcher;
    2.22 +
    2.23 +import java.io.IOException;
    2.24 +import java.io.InputStream;
    2.25 +import java.util.ArrayList;
    2.26 +import java.util.List;
    2.27 +import java.util.concurrent.CountDownLatch;
    2.28 +import java.util.concurrent.TimeUnit;
    2.29 +
    2.30 +/** Represents individual method invocation, its context and its result.
    2.31 + *
    2.32 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    2.33 + */
    2.34 +public final class InvocationContext {
    2.35 +    final CountDownLatch wait = new CountDownLatch(1);
    2.36 +    final Class<?> clazz;
    2.37 +    final String methodName;
    2.38 +    private final Launcher launcher;
    2.39 +    private String result;
    2.40 +    private Throwable exception;
    2.41 +    String html;
    2.42 +    final List<Resource> resources = new ArrayList<>();
    2.43 +
    2.44 +    InvocationContext(Launcher launcher, Class<?> clazz, String methodName) {
    2.45 +        this.launcher = launcher;
    2.46 +        this.clazz = clazz;
    2.47 +        this.methodName = methodName;
    2.48 +    }
    2.49 +    
    2.50 +    /** An HTML fragment to be available for the execution. Useful primarily when
    2.51 +     * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}.
    2.52 +     * @param html the html fragment
    2.53 +     */
    2.54 +    public void setHtmlFragment(String html) {
    2.55 +        this.html = html;
    2.56 +    }
    2.57 +    
    2.58 +    /** HTTP resource to be available during execution. An invocation may
    2.59 +     * perform an HTTP query and obtain a resource relative to the page.
    2.60 +     */
    2.61 +    public void addHttpResource(String relativePath, String mimeType, String[] parameters, InputStream content) {
    2.62 +        if (relativePath == null || mimeType == null || content == null || parameters == null) {
    2.63 +            throw new NullPointerException();
    2.64 +        }
    2.65 +        resources.add(new Resource(content, mimeType, relativePath, parameters));
    2.66 +    }
    2.67 +    
    2.68 +    /** Invokes the associated method. 
    2.69 +     * @return the textual result of the invocation
    2.70 +     */
    2.71 +    public String invoke() throws IOException {
    2.72 +        launcher.runMethod(this);
    2.73 +        return toString();
    2.74 +    }
    2.75 +    
    2.76 +    /** Obtains textual result of the invocation.
    2.77 +     * @return text representing the exception or result value
    2.78 +     */
    2.79 +    @Override
    2.80 +    public String toString() {
    2.81 +        if (exception != null) {
    2.82 +            return exception.toString();
    2.83 +        }
    2.84 +        return result;
    2.85 +    }
    2.86 +    
    2.87 +    /**
    2.88 +     * @param timeOut
    2.89 +     * @throws InterruptedException 
    2.90 +     */
    2.91 +    void await(long timeOut) throws InterruptedException {
    2.92 +        wait.await(timeOut, TimeUnit.MILLISECONDS);
    2.93 +    }
    2.94 +    
    2.95 +    void result(String r, Throwable e) {
    2.96 +        this.result = r;
    2.97 +        this.exception = e;
    2.98 +        wait.countDown();
    2.99 +    }
   2.100 +
   2.101 +
   2.102 +    static final class Resource {
   2.103 +        final InputStream httpContent;
   2.104 +        final String httpType;
   2.105 +        final String httpPath;
   2.106 +        final String[] parameters;
   2.107 +
   2.108 +        Resource(InputStream httpContent, String httpType, String httpPath,
   2.109 +            String[] parameters
   2.110 +        ) {
   2.111 +            httpContent.mark(Integer.MAX_VALUE);
   2.112 +            this.httpContent = httpContent;
   2.113 +            this.httpType = httpType;
   2.114 +            this.httpPath = httpPath;
   2.115 +            this.parameters = parameters;
   2.116 +        }
   2.117 +    }
   2.118 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java	Sun Apr 28 10:14:31 2013 +0200
     3.3 @@ -0,0 +1,139 @@
     3.4 +/**
     3.5 + * Back 2 Browser Bytecode Translator
     3.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     3.7 + *
     3.8 + * This program is free software: you can redistribute it and/or modify
     3.9 + * it under the terms of the GNU General Public License as published by
    3.10 + * the Free Software Foundation, version 2 of the License.
    3.11 + *
    3.12 + * This program is distributed in the hope that it will be useful,
    3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    3.15 + * GNU General Public License for more details.
    3.16 + *
    3.17 + * You should have received a copy of the GNU General Public License
    3.18 + * along with this program. Look for COPYING file in the top folder.
    3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    3.20 + */
    3.21 +package org.apidesign.bck2brwsr.launcher;
    3.22 +
    3.23 +import java.io.Closeable;
    3.24 +import java.io.File;
    3.25 +import java.io.IOException;
    3.26 +import java.lang.reflect.Constructor;
    3.27 +
    3.28 +/** An abstraction for executing tests in a Bck2Brwsr virtual machine.
    3.29 + * Either in {@link Launcher#createJavaScript JavaScript engine}, 
    3.30 + * or in {@link Launcher#createBrowser external browser}.
    3.31 + * <p>
    3.32 + * There also are methods to {@link #showDir(java.io.File, java.lang.String) display pages} 
    3.33 + * in an external browser served by internal HTTP server.
    3.34 + *
    3.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    3.36 + */
    3.37 +public abstract class Launcher {
    3.38 +
    3.39 +    Launcher() {
    3.40 +    }
    3.41 +
    3.42 +    /** Initializes the launcher. This may mean starting a web browser or
    3.43 +     * initializing execution engine.
    3.44 +     * @throws IOException if something goes wrong
    3.45 +     */
    3.46 +    public abstract void initialize() throws IOException;
    3.47 +    
    3.48 +    /** Shuts down the launcher.
    3.49 +     * @throws IOException if something goes wrong
    3.50 +     */
    3.51 +    public abstract void shutdown() throws IOException;
    3.52 +    
    3.53 +    
    3.54 +    /** Builds an invocation context. The context can later be customized
    3.55 +     * and {@link InvocationContext#invoke() invoked}.
    3.56 +     * 
    3.57 +     * @param clazz the class to execute method from
    3.58 +     * @param method the method to execute
    3.59 +     * @return the context pointing to the selected method
    3.60 +     */
    3.61 +    public InvocationContext createInvocation(Class<?> clazz, String method) {
    3.62 +        return new InvocationContext(this, clazz, method);
    3.63 +    }
    3.64 +    
    3.65 +
    3.66 +    /** Creates launcher that uses internal JavaScript engine (Rhino).
    3.67 +     * @return the launcher
    3.68 +     */
    3.69 +    public static Launcher createJavaScript() {
    3.70 +        try {
    3.71 +            Class<?> c = loadClass("org.apidesign.bck2brwsr.launcher.JSLauncher");
    3.72 +            return (Launcher) c.newInstance();
    3.73 +        } catch (Exception ex) {
    3.74 +            throw new IllegalStateException("Please include org.apidesign.bck2brwsr:launcher.html dependency!", ex);
    3.75 +        }
    3.76 +    }
    3.77 +    
    3.78 +    /** Creates launcher that is using external browser.
    3.79 +     * 
    3.80 +     * @param cmd <code>null</code> to use <code>java.awt.Desktop</code> to show the launcher
    3.81 +     *    or a string to execute in an external process (with a parameter to the URL)
    3.82 +     * @return launcher executing in external browser.
    3.83 +     */
    3.84 +    public static Launcher createBrowser(String cmd) {
    3.85 +        try {
    3.86 +            Class<?> c = loadClass("org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher");
    3.87 +            Constructor<?> cnstr = c.getConstructor(String.class);
    3.88 +            return (Launcher) cnstr.newInstance(cmd);
    3.89 +        } catch (Exception ex) {
    3.90 +            throw new IllegalStateException("Please include org.apidesign.bck2brwsr:launcher.html dependency!", ex);
    3.91 +        }
    3.92 +    }
    3.93 +    
    3.94 +    /** Starts an HTTP server which provides access to classes and resources
    3.95 +     * available in the <code>classes</code> URL and shows a start page
    3.96 +     * available as {@link ClassLoader#getResource(java.lang.String)} from the
    3.97 +     * provide classloader. Opens a browser with URL showing the start page.
    3.98 +     * 
    3.99 +     * @param classes classloader offering access to classes and resources
   3.100 +     * @param startpage page to show in the browser
   3.101 +     * @return interface that allows one to stop the server
   3.102 +     * @throws IOException if something goes wrong
   3.103 +     */
   3.104 +    public static Closeable showURL(ClassLoader classes, String startpage) throws IOException {
   3.105 +        Launcher l = createBrowser(null);
   3.106 +        l.addClassLoader(classes);
   3.107 +        l.showURL(startpage);
   3.108 +        return (Closeable) l;
   3.109 +    }
   3.110 +    /** Starts an HTTP server which provides access to certain directory.
   3.111 +     * The <code>startpage</code> should be relative location inside the root 
   3.112 +     * directory. Opens a browser with URL showing the start page.
   3.113 +     * 
   3.114 +     * @param directory the root directory on disk
   3.115 +     * @param startpage relative path from the root to the page
   3.116 +     * @exception IOException if something goes wrong.
   3.117 +     */
   3.118 +    public static Closeable showDir(File directory, String startpage) throws IOException {
   3.119 +        Launcher l = createBrowser(null);
   3.120 +        l.showDirectory(directory, startpage);
   3.121 +        return (Closeable) l;
   3.122 +    }
   3.123 +
   3.124 +    abstract InvocationContext runMethod(InvocationContext c) throws IOException; 
   3.125 +
   3.126 +
   3.127 +    private static Class<?> loadClass(String cn) throws ClassNotFoundException {
   3.128 +        return Launcher.class.getClassLoader().loadClass(cn);
   3.129 +    }
   3.130 +
   3.131 +    void showDirectory(File directory, String startpage) throws IOException {
   3.132 +        throw new UnsupportedOperationException();
   3.133 +    }
   3.134 +
   3.135 +    void showURL(String startpage) throws IOException {
   3.136 +        throw new UnsupportedOperationException();
   3.137 +    }
   3.138 +
   3.139 +    void addClassLoader(ClassLoader classes) {
   3.140 +        throw new UnsupportedOperationException();
   3.141 +    }
   3.142 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/launcher/http/pom.xml	Sun Apr 28 10:14:31 2013 +0200
     4.3 @@ -0,0 +1,55 @@
     4.4 +<?xml version="1.0"?>
     4.5 +<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">
     4.6 +  <modelVersion>4.0.0</modelVersion>
     4.7 +  <parent>
     4.8 +    <groupId>org.apidesign.bck2brwsr</groupId>
     4.9 +    <artifactId>rt</artifactId>
    4.10 +    <version>0.7-SNAPSHOT</version>
    4.11 +  </parent>
    4.12 +  <groupId>org.apidesign.bck2brwsr</groupId>
    4.13 +  <artifactId>launcher.http</artifactId>
    4.14 +  <version>0.7-SNAPSHOT</version>
    4.15 +  <name>Bck2Brwsr Launcher</name>
    4.16 +  <url>http://maven.apache.org</url>
    4.17 +    <build>
    4.18 +        <plugins>
    4.19 +            <plugin>
    4.20 +                <groupId>org.apache.maven.plugins</groupId>
    4.21 +                <artifactId>maven-compiler-plugin</artifactId>
    4.22 +                <version>2.3.2</version>
    4.23 +                <configuration>
    4.24 +                    <source>1.7</source>
    4.25 +                    <target>1.7</target>
    4.26 +                </configuration>
    4.27 +            </plugin>
    4.28 +            <plugin>
    4.29 +                <groupId>org.apache.maven.plugins</groupId>
    4.30 +                <artifactId>maven-javadoc-plugin</artifactId>
    4.31 +                <configuration>
    4.32 +                    <excludePackageNames>org.apidesign.bck2brwsr.launcher.impl</excludePackageNames>
    4.33 +                    <skip>false</skip>
    4.34 +                </configuration>
    4.35 +            </plugin>
    4.36 +        </plugins>
    4.37 +    </build>
    4.38 +    <properties>
    4.39 +    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    4.40 +  </properties>
    4.41 +  <dependencies>
    4.42 +    <dependency>
    4.43 +      <groupId>${project.groupId}</groupId>
    4.44 +      <artifactId>launcher</artifactId>
    4.45 +      <version>${project.version}</version>
    4.46 +    </dependency>
    4.47 +    <dependency>
    4.48 +      <groupId>org.glassfish.grizzly</groupId>
    4.49 +      <artifactId>grizzly-http-server</artifactId>
    4.50 +      <version>2.2.19</version>
    4.51 +    </dependency>
    4.52 +    <dependency>
    4.53 +      <groupId>${project.groupId}</groupId>
    4.54 +      <artifactId>vm4brwsr</artifactId>
    4.55 +      <version>${project.version}</version>
    4.56 +    </dependency>
    4.57 +  </dependencies>
    4.58 +</project>
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java	Sun Apr 28 10:14:31 2013 +0200
     5.3 @@ -0,0 +1,604 @@
     5.4 +/**
     5.5 + * Back 2 Browser Bytecode Translator
     5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     5.7 + *
     5.8 + * This program is free software: you can redistribute it and/or modify
     5.9 + * it under the terms of the GNU General Public License as published by
    5.10 + * the Free Software Foundation, version 2 of the License.
    5.11 + *
    5.12 + * This program is distributed in the hope that it will be useful,
    5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    5.15 + * GNU General Public License for more details.
    5.16 + *
    5.17 + * You should have received a copy of the GNU General Public License
    5.18 + * along with this program. Look for COPYING file in the top folder.
    5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    5.20 + */
    5.21 +package org.apidesign.bck2brwsr.launcher;
    5.22 +
    5.23 +import java.io.Closeable;
    5.24 +import java.io.File;
    5.25 +import java.io.IOException;
    5.26 +import java.io.InputStream;
    5.27 +import java.io.InterruptedIOException;
    5.28 +import java.io.OutputStream;
    5.29 +import java.io.UnsupportedEncodingException;
    5.30 +import java.io.Writer;
    5.31 +import java.net.URI;
    5.32 +import java.net.URISyntaxException;
    5.33 +import java.net.URL;
    5.34 +import java.util.ArrayList;
    5.35 +import java.util.Arrays;
    5.36 +import java.util.Enumeration;
    5.37 +import java.util.LinkedHashSet;
    5.38 +import java.util.List;
    5.39 +import java.util.Set;
    5.40 +import java.util.concurrent.BlockingQueue;
    5.41 +import java.util.concurrent.CountDownLatch;
    5.42 +import java.util.concurrent.LinkedBlockingQueue;
    5.43 +import java.util.concurrent.TimeUnit;
    5.44 +import java.util.logging.Level;
    5.45 +import java.util.logging.Logger;
    5.46 +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource;
    5.47 +import org.apidesign.vm4brwsr.Bck2Brwsr;
    5.48 +import org.glassfish.grizzly.PortRange;
    5.49 +import org.glassfish.grizzly.http.server.HttpHandler;
    5.50 +import org.glassfish.grizzly.http.server.HttpServer;
    5.51 +import org.glassfish.grizzly.http.server.NetworkListener;
    5.52 +import org.glassfish.grizzly.http.server.Request;
    5.53 +import org.glassfish.grizzly.http.server.Response;
    5.54 +import org.glassfish.grizzly.http.server.ServerConfiguration;
    5.55 +import org.glassfish.grizzly.http.util.HttpStatus;
    5.56 +
    5.57 +/**
    5.58 + * Lightweight server to launch Bck2Brwsr applications and tests.
    5.59 + * Supports execution in native browser as well as Java's internal 
    5.60 + * execution engine.
    5.61 + */
    5.62 +final class Bck2BrwsrLauncher extends Launcher implements Closeable {
    5.63 +    private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName());
    5.64 +    private static final InvocationContext END = new InvocationContext(null, null, null);
    5.65 +    private final Set<ClassLoader> loaders = new LinkedHashSet<>();
    5.66 +    private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<>();
    5.67 +    private long timeOut;
    5.68 +    private final Res resources = new Res();
    5.69 +    private final String cmd;
    5.70 +    private Object[] brwsr;
    5.71 +    private HttpServer server;
    5.72 +    private CountDownLatch wait;
    5.73 +    
    5.74 +    public Bck2BrwsrLauncher(String cmd) {
    5.75 +        this.cmd = cmd;
    5.76 +        addClassLoader(Bck2Brwsr.class.getClassLoader());
    5.77 +        setTimeout(180000);
    5.78 +    }
    5.79 +    
    5.80 +    @Override
    5.81 +    InvocationContext runMethod(InvocationContext c) throws IOException {
    5.82 +        loaders.add(c.clazz.getClassLoader());
    5.83 +        methods.add(c);
    5.84 +        try {
    5.85 +            c.await(timeOut);
    5.86 +        } catch (InterruptedException ex) {
    5.87 +            throw new IOException(ex);
    5.88 +        }
    5.89 +        return c;
    5.90 +    }
    5.91 +    
    5.92 +    public void setTimeout(long ms) {
    5.93 +        timeOut = ms;
    5.94 +    }
    5.95 +    
    5.96 +    public void addClassLoader(ClassLoader url) {
    5.97 +        this.loaders.add(url);
    5.98 +    }
    5.99 +
   5.100 +    public void showURL(String startpage) throws IOException {
   5.101 +        if (!startpage.startsWith("/")) {
   5.102 +            startpage = "/" + startpage;
   5.103 +        }
   5.104 +        HttpServer s = initServer(".", true);
   5.105 +        int last = startpage.lastIndexOf('/');
   5.106 +        String prefix = startpage.substring(0, last);
   5.107 +        String simpleName = startpage.substring(last);
   5.108 +        s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/");
   5.109 +        try {
   5.110 +            launchServerAndBrwsr(s, simpleName);
   5.111 +        } catch (URISyntaxException | InterruptedException ex) {
   5.112 +            throw new IOException(ex);
   5.113 +        }
   5.114 +    }
   5.115 +
   5.116 +    void showDirectory(File dir, String startpage) throws IOException {
   5.117 +        if (!startpage.startsWith("/")) {
   5.118 +            startpage = "/" + startpage;
   5.119 +        }
   5.120 +        HttpServer s = initServer(dir.getPath(), false);
   5.121 +        try {
   5.122 +            launchServerAndBrwsr(s, startpage);
   5.123 +        } catch (URISyntaxException | InterruptedException ex) {
   5.124 +            throw new IOException(ex);
   5.125 +        }
   5.126 +    }
   5.127 +
   5.128 +    @Override
   5.129 +    public void initialize() throws IOException {
   5.130 +        try {
   5.131 +            executeInBrowser();
   5.132 +        } catch (InterruptedException ex) {
   5.133 +            final InterruptedIOException iio = new InterruptedIOException(ex.getMessage());
   5.134 +            iio.initCause(ex);
   5.135 +            throw iio;
   5.136 +        } catch (Exception ex) {
   5.137 +            if (ex instanceof IOException) {
   5.138 +                throw (IOException)ex;
   5.139 +            }
   5.140 +            if (ex instanceof RuntimeException) {
   5.141 +                throw (RuntimeException)ex;
   5.142 +            }
   5.143 +            throw new IOException(ex);
   5.144 +        }
   5.145 +    }
   5.146 +    
   5.147 +    private HttpServer initServer(String path, boolean addClasses) throws IOException {
   5.148 +        HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535));
   5.149 +
   5.150 +        final ServerConfiguration conf = s.getServerConfiguration();
   5.151 +        if (addClasses) {
   5.152 +            conf.addHttpHandler(new VM(resources), "/bck2brwsr.js");
   5.153 +            conf.addHttpHandler(new Classes(resources), "/classes/");
   5.154 +        }
   5.155 +        return s;
   5.156 +    }
   5.157 +    
   5.158 +    private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
   5.159 +        wait = new CountDownLatch(1);
   5.160 +        server = initServer(".", true);
   5.161 +        final ServerConfiguration conf = server.getServerConfiguration();
   5.162 +        
   5.163 +        class DynamicResourceHandler extends HttpHandler {
   5.164 +            private final InvocationContext ic;
   5.165 +            public DynamicResourceHandler(InvocationContext ic) {
   5.166 +                if (ic == null || ic.resources.isEmpty()) {
   5.167 +                    throw new NullPointerException();
   5.168 +                }
   5.169 +                this.ic = ic;
   5.170 +                for (Resource r : ic.resources) {
   5.171 +                    conf.addHttpHandler(this, r.httpPath);
   5.172 +                }
   5.173 +            }
   5.174 +
   5.175 +            public void close() {
   5.176 +                conf.removeHttpHandler(this);
   5.177 +            }
   5.178 +            
   5.179 +            @Override
   5.180 +            public void service(Request request, Response response) throws Exception {
   5.181 +                for (Resource r : ic.resources) {
   5.182 +                    if (r.httpPath.equals(request.getRequestURI())) {
   5.183 +                        LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
   5.184 +                        response.setContentType(r.httpType);
   5.185 +                        r.httpContent.reset();
   5.186 +                        String[] params = null;
   5.187 +                        if (r.parameters.length != 0) {
   5.188 +                            params = new String[r.parameters.length];
   5.189 +                            for (int i = 0; i < r.parameters.length; i++) {
   5.190 +                                params[i] = request.getParameter(r.parameters[i]);
   5.191 +                            }
   5.192 +                        }
   5.193 +                        
   5.194 +                        copyStream(r.httpContent, response.getOutputStream(), null, params);
   5.195 +                    }
   5.196 +                }
   5.197 +            }
   5.198 +        }
   5.199 +        
   5.200 +        conf.addHttpHandler(new Page(resources, 
   5.201 +            "org/apidesign/bck2brwsr/launcher/harness.xhtml"
   5.202 +        ), "/execute");
   5.203 +        
   5.204 +        conf.addHttpHandler(new HttpHandler() {
   5.205 +            int cnt;
   5.206 +            List<InvocationContext> cases = new ArrayList<>();
   5.207 +            DynamicResourceHandler prev;
   5.208 +            @Override
   5.209 +            public void service(Request request, Response response) throws Exception {
   5.210 +                String id = request.getParameter("request");
   5.211 +                String value = request.getParameter("result");
   5.212 +                if (value != null && value.indexOf((char)0xC5) != -1) {
   5.213 +                    value = toUTF8(value);
   5.214 +                }
   5.215 +                
   5.216 +                
   5.217 +                InvocationContext mi = null;
   5.218 +                int caseNmbr = -1;
   5.219 +                
   5.220 +                if (id != null && value != null) {
   5.221 +                    LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value});
   5.222 +                    value = decodeURL(value);
   5.223 +                    int indx = Integer.parseInt(id);
   5.224 +                    cases.get(indx).result(value, null);
   5.225 +                    if (++indx < cases.size()) {
   5.226 +                        mi = cases.get(indx);
   5.227 +                        LOG.log(Level.INFO, "Re-executing case {0}", indx);
   5.228 +                        caseNmbr = indx;
   5.229 +                    }
   5.230 +                } else {
   5.231 +                    if (!cases.isEmpty()) {
   5.232 +                        LOG.info("Re-executing test cases");
   5.233 +                        mi = cases.get(0);
   5.234 +                        caseNmbr = 0;
   5.235 +                    }
   5.236 +                }
   5.237 +                
   5.238 +                if (prev != null) {
   5.239 +                    prev.close();
   5.240 +                    prev = null;
   5.241 +                }
   5.242 +                
   5.243 +                if (mi == null) {
   5.244 +                    mi = methods.take();
   5.245 +                    caseNmbr = cnt++;
   5.246 +                }
   5.247 +                if (mi == END) {
   5.248 +                    response.getWriter().write("");
   5.249 +                    wait.countDown();
   5.250 +                    cnt = 0;
   5.251 +                    LOG.log(Level.INFO, "End of data reached. Exiting.");
   5.252 +                    return;
   5.253 +                }
   5.254 +                
   5.255 +                if (!mi.resources.isEmpty()) {
   5.256 +                    prev = new DynamicResourceHandler(mi);
   5.257 +                }
   5.258 +                
   5.259 +                cases.add(mi);
   5.260 +                final String cn = mi.clazz.getName();
   5.261 +                final String mn = mi.methodName;
   5.262 +                LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn});
   5.263 +                response.getWriter().write("{"
   5.264 +                    + "className: '" + cn + "', "
   5.265 +                    + "methodName: '" + mn + "', "
   5.266 +                    + "request: " + caseNmbr
   5.267 +                );
   5.268 +                if (mi.html != null) {
   5.269 +                    response.getWriter().write(", html: '");
   5.270 +                    response.getWriter().write(encodeJSON(mi.html));
   5.271 +                    response.getWriter().write("'");
   5.272 +                }
   5.273 +                response.getWriter().write("}");
   5.274 +            }
   5.275 +        }, "/data");
   5.276 +
   5.277 +        this.brwsr = launchServerAndBrwsr(server, "/execute");
   5.278 +    }
   5.279 +    
   5.280 +    private static String encodeJSON(String in) {
   5.281 +        StringBuilder sb = new StringBuilder();
   5.282 +        for (int i = 0; i < in.length(); i++) {
   5.283 +            char ch = in.charAt(i);
   5.284 +            if (ch < 32 || ch == '\'' || ch == '"') {
   5.285 +                sb.append("\\u");
   5.286 +                String hs = "0000" + Integer.toHexString(ch);
   5.287 +                hs = hs.substring(hs.length() - 4);
   5.288 +                sb.append(hs);
   5.289 +            } else {
   5.290 +                sb.append(ch);
   5.291 +            }
   5.292 +        }
   5.293 +        return sb.toString();
   5.294 +    }
   5.295 +    
   5.296 +    @Override
   5.297 +    public void shutdown() throws IOException {
   5.298 +        methods.offer(END);
   5.299 +        for (;;) {
   5.300 +            int prev = methods.size();
   5.301 +            try {
   5.302 +                if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) {
   5.303 +                    break;
   5.304 +                }
   5.305 +            } catch (InterruptedException ex) {
   5.306 +                throw new IOException(ex);
   5.307 +            }
   5.308 +            if (prev == methods.size()) {
   5.309 +                LOG.log(
   5.310 +                    Level.WARNING, 
   5.311 +                    "Timeout and no test has been executed meanwhile (at {0}). Giving up.", 
   5.312 +                    methods.size()
   5.313 +                );
   5.314 +                break;
   5.315 +            }
   5.316 +            LOG.log(Level.INFO, 
   5.317 +                "Timeout, but tests got from {0} to {1}. Trying again.", 
   5.318 +                new Object[]{prev, methods.size()}
   5.319 +            );
   5.320 +        }
   5.321 +        stopServerAndBrwsr(server, brwsr);
   5.322 +    }
   5.323 +    
   5.324 +    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
   5.325 +        for (;;) {
   5.326 +            int ch = is.read();
   5.327 +            if (ch == -1) {
   5.328 +                break;
   5.329 +            }
   5.330 +            if (ch == '$' && params.length > 0) {
   5.331 +                int cnt = is.read() - '0';
   5.332 +                if (baseURL != null && cnt == 'U' - '0') {
   5.333 +                    os.write(baseURL.getBytes("UTF-8"));
   5.334 +                } else {
   5.335 +                    if (cnt >= 0 && cnt < params.length) {
   5.336 +                        os.write(params[cnt].getBytes("UTF-8"));
   5.337 +                    } else {
   5.338 +                        os.write('$');
   5.339 +                        os.write(cnt + '0');
   5.340 +                    }
   5.341 +                }
   5.342 +            } else {
   5.343 +                os.write(ch);
   5.344 +            }
   5.345 +        }
   5.346 +    }
   5.347 +
   5.348 +    private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
   5.349 +        server.start();
   5.350 +        NetworkListener listener = server.getListeners().iterator().next();
   5.351 +        int port = listener.getPort();
   5.352 +        
   5.353 +        URI uri = new URI("http://localhost:" + port + page);
   5.354 +        LOG.log(Level.INFO, "Showing {0}", uri);
   5.355 +        if (cmd == null) {
   5.356 +            try {
   5.357 +                LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] {
   5.358 +                    System.getProperty("java.vm.name"),
   5.359 +                    System.getProperty("java.vm.vendor"),
   5.360 +                    System.getProperty("java.vm.version"),
   5.361 +                });
   5.362 +                java.awt.Desktop.getDesktop().browse(uri);
   5.363 +                LOG.log(Level.INFO, "Desktop.browse successfully finished");
   5.364 +                return null;
   5.365 +            } catch (UnsupportedOperationException ex) {
   5.366 +                LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage());
   5.367 +                LOG.log(Level.FINE, null, ex);
   5.368 +            }
   5.369 +        }
   5.370 +        {
   5.371 +            String cmdName = cmd == null ? "xdg-open" : cmd;
   5.372 +            String[] cmdArr = { 
   5.373 +                cmdName, uri.toString()
   5.374 +            };
   5.375 +            LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr));
   5.376 +            final Process process = Runtime.getRuntime().exec(cmdArr);
   5.377 +            return new Object[] { process, null };
   5.378 +        }
   5.379 +    }
   5.380 +    private static String toUTF8(String value) throws UnsupportedEncodingException {
   5.381 +        byte[] arr = new byte[value.length()];
   5.382 +        for (int i = 0; i < arr.length; i++) {
   5.383 +            arr[i] = (byte)value.charAt(i);
   5.384 +        }
   5.385 +        return new String(arr, "UTF-8");
   5.386 +    }
   5.387 +    
   5.388 +    private static String decodeURL(String s) {
   5.389 +        for (;;) {
   5.390 +            int pos = s.indexOf('%');
   5.391 +            if (pos == -1) {
   5.392 +                return s;
   5.393 +            }
   5.394 +            int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16);
   5.395 +            s = s.substring(0, pos) + (char)i + s.substring(pos + 2);
   5.396 +        }
   5.397 +    }
   5.398 +    
   5.399 +    private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException {
   5.400 +        if (brwsr == null) {
   5.401 +            return;
   5.402 +        }
   5.403 +        Process process = (Process)brwsr[0];
   5.404 +        
   5.405 +        server.stop();
   5.406 +        InputStream stdout = process.getInputStream();
   5.407 +        InputStream stderr = process.getErrorStream();
   5.408 +        drain("StdOut", stdout);
   5.409 +        drain("StdErr", stderr);
   5.410 +        process.destroy();
   5.411 +        int res;
   5.412 +        try {
   5.413 +            res = process.waitFor();
   5.414 +        } catch (InterruptedException ex) {
   5.415 +            throw new IOException(ex);
   5.416 +        }
   5.417 +        LOG.log(Level.INFO, "Exit code: {0}", res);
   5.418 +
   5.419 +        deleteTree((File)brwsr[1]);
   5.420 +    }
   5.421 +    
   5.422 +    private static void drain(String name, InputStream is) throws IOException {
   5.423 +        int av = is.available();
   5.424 +        if (av > 0) {
   5.425 +            StringBuilder sb = new StringBuilder();
   5.426 +            sb.append("v== ").append(name).append(" ==v\n");
   5.427 +            while (av-- > 0) {
   5.428 +                sb.append((char)is.read());
   5.429 +            }
   5.430 +            sb.append("\n^== ").append(name).append(" ==^");
   5.431 +            LOG.log(Level.INFO, sb.toString());
   5.432 +        }
   5.433 +    }
   5.434 +
   5.435 +    private void deleteTree(File file) {
   5.436 +        if (file == null) {
   5.437 +            return;
   5.438 +        }
   5.439 +        File[] arr = file.listFiles();
   5.440 +        if (arr != null) {
   5.441 +            for (File s : arr) {
   5.442 +                deleteTree(s);
   5.443 +            }
   5.444 +        }
   5.445 +        file.delete();
   5.446 +    }
   5.447 +
   5.448 +    @Override
   5.449 +    public void close() throws IOException {
   5.450 +        shutdown();
   5.451 +    }
   5.452 +
   5.453 +    private class Res implements Bck2Brwsr.Resources {
   5.454 +        @Override
   5.455 +        public InputStream get(String resource) throws IOException {
   5.456 +            for (ClassLoader l : loaders) {
   5.457 +                URL u = null;
   5.458 +                Enumeration<URL> en = l.getResources(resource);
   5.459 +                while (en.hasMoreElements()) {
   5.460 +                    u = en.nextElement();
   5.461 +                }
   5.462 +                if (u != null) {
   5.463 +                    return u.openStream();
   5.464 +                }
   5.465 +            }
   5.466 +            throw new IOException("Can't find " + resource);
   5.467 +        }
   5.468 +    }
   5.469 +
   5.470 +    private static class Page extends HttpHandler {
   5.471 +        final String resource;
   5.472 +        private final String[] args;
   5.473 +        private final Res res;
   5.474 +        
   5.475 +        public Page(Res res, String resource, String... args) {
   5.476 +            this.res = res;
   5.477 +            this.resource = resource;
   5.478 +            this.args = args.length == 0 ? new String[] { "$0" } : args;
   5.479 +        }
   5.480 +
   5.481 +        @Override
   5.482 +        public void service(Request request, Response response) throws Exception {
   5.483 +            String r = computePage(request);
   5.484 +            if (r.startsWith("/")) {
   5.485 +                r = r.substring(1);
   5.486 +            }
   5.487 +            String[] replace = {};
   5.488 +            if (r.endsWith(".html")) {
   5.489 +                response.setContentType("text/html");
   5.490 +                LOG.info("Content type text/html");
   5.491 +                replace = args;
   5.492 +            }
   5.493 +            if (r.endsWith(".xhtml")) {
   5.494 +                response.setContentType("application/xhtml+xml");
   5.495 +                LOG.info("Content type application/xhtml+xml");
   5.496 +                replace = args;
   5.497 +            }
   5.498 +            OutputStream os = response.getOutputStream();
   5.499 +            try (InputStream is = res.get(r)) {
   5.500 +                copyStream(is, os, request.getRequestURL().toString(), replace);
   5.501 +            } catch (IOException ex) {
   5.502 +                response.setDetailMessage(ex.getLocalizedMessage());
   5.503 +                response.setError();
   5.504 +                response.setStatus(404);
   5.505 +            }
   5.506 +        }
   5.507 +
   5.508 +        protected String computePage(Request request) {
   5.509 +            String r = resource;
   5.510 +            if (r == null) {
   5.511 +                r = request.getHttpHandlerPath();
   5.512 +            }
   5.513 +            return r;
   5.514 +        }
   5.515 +    }
   5.516 +    
   5.517 +    private static class SubTree extends Page {
   5.518 +
   5.519 +        public SubTree(Res res, String resource, String... args) {
   5.520 +            super(res, resource, args);
   5.521 +        }
   5.522 +
   5.523 +        @Override
   5.524 +        protected String computePage(Request request) {
   5.525 +            return resource + request.getHttpHandlerPath();
   5.526 +        }
   5.527 +        
   5.528 +        
   5.529 +    }
   5.530 +
   5.531 +    private static class VM extends HttpHandler {
   5.532 +        private final String bck2brwsr;
   5.533 +
   5.534 +        public VM(Res loader) throws IOException {
   5.535 +            StringBuilder sb = new StringBuilder();
   5.536 +            Bck2Brwsr.generate(sb, loader);
   5.537 +            sb.append(
   5.538 +                  "(function WrapperVM(global) {"
   5.539 +                + "  function ldCls(res) {\n"
   5.540 +                + "    var request = new XMLHttpRequest();\n"
   5.541 +                + "    request.open('GET', '/classes/' + res, false);\n"
   5.542 +                + "    request.send();\n"
   5.543 +                + "    if (request.status !== 200) return null;\n"
   5.544 +                + "    var arr = eval('(' + request.responseText + ')');\n"
   5.545 +                + "    return arr;\n"
   5.546 +                + "  }\n"
   5.547 +                + "  var prevvm = global.bck2brwsr;\n"
   5.548 +                + "  global.bck2brwsr = function() {\n"
   5.549 +                + "    var args = Array.prototype.slice.apply(arguments);\n"
   5.550 +                + "    args.unshift(ldCls);\n"
   5.551 +                + "    return prevvm.apply(null, args);\n"
   5.552 +                + "  };\n"
   5.553 +                + "})(this);\n"
   5.554 +            );
   5.555 +            this.bck2brwsr = sb.toString();
   5.556 +        }
   5.557 +
   5.558 +        @Override
   5.559 +        public void service(Request request, Response response) throws Exception {
   5.560 +            response.setCharacterEncoding("UTF-8");
   5.561 +            response.setContentType("text/javascript");
   5.562 +            response.getWriter().write(bck2brwsr);
   5.563 +        }
   5.564 +    }
   5.565 +
   5.566 +    private static class Classes extends HttpHandler {
   5.567 +        private final Res loader;
   5.568 +
   5.569 +        public Classes(Res loader) {
   5.570 +            this.loader = loader;
   5.571 +        }
   5.572 +
   5.573 +        @Override
   5.574 +        public void service(Request request, Response response) throws Exception {
   5.575 +            String res = request.getHttpHandlerPath();
   5.576 +            if (res.startsWith("/")) {
   5.577 +                res = res.substring(1);
   5.578 +            }
   5.579 +            try (InputStream is = loader.get(res)) {
   5.580 +                response.setContentType("text/javascript");
   5.581 +                Writer w = response.getWriter();
   5.582 +                w.append("[");
   5.583 +                for (int i = 0;; i++) {
   5.584 +                    int b = is.read();
   5.585 +                    if (b == -1) {
   5.586 +                        break;
   5.587 +                    }
   5.588 +                    if (i > 0) {
   5.589 +                        w.append(", ");
   5.590 +                    }
   5.591 +                    if (i % 20 == 0) {
   5.592 +                        w.write("\n");
   5.593 +                    }
   5.594 +                    if (b > 127) {
   5.595 +                        b = b - 256;
   5.596 +                    }
   5.597 +                    w.append(Integer.toString(b));
   5.598 +                }
   5.599 +                w.append("\n]");
   5.600 +            } catch (IOException ex) {
   5.601 +                response.setStatus(HttpStatus.NOT_FOUND_404);
   5.602 +                response.setError();
   5.603 +                response.setDetailMessage(ex.getMessage());
   5.604 +            }
   5.605 +        }
   5.606 +    }
   5.607 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java	Sun Apr 28 10:14:31 2013 +0200
     6.3 @@ -0,0 +1,135 @@
     6.4 +/**
     6.5 + * Back 2 Browser Bytecode Translator
     6.6 + * Copyright (C) 2012 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.
    6.16 + *
    6.17 + * You should have received a copy of the GNU General Public License
    6.18 + * along with this program. Look for COPYING file in the top folder.
    6.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    6.20 + */
    6.21 +package org.apidesign.bck2brwsr.launcher;
    6.22 +
    6.23 +import org.apidesign.bck2brwsr.launcher.impl.Console;
    6.24 +import java.io.IOException;
    6.25 +import java.io.InputStream;
    6.26 +import java.net.URL;
    6.27 +import java.util.Enumeration;
    6.28 +import java.util.LinkedHashSet;
    6.29 +import java.util.Set;
    6.30 +import java.util.logging.Level;
    6.31 +import java.util.logging.Logger;
    6.32 +import javax.script.Invocable;
    6.33 +import javax.script.ScriptEngine;
    6.34 +import javax.script.ScriptEngineManager;
    6.35 +import javax.script.ScriptException;
    6.36 +import org.apidesign.vm4brwsr.Bck2Brwsr;
    6.37 +
    6.38 +/**
    6.39 + * Tests execution in Java's internal scripting engine.
    6.40 + */
    6.41 +final class JSLauncher extends Launcher {
    6.42 +    private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName());
    6.43 +    private Set<ClassLoader> loaders = new LinkedHashSet<>();
    6.44 +    private final Res resources = new Res();
    6.45 +    private Invocable code;
    6.46 +    private StringBuilder codeSeq;
    6.47 +    private Object console;
    6.48 +
    6.49 +    JSLauncher() {
    6.50 +        addClassLoader(Bck2Brwsr.class.getClassLoader());
    6.51 +    }
    6.52 +    
    6.53 +    @Override InvocationContext runMethod(InvocationContext mi) {
    6.54 +        loaders.add(mi.clazz.getClassLoader());
    6.55 +        try {
    6.56 +            long time = System.currentTimeMillis();
    6.57 +            LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName});
    6.58 +            String res = code.invokeMethod(
    6.59 +                console,
    6.60 +                "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2",
    6.61 +                mi.clazz.getName(), mi.methodName).toString();
    6.62 +            time = System.currentTimeMillis() - time;
    6.63 +            LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time});
    6.64 +            mi.result(res, null);
    6.65 +        } catch (ScriptException | NoSuchMethodException ex) {
    6.66 +            mi.result(null, ex);
    6.67 +        }
    6.68 +        return mi;
    6.69 +    }
    6.70 +    
    6.71 +    public void addClassLoader(ClassLoader url) {
    6.72 +        this.loaders.add(url);
    6.73 +    }
    6.74 +
    6.75 +    @Override
    6.76 +    public void initialize() throws IOException {
    6.77 +        try {
    6.78 +            initRhino();
    6.79 +        } catch (Exception ex) {
    6.80 +            if (ex instanceof IOException) {
    6.81 +                throw (IOException)ex;
    6.82 +            }
    6.83 +            if (ex instanceof RuntimeException) {
    6.84 +                throw (RuntimeException)ex;
    6.85 +            }
    6.86 +            throw new IOException(ex);
    6.87 +        }
    6.88 +    }
    6.89 +    
    6.90 +    private void initRhino() throws IOException, ScriptException, NoSuchMethodException {
    6.91 +        StringBuilder sb = new StringBuilder();
    6.92 +        Bck2Brwsr.generate(sb, new Res());
    6.93 +
    6.94 +        ScriptEngineManager sem = new ScriptEngineManager();
    6.95 +        ScriptEngine mach = sem.getEngineByExtension("js");
    6.96 +
    6.97 +        sb.append(
    6.98 +              "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.impl.Console.read);"
    6.99 +            + "\nfunction initVM() { return vm; };"
   6.100 +            + "\n");
   6.101 +
   6.102 +        Object res = mach.eval(sb.toString());
   6.103 +        if (!(mach instanceof Invocable)) {
   6.104 +            throw new IOException("It is invocable object: " + res);
   6.105 +        }
   6.106 +        code = (Invocable) mach;
   6.107 +        codeSeq = sb;
   6.108 +        
   6.109 +        Object vm = code.invokeFunction("initVM");
   6.110 +        console = code.invokeMethod(vm, "loadClass", Console.class.getName());
   6.111 +    }
   6.112 +
   6.113 +    @Override
   6.114 +    public void shutdown() throws IOException {
   6.115 +    }
   6.116 +
   6.117 +    @Override
   6.118 +    public String toString() {
   6.119 +        return codeSeq.toString();
   6.120 +    }
   6.121 +    
   6.122 +    private class Res implements Bck2Brwsr.Resources {
   6.123 +        @Override
   6.124 +        public InputStream get(String resource) throws IOException {
   6.125 +            for (ClassLoader l : loaders) {
   6.126 +                URL u = null;
   6.127 +                Enumeration<URL> en = l.getResources(resource);
   6.128 +                while (en.hasMoreElements()) {
   6.129 +                    u = en.nextElement();
   6.130 +                }
   6.131 +                if (u != null) {
   6.132 +                    return u.openStream();
   6.133 +                }
   6.134 +            }
   6.135 +            throw new IOException("Can't find " + resource);
   6.136 +        }
   6.137 +    }
   6.138 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/launcher/http/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java	Sun Apr 28 10:14:31 2013 +0200
     7.3 @@ -0,0 +1,356 @@
     7.4 +/**
     7.5 + * Back 2 Browser Bytecode Translator
     7.6 + * Copyright (C) 2012 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.
    7.16 + *
    7.17 + * You should have received a copy of the GNU General Public License
    7.18 + * along with this program. Look for COPYING file in the top folder.
    7.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
    7.20 + */
    7.21 +package org.apidesign.bck2brwsr.launcher.impl;
    7.22 +
    7.23 +import java.io.IOException;
    7.24 +import java.io.InputStream;
    7.25 +import java.io.UnsupportedEncodingException;
    7.26 +import java.lang.reflect.InvocationTargetException;
    7.27 +import java.lang.reflect.Method;
    7.28 +import java.lang.reflect.Modifier;
    7.29 +import java.net.URL;
    7.30 +import java.util.Enumeration;
    7.31 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
    7.32 +
    7.33 +/**
    7.34 + *
    7.35 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    7.36 + */
    7.37 +public class Console {
    7.38 +    private Console() {
    7.39 +    }
    7.40 +    static {
    7.41 +        turnAssetionStatusOn();
    7.42 +    }
    7.43 +    
    7.44 +    @JavaScriptBody(args = {"id", "attr"}, body = 
    7.45 +        "return window.document.getElementById(id)[attr].toString();")
    7.46 +    private static native Object getAttr(String id, String attr);
    7.47 +    @JavaScriptBody(args = {"elem", "attr"}, body = 
    7.48 +        "return elem[attr].toString();")
    7.49 +    private static native Object getAttr(Object elem, String attr);
    7.50 +
    7.51 +    @JavaScriptBody(args = {"id", "attr", "value"}, body = 
    7.52 +        "window.document.getElementById(id)[attr] = value;")
    7.53 +    private static native void setAttr(String id, String attr, Object value);
    7.54 +    @JavaScriptBody(args = {"elem", "attr", "value"}, body = 
    7.55 +        "elem[attr] = value;")
    7.56 +    private static native void setAttr(Object id, String attr, Object value);
    7.57 +    
    7.58 +    @JavaScriptBody(args = {}, body = "return; window.close();")
    7.59 +    private static native void closeWindow();
    7.60 +
    7.61 +    private static Object textArea;
    7.62 +    private static Object statusArea;
    7.63 +    
    7.64 +    private static void log(String newText) {
    7.65 +        if (textArea == null) {
    7.66 +            return;
    7.67 +        }
    7.68 +        String attr = "value";
    7.69 +        setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText);
    7.70 +        setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight"));
    7.71 +    }
    7.72 +    
    7.73 +    private static void beginTest(Case c) {
    7.74 +        Object[] arr = new Object[2];
    7.75 +        beginTest(c.getClassName() + "." + c.getMethodName(), c, arr);
    7.76 +        textArea = arr[0];
    7.77 +        statusArea = arr[1];
    7.78 +    }
    7.79 +    
    7.80 +    private static void finishTest(Case c, Object res) {
    7.81 +        if ("null".equals(res)) {
    7.82 +            setAttr(statusArea, "innerHTML", "Success");
    7.83 +        } else {
    7.84 +            setAttr(statusArea, "innerHTML", "Result " + res);
    7.85 +        }
    7.86 +        statusArea = null;
    7.87 +        textArea = null;
    7.88 +    }
    7.89 +
    7.90 +    @JavaScriptBody(args = { "test", "c", "arr" }, body = 
    7.91 +          "var ul = window.document.getElementById('bck2brwsr.result');\n"
    7.92 +        + "var li = window.document.createElement('li');\n"
    7.93 +        + "var span = window.document.createElement('span');"
    7.94 +        + "span.innerHTML = test + ' - ';\n"
    7.95 +        + "var details = window.document.createElement('a');\n"
    7.96 +        + "details.innerHTML = 'Details';\n"
    7.97 +        + "details.href = '#';\n"
    7.98 +        + "var p = window.document.createElement('p');\n"
    7.99 +        + "var status = window.document.createElement('a');\n"
   7.100 +        + "status.innerHTML = 'running';"
   7.101 +        + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n"
   7.102 +        + "status.onclick = function() { c.again__V_3Ljava_lang_Object_2(arr); }\n"
   7.103 +        + "var pre = window.document.createElement('textarea');\n"
   7.104 +        + "pre.cols = 100;"
   7.105 +        + "pre.rows = 10;"
   7.106 +        + "li.appendChild(span);\n"
   7.107 +        + "li.appendChild(status);\n"
   7.108 +        + "var span = window.document.createElement('span');"
   7.109 +        + "span.innerHTML = ' ';\n"
   7.110 +        + "li.appendChild(span);\n"
   7.111 +        + "li.appendChild(details);\n"
   7.112 +        + "p.appendChild(pre);\n"
   7.113 +        + "ul.appendChild(li);\n"
   7.114 +        + "arr[0] = pre;\n"
   7.115 +        + "arr[1] = status;\n"
   7.116 +    )
   7.117 +    private static native void beginTest(String test, Case c, Object[] arr);
   7.118 +    
   7.119 +    @JavaScriptBody(args = { "url", "callback", "arr" }, body = ""
   7.120 +        + "var request = new XMLHttpRequest();\n"
   7.121 +        + "request.open('GET', url, true);\n"
   7.122 +        + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
   7.123 +        + "request.onreadystatechange = function() {\n"
   7.124 +        + "  if (this.readyState!==4) return;\n"
   7.125 +        + "  arr[0] = this.responseText;\n"
   7.126 +        + "  callback.run__V();\n"
   7.127 +        + "};"
   7.128 +        + "request.send();"
   7.129 +    )
   7.130 +    private static native void loadText(String url, Runnable callback, String[] arr) throws IOException;
   7.131 +    
   7.132 +    public static void harness(String url) throws IOException {
   7.133 +        log("Connecting to " + url);
   7.134 +        Request r = new Request(url);
   7.135 +    }
   7.136 +    
   7.137 +    private static class Request implements Runnable {
   7.138 +        private final String[] arr = { null };
   7.139 +        private final String url;
   7.140 +        private Case c;
   7.141 +        private int retries;
   7.142 +
   7.143 +        private Request(String url) throws IOException {
   7.144 +            this.url = url;
   7.145 +            loadText(url, this, arr);
   7.146 +        }
   7.147 +        private Request(String url, String u) throws IOException {
   7.148 +            this.url = url;
   7.149 +            loadText(u, this, arr);
   7.150 +        }
   7.151 +        
   7.152 +        @Override
   7.153 +        public void run() {
   7.154 +            try {
   7.155 +                if (c == null) {
   7.156 +                    String data = arr[0];
   7.157 +
   7.158 +                    if (data == null) {
   7.159 +                        log("Some error exiting");
   7.160 +                        closeWindow();
   7.161 +                        return;
   7.162 +                    }
   7.163 +
   7.164 +                    if (data.isEmpty()) {
   7.165 +                        log("No data, exiting");
   7.166 +                        closeWindow();
   7.167 +                        return;
   7.168 +                    }
   7.169 +
   7.170 +                    c = Case.parseData(data);
   7.171 +                    beginTest(c);
   7.172 +                    log("Got \"" + data + "\"");
   7.173 +                } else {
   7.174 +                    log("Processing \"" + arr[0] + "\" for " + retries + " time");
   7.175 +                }
   7.176 +                Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest();
   7.177 +                finishTest(c, result);
   7.178 +                
   7.179 +                String u = url + "?request=" + c.getRequestId() + "&result=" + result;
   7.180 +                new Request(url, u);
   7.181 +            } catch (Exception ex) {
   7.182 +                if (ex instanceof InterruptedException) {
   7.183 +                    log("Re-scheduling in 100ms");
   7.184 +                    schedule(this, 100);
   7.185 +                    return;
   7.186 +                }
   7.187 +                log(ex.getClass().getName() + ":" + ex.getMessage());
   7.188 +            }
   7.189 +        }
   7.190 +    }
   7.191 +    
   7.192 +    private static String encodeURL(String r) throws UnsupportedEncodingException {
   7.193 +        final String SPECIAL = "%$&+,/:;=?@";
   7.194 +        StringBuilder sb = new StringBuilder();
   7.195 +        byte[] utf8 = r.getBytes("UTF-8");
   7.196 +        for (int i = 0; i < utf8.length; i++) {
   7.197 +            int ch = utf8[i] & 0xff;
   7.198 +            if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) {
   7.199 +                final String numbers = "0" + Integer.toHexString(ch);
   7.200 +                sb.append("%").append(numbers.substring(numbers.length() - 2));
   7.201 +            } else {
   7.202 +                if (ch == 32) {
   7.203 +                    sb.append("+");
   7.204 +                } else {
   7.205 +                    sb.append((char)ch);
   7.206 +                }
   7.207 +            }
   7.208 +        }
   7.209 +        return sb.toString();
   7.210 +    }
   7.211 +    
   7.212 +    static String invoke(String clazz, String method) throws 
   7.213 +    ClassNotFoundException, InvocationTargetException, IllegalAccessException, 
   7.214 +    InstantiationException, InterruptedException {
   7.215 +        final Object r = new Case(null).invokeMethod(clazz, method);
   7.216 +        return r == null ? "null" : r.toString().toString();
   7.217 +    }
   7.218 +
   7.219 +    /** Helper method that inspects the classpath and loads given resource
   7.220 +     * (usually a class file). Used while running tests in Rhino.
   7.221 +     * 
   7.222 +     * @param name resource name to find
   7.223 +     * @return the array of bytes in the given resource
   7.224 +     * @throws IOException I/O in case something goes wrong
   7.225 +     */
   7.226 +    public static byte[] read(String name) throws IOException {
   7.227 +        URL u = null;
   7.228 +        Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
   7.229 +        while (en.hasMoreElements()) {
   7.230 +            u = en.nextElement();
   7.231 +        }
   7.232 +        if (u == null) {
   7.233 +            throw new IOException("Can't find " + name);
   7.234 +        }
   7.235 +        try (InputStream is = u.openStream()) {
   7.236 +            byte[] arr;
   7.237 +            arr = new byte[is.available()];
   7.238 +            int offset = 0;
   7.239 +            while (offset < arr.length) {
   7.240 +                int len = is.read(arr, offset, arr.length - offset);
   7.241 +                if (len == -1) {
   7.242 +                    throw new IOException("Can't read " + name);
   7.243 +                }
   7.244 +                offset += len;
   7.245 +            }
   7.246 +            return arr;
   7.247 +        }
   7.248 +    }
   7.249 +   
   7.250 +    @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;")
   7.251 +    private static void turnAssetionStatusOn() {
   7.252 +    }
   7.253 +
   7.254 +    @JavaScriptBody(args = {"r", "time"}, body =
   7.255 +        "return window.setTimeout(function() { r.run__V(); }, time);")
   7.256 +    private static native Object schedule(Runnable r, int time);
   7.257 +    
   7.258 +    private static final class Case {
   7.259 +        private final Object data;
   7.260 +        private Object inst;
   7.261 +
   7.262 +        private Case(Object data) {
   7.263 +            this.data = data;
   7.264 +        }
   7.265 +        
   7.266 +        public static Case parseData(String s) {
   7.267 +            return new Case(toJSON(s));
   7.268 +        }
   7.269 +        
   7.270 +        public String getMethodName() {
   7.271 +            return value("methodName", data);
   7.272 +        }
   7.273 +
   7.274 +        public String getClassName() {
   7.275 +            return value("className", data);
   7.276 +        }
   7.277 +        
   7.278 +        public String getRequestId() {
   7.279 +            return value("request", data);
   7.280 +        }
   7.281 +
   7.282 +        public String getHtmlFragment() {
   7.283 +            return value("html", data);
   7.284 +        }
   7.285 +        
   7.286 +        void again(Object[] arr) {
   7.287 +            try {
   7.288 +                textArea = arr[0];
   7.289 +                statusArea = arr[1];
   7.290 +                setAttr(textArea, "value", "");
   7.291 +                runTest();
   7.292 +            } catch (Exception ex) {
   7.293 +                log(ex.getClass().getName() + ":" + ex.getMessage());
   7.294 +            }
   7.295 +        }
   7.296 +
   7.297 +        private Object runTest() throws IllegalAccessException, 
   7.298 +        IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException, 
   7.299 +        InvocationTargetException, InstantiationException, InterruptedException {
   7.300 +            if (this.getHtmlFragment() != null) {
   7.301 +                setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment());
   7.302 +            }
   7.303 +            log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId());
   7.304 +            Object result = invokeMethod(this.getClassName(), this.getMethodName());
   7.305 +            setAttr("bck2brwsr.fragment", "innerHTML", "");
   7.306 +            log("Result: " + result);
   7.307 +            result = encodeURL("" + result);
   7.308 +            log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
   7.309 +            return result;
   7.310 +        }
   7.311 +
   7.312 +        private Object invokeMethod(String clazz, String method)
   7.313 +        throws ClassNotFoundException, InvocationTargetException,
   7.314 +        InterruptedException, IllegalAccessException, IllegalArgumentException,
   7.315 +        InstantiationException {
   7.316 +            Method found = null;
   7.317 +            Class<?> c = Class.forName(clazz);
   7.318 +            for (Method m : c.getMethods()) {
   7.319 +                if (m.getName().equals(method)) {
   7.320 +                    found = m;
   7.321 +                }
   7.322 +            }
   7.323 +            Object res;
   7.324 +            if (found != null) {
   7.325 +                try {
   7.326 +                    if ((found.getModifiers() & Modifier.STATIC) != 0) {
   7.327 +                        res = found.invoke(null);
   7.328 +                    } else {
   7.329 +                        if (inst == null) {
   7.330 +                            inst = c.newInstance();
   7.331 +                        }
   7.332 +                        res = found.invoke(inst);
   7.333 +                    }
   7.334 +                } catch (Throwable ex) {
   7.335 +                    if (ex instanceof InvocationTargetException) {
   7.336 +                        ex = ((InvocationTargetException) ex).getTargetException();
   7.337 +                    }
   7.338 +                    if (ex instanceof InterruptedException) {
   7.339 +                        throw (InterruptedException)ex;
   7.340 +                    }
   7.341 +                    res = ex.getClass().getName() + ":" + ex.getMessage();
   7.342 +                }
   7.343 +            } else {
   7.344 +                res = "Can't find method " + method + " in " + clazz;
   7.345 +            }
   7.346 +            return res;
   7.347 +        }
   7.348 +        
   7.349 +        @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');")
   7.350 +        private static native Object toJSON(String s);
   7.351 +        
   7.352 +        @JavaScriptBody(args = {"p", "d"}, body = 
   7.353 +              "var v = d[p];\n"
   7.354 +            + "if (typeof v === 'undefined') return null;\n"
   7.355 +            + "return v.toString();"
   7.356 +        )
   7.357 +        private static native String value(String p, Object d);
   7.358 +    }
   7.359 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/launcher/http/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml	Sun Apr 28 10:14:31 2013 +0200
     8.3 @@ -0,0 +1,43 @@
     8.4 +<?xml version="1.0" encoding="UTF-8"?>
     8.5 +<!--
     8.6 +
     8.7 +    Back 2 Browser Bytecode Translator
     8.8 +    Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     8.9 +
    8.10 +    This program is free software: you can redistribute it and/or modify
    8.11 +    it under the terms of the GNU General Public License as published by
    8.12 +    the Free Software Foundation, version 2 of the License.
    8.13 +
    8.14 +    This program is distributed in the hope that it will be useful,
    8.15 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.16 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    8.17 +    GNU General Public License for more details.
    8.18 +
    8.19 +    You should have received a copy of the GNU General Public License
    8.20 +    along with this program. Look for COPYING file in the top folder.
    8.21 +    If not, see http://opensource.org/licenses/GPL-2.0.
    8.22 +
    8.23 +-->
    8.24 +<!DOCTYPE html>
    8.25 +<html xmlns="http://www.w3.org/1999/xhtml">
    8.26 +    <head>
    8.27 +        <title>Bck2Brwsr Harness</title>
    8.28 +    </head>
    8.29 +    <body>
    8.30 +        <script src="/bck2brwsr.js"></script>
    8.31 +        <script>
    8.32 +            var vm = bck2brwsr();
    8.33 +        </script>
    8.34 +        
    8.35 +        <h1>Bck2Brwsr Execution Harness</h1>
    8.36 +        
    8.37 +        <ul id="bck2brwsr.result" style="width: 100%;" >
    8.38 +        </ul>
    8.39 +
    8.40 +        <div id="bck2brwsr.fragment"/>
    8.41 +        
    8.42 +        <script type="text/javascript">
    8.43 +            vm.loadClass('org.apidesign.bck2brwsr.launcher.impl.Console').harness__VLjava_lang_String_2('$U/../data');
    8.44 +        </script>
    8.45 +    </body>
    8.46 +</html>
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/launcher/pom.xml	Sun Apr 28 10:14:31 2013 +0200
     9.3 @@ -0,0 +1,18 @@
     9.4 +<?xml version="1.0" encoding="UTF-8"?>
     9.5 +<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">
     9.6 +  <modelVersion>4.0.0</modelVersion>
     9.7 +  <parent>
     9.8 +    <artifactId>bck2brwsr</artifactId>
     9.9 +    <groupId>org.apidesign</groupId>
    9.10 +    <version>0.7-SNAPSHOT</version>
    9.11 +  </parent>
    9.12 +  <groupId>org.apidesign.bck2brwsr</groupId>
    9.13 +  <artifactId>launcher-pom</artifactId>
    9.14 +  <version>0.7-SNAPSHOT</version>
    9.15 +  <packaging>pom</packaging>
    9.16 +  <name>Launchers</name>
    9.17 +  <modules>
    9.18 +    <module>api</module>
    9.19 +    <module>http</module>
    9.20 +  </modules>
    9.21 +</project>
    9.22 \ No newline at end of file
    10.1 --- a/rt/launcher/pom.xml	Sun Apr 28 09:46:23 2013 +0200
    10.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.3 @@ -1,56 +0,0 @@
    10.4 -<?xml version="1.0"?>
    10.5 -<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">
    10.6 -  <modelVersion>4.0.0</modelVersion>
    10.7 -  <parent>
    10.8 -    <groupId>org.apidesign.bck2brwsr</groupId>
    10.9 -    <artifactId>rt</artifactId>
   10.10 -    <version>0.7-SNAPSHOT</version>
   10.11 -  </parent>
   10.12 -  <groupId>org.apidesign.bck2brwsr</groupId>
   10.13 -  <artifactId>launcher</artifactId>
   10.14 -  <version>0.7-SNAPSHOT</version>
   10.15 -  <name>Bck2Brwsr Launcher</name>
   10.16 -  <url>http://maven.apache.org</url>
   10.17 -    <build>
   10.18 -        <plugins>
   10.19 -            <plugin>
   10.20 -                <groupId>org.apache.maven.plugins</groupId>
   10.21 -                <artifactId>maven-compiler-plugin</artifactId>
   10.22 -                <version>2.3.2</version>
   10.23 -                <configuration>
   10.24 -                    <source>1.7</source>
   10.25 -                    <target>1.7</target>
   10.26 -                </configuration>
   10.27 -            </plugin>
   10.28 -            <plugin>
   10.29 -                <groupId>org.apache.maven.plugins</groupId>
   10.30 -                <artifactId>maven-javadoc-plugin</artifactId>
   10.31 -                <configuration>
   10.32 -                    <excludePackageNames>org.apidesign.bck2brwsr.launcher.impl</excludePackageNames>
   10.33 -                    <skip>false</skip>
   10.34 -                </configuration>
   10.35 -            </plugin>
   10.36 -        </plugins>
   10.37 -    </build>
   10.38 -    <properties>
   10.39 -    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
   10.40 -  </properties>
   10.41 -  <dependencies>
   10.42 -    <dependency>
   10.43 -      <groupId>junit</groupId>
   10.44 -      <artifactId>junit</artifactId>
   10.45 -      <version>3.8.1</version>
   10.46 -      <scope>test</scope>
   10.47 -    </dependency>
   10.48 -    <dependency>
   10.49 -      <groupId>org.glassfish.grizzly</groupId>
   10.50 -      <artifactId>grizzly-http-server</artifactId>
   10.51 -      <version>2.2.19</version>
   10.52 -    </dependency>
   10.53 -    <dependency>
   10.54 -      <groupId>${project.groupId}</groupId>
   10.55 -      <artifactId>vm4brwsr</artifactId>
   10.56 -      <version>${project.version}</version>
   10.57 -    </dependency>
   10.58 -  </dependencies>
   10.59 -</project>
    11.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java	Sun Apr 28 09:46:23 2013 +0200
    11.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.3 @@ -1,602 +0,0 @@
    11.4 -/**
    11.5 - * Back 2 Browser Bytecode Translator
    11.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    11.7 - *
    11.8 - * This program is free software: you can redistribute it and/or modify
    11.9 - * it under the terms of the GNU General Public License as published by
   11.10 - * the Free Software Foundation, version 2 of the License.
   11.11 - *
   11.12 - * This program is distributed in the hope that it will be useful,
   11.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   11.15 - * GNU General Public License for more details.
   11.16 - *
   11.17 - * You should have received a copy of the GNU General Public License
   11.18 - * along with this program. Look for COPYING file in the top folder.
   11.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
   11.20 - */
   11.21 -package org.apidesign.bck2brwsr.launcher;
   11.22 -
   11.23 -import java.io.Closeable;
   11.24 -import java.io.File;
   11.25 -import java.io.IOException;
   11.26 -import java.io.InputStream;
   11.27 -import java.io.InterruptedIOException;
   11.28 -import java.io.OutputStream;
   11.29 -import java.io.UnsupportedEncodingException;
   11.30 -import java.io.Writer;
   11.31 -import java.net.URI;
   11.32 -import java.net.URISyntaxException;
   11.33 -import java.net.URL;
   11.34 -import java.util.ArrayList;
   11.35 -import java.util.Arrays;
   11.36 -import java.util.Enumeration;
   11.37 -import java.util.LinkedHashSet;
   11.38 -import java.util.List;
   11.39 -import java.util.Set;
   11.40 -import java.util.concurrent.BlockingQueue;
   11.41 -import java.util.concurrent.CountDownLatch;
   11.42 -import java.util.concurrent.LinkedBlockingQueue;
   11.43 -import java.util.concurrent.TimeUnit;
   11.44 -import java.util.logging.Level;
   11.45 -import java.util.logging.Logger;
   11.46 -import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource;
   11.47 -import org.apidesign.vm4brwsr.Bck2Brwsr;
   11.48 -import org.glassfish.grizzly.PortRange;
   11.49 -import org.glassfish.grizzly.http.server.HttpHandler;
   11.50 -import org.glassfish.grizzly.http.server.HttpServer;
   11.51 -import org.glassfish.grizzly.http.server.NetworkListener;
   11.52 -import org.glassfish.grizzly.http.server.Request;
   11.53 -import org.glassfish.grizzly.http.server.Response;
   11.54 -import org.glassfish.grizzly.http.server.ServerConfiguration;
   11.55 -import org.glassfish.grizzly.http.util.HttpStatus;
   11.56 -
   11.57 -/**
   11.58 - * Lightweight server to launch Bck2Brwsr applications and tests.
   11.59 - * Supports execution in native browser as well as Java's internal 
   11.60 - * execution engine.
   11.61 - */
   11.62 -final class Bck2BrwsrLauncher extends Launcher implements Closeable {
   11.63 -    private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName());
   11.64 -    private static final InvocationContext END = new InvocationContext(null, null, null);
   11.65 -    private final Set<ClassLoader> loaders = new LinkedHashSet<>();
   11.66 -    private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<>();
   11.67 -    private long timeOut;
   11.68 -    private final Res resources = new Res();
   11.69 -    private final String cmd;
   11.70 -    private Object[] brwsr;
   11.71 -    private HttpServer server;
   11.72 -    private CountDownLatch wait;
   11.73 -    
   11.74 -    public Bck2BrwsrLauncher(String cmd) {
   11.75 -        this.cmd = cmd;
   11.76 -    }
   11.77 -    
   11.78 -    @Override
   11.79 -    InvocationContext runMethod(InvocationContext c) throws IOException {
   11.80 -        loaders.add(c.clazz.getClassLoader());
   11.81 -        methods.add(c);
   11.82 -        try {
   11.83 -            c.await(timeOut);
   11.84 -        } catch (InterruptedException ex) {
   11.85 -            throw new IOException(ex);
   11.86 -        }
   11.87 -        return c;
   11.88 -    }
   11.89 -    
   11.90 -    public void setTimeout(long ms) {
   11.91 -        timeOut = ms;
   11.92 -    }
   11.93 -    
   11.94 -    public void addClassLoader(ClassLoader url) {
   11.95 -        this.loaders.add(url);
   11.96 -    }
   11.97 -
   11.98 -    public void showURL(String startpage) throws IOException {
   11.99 -        if (!startpage.startsWith("/")) {
  11.100 -            startpage = "/" + startpage;
  11.101 -        }
  11.102 -        HttpServer s = initServer(".", true);
  11.103 -        int last = startpage.lastIndexOf('/');
  11.104 -        String prefix = startpage.substring(0, last);
  11.105 -        String simpleName = startpage.substring(last);
  11.106 -        s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/");
  11.107 -        try {
  11.108 -            launchServerAndBrwsr(s, simpleName);
  11.109 -        } catch (URISyntaxException | InterruptedException ex) {
  11.110 -            throw new IOException(ex);
  11.111 -        }
  11.112 -    }
  11.113 -
  11.114 -    void showDirectory(File dir, String startpage) throws IOException {
  11.115 -        if (!startpage.startsWith("/")) {
  11.116 -            startpage = "/" + startpage;
  11.117 -        }
  11.118 -        HttpServer s = initServer(dir.getPath(), false);
  11.119 -        try {
  11.120 -            launchServerAndBrwsr(s, startpage);
  11.121 -        } catch (URISyntaxException | InterruptedException ex) {
  11.122 -            throw new IOException(ex);
  11.123 -        }
  11.124 -    }
  11.125 -
  11.126 -    @Override
  11.127 -    public void initialize() throws IOException {
  11.128 -        try {
  11.129 -            executeInBrowser();
  11.130 -        } catch (InterruptedException ex) {
  11.131 -            final InterruptedIOException iio = new InterruptedIOException(ex.getMessage());
  11.132 -            iio.initCause(ex);
  11.133 -            throw iio;
  11.134 -        } catch (Exception ex) {
  11.135 -            if (ex instanceof IOException) {
  11.136 -                throw (IOException)ex;
  11.137 -            }
  11.138 -            if (ex instanceof RuntimeException) {
  11.139 -                throw (RuntimeException)ex;
  11.140 -            }
  11.141 -            throw new IOException(ex);
  11.142 -        }
  11.143 -    }
  11.144 -    
  11.145 -    private HttpServer initServer(String path, boolean addClasses) throws IOException {
  11.146 -        HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535));
  11.147 -
  11.148 -        final ServerConfiguration conf = s.getServerConfiguration();
  11.149 -        if (addClasses) {
  11.150 -            conf.addHttpHandler(new VM(resources), "/bck2brwsr.js");
  11.151 -            conf.addHttpHandler(new Classes(resources), "/classes/");
  11.152 -        }
  11.153 -        return s;
  11.154 -    }
  11.155 -    
  11.156 -    private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
  11.157 -        wait = new CountDownLatch(1);
  11.158 -        server = initServer(".", true);
  11.159 -        final ServerConfiguration conf = server.getServerConfiguration();
  11.160 -        
  11.161 -        class DynamicResourceHandler extends HttpHandler {
  11.162 -            private final InvocationContext ic;
  11.163 -            public DynamicResourceHandler(InvocationContext ic) {
  11.164 -                if (ic == null || ic.resources.isEmpty()) {
  11.165 -                    throw new NullPointerException();
  11.166 -                }
  11.167 -                this.ic = ic;
  11.168 -                for (Resource r : ic.resources) {
  11.169 -                    conf.addHttpHandler(this, r.httpPath);
  11.170 -                }
  11.171 -            }
  11.172 -
  11.173 -            public void close() {
  11.174 -                conf.removeHttpHandler(this);
  11.175 -            }
  11.176 -            
  11.177 -            @Override
  11.178 -            public void service(Request request, Response response) throws Exception {
  11.179 -                for (Resource r : ic.resources) {
  11.180 -                    if (r.httpPath.equals(request.getRequestURI())) {
  11.181 -                        LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
  11.182 -                        response.setContentType(r.httpType);
  11.183 -                        r.httpContent.reset();
  11.184 -                        String[] params = null;
  11.185 -                        if (r.parameters.length != 0) {
  11.186 -                            params = new String[r.parameters.length];
  11.187 -                            for (int i = 0; i < r.parameters.length; i++) {
  11.188 -                                params[i] = request.getParameter(r.parameters[i]);
  11.189 -                            }
  11.190 -                        }
  11.191 -                        
  11.192 -                        copyStream(r.httpContent, response.getOutputStream(), null, params);
  11.193 -                    }
  11.194 -                }
  11.195 -            }
  11.196 -        }
  11.197 -        
  11.198 -        conf.addHttpHandler(new Page(resources, 
  11.199 -            "org/apidesign/bck2brwsr/launcher/harness.xhtml"
  11.200 -        ), "/execute");
  11.201 -        
  11.202 -        conf.addHttpHandler(new HttpHandler() {
  11.203 -            int cnt;
  11.204 -            List<InvocationContext> cases = new ArrayList<>();
  11.205 -            DynamicResourceHandler prev;
  11.206 -            @Override
  11.207 -            public void service(Request request, Response response) throws Exception {
  11.208 -                String id = request.getParameter("request");
  11.209 -                String value = request.getParameter("result");
  11.210 -                if (value != null && value.indexOf((char)0xC5) != -1) {
  11.211 -                    value = toUTF8(value);
  11.212 -                }
  11.213 -                
  11.214 -                
  11.215 -                InvocationContext mi = null;
  11.216 -                int caseNmbr = -1;
  11.217 -                
  11.218 -                if (id != null && value != null) {
  11.219 -                    LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value});
  11.220 -                    value = decodeURL(value);
  11.221 -                    int indx = Integer.parseInt(id);
  11.222 -                    cases.get(indx).result(value, null);
  11.223 -                    if (++indx < cases.size()) {
  11.224 -                        mi = cases.get(indx);
  11.225 -                        LOG.log(Level.INFO, "Re-executing case {0}", indx);
  11.226 -                        caseNmbr = indx;
  11.227 -                    }
  11.228 -                } else {
  11.229 -                    if (!cases.isEmpty()) {
  11.230 -                        LOG.info("Re-executing test cases");
  11.231 -                        mi = cases.get(0);
  11.232 -                        caseNmbr = 0;
  11.233 -                    }
  11.234 -                }
  11.235 -                
  11.236 -                if (prev != null) {
  11.237 -                    prev.close();
  11.238 -                    prev = null;
  11.239 -                }
  11.240 -                
  11.241 -                if (mi == null) {
  11.242 -                    mi = methods.take();
  11.243 -                    caseNmbr = cnt++;
  11.244 -                }
  11.245 -                if (mi == END) {
  11.246 -                    response.getWriter().write("");
  11.247 -                    wait.countDown();
  11.248 -                    cnt = 0;
  11.249 -                    LOG.log(Level.INFO, "End of data reached. Exiting.");
  11.250 -                    return;
  11.251 -                }
  11.252 -                
  11.253 -                if (!mi.resources.isEmpty()) {
  11.254 -                    prev = new DynamicResourceHandler(mi);
  11.255 -                }
  11.256 -                
  11.257 -                cases.add(mi);
  11.258 -                final String cn = mi.clazz.getName();
  11.259 -                final String mn = mi.methodName;
  11.260 -                LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn});
  11.261 -                response.getWriter().write("{"
  11.262 -                    + "className: '" + cn + "', "
  11.263 -                    + "methodName: '" + mn + "', "
  11.264 -                    + "request: " + caseNmbr
  11.265 -                );
  11.266 -                if (mi.html != null) {
  11.267 -                    response.getWriter().write(", html: '");
  11.268 -                    response.getWriter().write(encodeJSON(mi.html));
  11.269 -                    response.getWriter().write("'");
  11.270 -                }
  11.271 -                response.getWriter().write("}");
  11.272 -            }
  11.273 -        }, "/data");
  11.274 -
  11.275 -        this.brwsr = launchServerAndBrwsr(server, "/execute");
  11.276 -    }
  11.277 -    
  11.278 -    private static String encodeJSON(String in) {
  11.279 -        StringBuilder sb = new StringBuilder();
  11.280 -        for (int i = 0; i < in.length(); i++) {
  11.281 -            char ch = in.charAt(i);
  11.282 -            if (ch < 32 || ch == '\'' || ch == '"') {
  11.283 -                sb.append("\\u");
  11.284 -                String hs = "0000" + Integer.toHexString(ch);
  11.285 -                hs = hs.substring(hs.length() - 4);
  11.286 -                sb.append(hs);
  11.287 -            } else {
  11.288 -                sb.append(ch);
  11.289 -            }
  11.290 -        }
  11.291 -        return sb.toString();
  11.292 -    }
  11.293 -    
  11.294 -    @Override
  11.295 -    public void shutdown() throws IOException {
  11.296 -        methods.offer(END);
  11.297 -        for (;;) {
  11.298 -            int prev = methods.size();
  11.299 -            try {
  11.300 -                if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) {
  11.301 -                    break;
  11.302 -                }
  11.303 -            } catch (InterruptedException ex) {
  11.304 -                throw new IOException(ex);
  11.305 -            }
  11.306 -            if (prev == methods.size()) {
  11.307 -                LOG.log(
  11.308 -                    Level.WARNING, 
  11.309 -                    "Timeout and no test has been executed meanwhile (at {0}). Giving up.", 
  11.310 -                    methods.size()
  11.311 -                );
  11.312 -                break;
  11.313 -            }
  11.314 -            LOG.log(Level.INFO, 
  11.315 -                "Timeout, but tests got from {0} to {1}. Trying again.", 
  11.316 -                new Object[]{prev, methods.size()}
  11.317 -            );
  11.318 -        }
  11.319 -        stopServerAndBrwsr(server, brwsr);
  11.320 -    }
  11.321 -    
  11.322 -    static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
  11.323 -        for (;;) {
  11.324 -            int ch = is.read();
  11.325 -            if (ch == -1) {
  11.326 -                break;
  11.327 -            }
  11.328 -            if (ch == '$' && params.length > 0) {
  11.329 -                int cnt = is.read() - '0';
  11.330 -                if (baseURL != null && cnt == 'U' - '0') {
  11.331 -                    os.write(baseURL.getBytes("UTF-8"));
  11.332 -                } else {
  11.333 -                    if (cnt >= 0 && cnt < params.length) {
  11.334 -                        os.write(params[cnt].getBytes("UTF-8"));
  11.335 -                    } else {
  11.336 -                        os.write('$');
  11.337 -                        os.write(cnt + '0');
  11.338 -                    }
  11.339 -                }
  11.340 -            } else {
  11.341 -                os.write(ch);
  11.342 -            }
  11.343 -        }
  11.344 -    }
  11.345 -
  11.346 -    private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
  11.347 -        server.start();
  11.348 -        NetworkListener listener = server.getListeners().iterator().next();
  11.349 -        int port = listener.getPort();
  11.350 -        
  11.351 -        URI uri = new URI("http://localhost:" + port + page);
  11.352 -        LOG.log(Level.INFO, "Showing {0}", uri);
  11.353 -        if (cmd == null) {
  11.354 -            try {
  11.355 -                LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] {
  11.356 -                    System.getProperty("java.vm.name"),
  11.357 -                    System.getProperty("java.vm.vendor"),
  11.358 -                    System.getProperty("java.vm.version"),
  11.359 -                });
  11.360 -                java.awt.Desktop.getDesktop().browse(uri);
  11.361 -                LOG.log(Level.INFO, "Desktop.browse successfully finished");
  11.362 -                return null;
  11.363 -            } catch (UnsupportedOperationException ex) {
  11.364 -                LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage());
  11.365 -                LOG.log(Level.FINE, null, ex);
  11.366 -            }
  11.367 -        }
  11.368 -        {
  11.369 -            String cmdName = cmd == null ? "xdg-open" : cmd;
  11.370 -            String[] cmdArr = { 
  11.371 -                cmdName, uri.toString()
  11.372 -            };
  11.373 -            LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr));
  11.374 -            final Process process = Runtime.getRuntime().exec(cmdArr);
  11.375 -            return new Object[] { process, null };
  11.376 -        }
  11.377 -    }
  11.378 -    private static String toUTF8(String value) throws UnsupportedEncodingException {
  11.379 -        byte[] arr = new byte[value.length()];
  11.380 -        for (int i = 0; i < arr.length; i++) {
  11.381 -            arr[i] = (byte)value.charAt(i);
  11.382 -        }
  11.383 -        return new String(arr, "UTF-8");
  11.384 -    }
  11.385 -    
  11.386 -    private static String decodeURL(String s) {
  11.387 -        for (;;) {
  11.388 -            int pos = s.indexOf('%');
  11.389 -            if (pos == -1) {
  11.390 -                return s;
  11.391 -            }
  11.392 -            int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16);
  11.393 -            s = s.substring(0, pos) + (char)i + s.substring(pos + 2);
  11.394 -        }
  11.395 -    }
  11.396 -    
  11.397 -    private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException {
  11.398 -        if (brwsr == null) {
  11.399 -            return;
  11.400 -        }
  11.401 -        Process process = (Process)brwsr[0];
  11.402 -        
  11.403 -        server.stop();
  11.404 -        InputStream stdout = process.getInputStream();
  11.405 -        InputStream stderr = process.getErrorStream();
  11.406 -        drain("StdOut", stdout);
  11.407 -        drain("StdErr", stderr);
  11.408 -        process.destroy();
  11.409 -        int res;
  11.410 -        try {
  11.411 -            res = process.waitFor();
  11.412 -        } catch (InterruptedException ex) {
  11.413 -            throw new IOException(ex);
  11.414 -        }
  11.415 -        LOG.log(Level.INFO, "Exit code: {0}", res);
  11.416 -
  11.417 -        deleteTree((File)brwsr[1]);
  11.418 -    }
  11.419 -    
  11.420 -    private static void drain(String name, InputStream is) throws IOException {
  11.421 -        int av = is.available();
  11.422 -        if (av > 0) {
  11.423 -            StringBuilder sb = new StringBuilder();
  11.424 -            sb.append("v== ").append(name).append(" ==v\n");
  11.425 -            while (av-- > 0) {
  11.426 -                sb.append((char)is.read());
  11.427 -            }
  11.428 -            sb.append("\n^== ").append(name).append(" ==^");
  11.429 -            LOG.log(Level.INFO, sb.toString());
  11.430 -        }
  11.431 -    }
  11.432 -
  11.433 -    private void deleteTree(File file) {
  11.434 -        if (file == null) {
  11.435 -            return;
  11.436 -        }
  11.437 -        File[] arr = file.listFiles();
  11.438 -        if (arr != null) {
  11.439 -            for (File s : arr) {
  11.440 -                deleteTree(s);
  11.441 -            }
  11.442 -        }
  11.443 -        file.delete();
  11.444 -    }
  11.445 -
  11.446 -    @Override
  11.447 -    public void close() throws IOException {
  11.448 -        shutdown();
  11.449 -    }
  11.450 -
  11.451 -    private class Res implements Bck2Brwsr.Resources {
  11.452 -        @Override
  11.453 -        public InputStream get(String resource) throws IOException {
  11.454 -            for (ClassLoader l : loaders) {
  11.455 -                URL u = null;
  11.456 -                Enumeration<URL> en = l.getResources(resource);
  11.457 -                while (en.hasMoreElements()) {
  11.458 -                    u = en.nextElement();
  11.459 -                }
  11.460 -                if (u != null) {
  11.461 -                    return u.openStream();
  11.462 -                }
  11.463 -            }
  11.464 -            throw new IOException("Can't find " + resource);
  11.465 -        }
  11.466 -    }
  11.467 -
  11.468 -    private static class Page extends HttpHandler {
  11.469 -        final String resource;
  11.470 -        private final String[] args;
  11.471 -        private final Res res;
  11.472 -        
  11.473 -        public Page(Res res, String resource, String... args) {
  11.474 -            this.res = res;
  11.475 -            this.resource = resource;
  11.476 -            this.args = args.length == 0 ? new String[] { "$0" } : args;
  11.477 -        }
  11.478 -
  11.479 -        @Override
  11.480 -        public void service(Request request, Response response) throws Exception {
  11.481 -            String r = computePage(request);
  11.482 -            if (r.startsWith("/")) {
  11.483 -                r = r.substring(1);
  11.484 -            }
  11.485 -            String[] replace = {};
  11.486 -            if (r.endsWith(".html")) {
  11.487 -                response.setContentType("text/html");
  11.488 -                LOG.info("Content type text/html");
  11.489 -                replace = args;
  11.490 -            }
  11.491 -            if (r.endsWith(".xhtml")) {
  11.492 -                response.setContentType("application/xhtml+xml");
  11.493 -                LOG.info("Content type application/xhtml+xml");
  11.494 -                replace = args;
  11.495 -            }
  11.496 -            OutputStream os = response.getOutputStream();
  11.497 -            try (InputStream is = res.get(r)) {
  11.498 -                copyStream(is, os, request.getRequestURL().toString(), replace);
  11.499 -            } catch (IOException ex) {
  11.500 -                response.setDetailMessage(ex.getLocalizedMessage());
  11.501 -                response.setError();
  11.502 -                response.setStatus(404);
  11.503 -            }
  11.504 -        }
  11.505 -
  11.506 -        protected String computePage(Request request) {
  11.507 -            String r = resource;
  11.508 -            if (r == null) {
  11.509 -                r = request.getHttpHandlerPath();
  11.510 -            }
  11.511 -            return r;
  11.512 -        }
  11.513 -    }
  11.514 -    
  11.515 -    private static class SubTree extends Page {
  11.516 -
  11.517 -        public SubTree(Res res, String resource, String... args) {
  11.518 -            super(res, resource, args);
  11.519 -        }
  11.520 -
  11.521 -        @Override
  11.522 -        protected String computePage(Request request) {
  11.523 -            return resource + request.getHttpHandlerPath();
  11.524 -        }
  11.525 -        
  11.526 -        
  11.527 -    }
  11.528 -
  11.529 -    private static class VM extends HttpHandler {
  11.530 -        private final String bck2brwsr;
  11.531 -
  11.532 -        public VM(Res loader) throws IOException {
  11.533 -            StringBuilder sb = new StringBuilder();
  11.534 -            Bck2Brwsr.generate(sb, loader);
  11.535 -            sb.append(
  11.536 -                  "(function WrapperVM(global) {"
  11.537 -                + "  function ldCls(res) {\n"
  11.538 -                + "    var request = new XMLHttpRequest();\n"
  11.539 -                + "    request.open('GET', '/classes/' + res, false);\n"
  11.540 -                + "    request.send();\n"
  11.541 -                + "    if (request.status !== 200) return null;\n"
  11.542 -                + "    var arr = eval('(' + request.responseText + ')');\n"
  11.543 -                + "    return arr;\n"
  11.544 -                + "  }\n"
  11.545 -                + "  var prevvm = global.bck2brwsr;\n"
  11.546 -                + "  global.bck2brwsr = function() {\n"
  11.547 -                + "    var args = Array.prototype.slice.apply(arguments);\n"
  11.548 -                + "    args.unshift(ldCls);\n"
  11.549 -                + "    return prevvm.apply(null, args);\n"
  11.550 -                + "  };\n"
  11.551 -                + "})(this);\n"
  11.552 -            );
  11.553 -            this.bck2brwsr = sb.toString();
  11.554 -        }
  11.555 -
  11.556 -        @Override
  11.557 -        public void service(Request request, Response response) throws Exception {
  11.558 -            response.setCharacterEncoding("UTF-8");
  11.559 -            response.setContentType("text/javascript");
  11.560 -            response.getWriter().write(bck2brwsr);
  11.561 -        }
  11.562 -    }
  11.563 -
  11.564 -    private static class Classes extends HttpHandler {
  11.565 -        private final Res loader;
  11.566 -
  11.567 -        public Classes(Res loader) {
  11.568 -            this.loader = loader;
  11.569 -        }
  11.570 -
  11.571 -        @Override
  11.572 -        public void service(Request request, Response response) throws Exception {
  11.573 -            String res = request.getHttpHandlerPath();
  11.574 -            if (res.startsWith("/")) {
  11.575 -                res = res.substring(1);
  11.576 -            }
  11.577 -            try (InputStream is = loader.get(res)) {
  11.578 -                response.setContentType("text/javascript");
  11.579 -                Writer w = response.getWriter();
  11.580 -                w.append("[");
  11.581 -                for (int i = 0;; i++) {
  11.582 -                    int b = is.read();
  11.583 -                    if (b == -1) {
  11.584 -                        break;
  11.585 -                    }
  11.586 -                    if (i > 0) {
  11.587 -                        w.append(", ");
  11.588 -                    }
  11.589 -                    if (i % 20 == 0) {
  11.590 -                        w.write("\n");
  11.591 -                    }
  11.592 -                    if (b > 127) {
  11.593 -                        b = b - 256;
  11.594 -                    }
  11.595 -                    w.append(Integer.toString(b));
  11.596 -                }
  11.597 -                w.append("\n]");
  11.598 -            } catch (IOException ex) {
  11.599 -                response.setStatus(HttpStatus.NOT_FOUND_404);
  11.600 -                response.setError();
  11.601 -                response.setDetailMessage(ex.getMessage());
  11.602 -            }
  11.603 -        }
  11.604 -    }
  11.605 -}
    12.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/InvocationContext.java	Sun Apr 28 09:46:23 2013 +0200
    12.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.3 @@ -1,115 +0,0 @@
    12.4 -/**
    12.5 - * Back 2 Browser Bytecode Translator
    12.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    12.7 - *
    12.8 - * This program is free software: you can redistribute it and/or modify
    12.9 - * it under the terms of the GNU General Public License as published by
   12.10 - * the Free Software Foundation, version 2 of the License.
   12.11 - *
   12.12 - * This program is distributed in the hope that it will be useful,
   12.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
   12.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   12.15 - * GNU General Public License for more details.
   12.16 - *
   12.17 - * You should have received a copy of the GNU General Public License
   12.18 - * along with this program. Look for COPYING file in the top folder.
   12.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
   12.20 - */
   12.21 -package org.apidesign.bck2brwsr.launcher;
   12.22 -
   12.23 -import java.io.IOException;
   12.24 -import java.io.InputStream;
   12.25 -import java.util.ArrayList;
   12.26 -import java.util.List;
   12.27 -import java.util.concurrent.CountDownLatch;
   12.28 -import java.util.concurrent.TimeUnit;
   12.29 -
   12.30 -/** Represents individual method invocation, its context and its result.
   12.31 - *
   12.32 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   12.33 - */
   12.34 -public final class InvocationContext {
   12.35 -    final CountDownLatch wait = new CountDownLatch(1);
   12.36 -    final Class<?> clazz;
   12.37 -    final String methodName;
   12.38 -    private final Launcher launcher;
   12.39 -    private String result;
   12.40 -    private Throwable exception;
   12.41 -    String html;
   12.42 -    final List<Resource> resources = new ArrayList<>();
   12.43 -
   12.44 -    InvocationContext(Launcher launcher, Class<?> clazz, String methodName) {
   12.45 -        this.launcher = launcher;
   12.46 -        this.clazz = clazz;
   12.47 -        this.methodName = methodName;
   12.48 -    }
   12.49 -    
   12.50 -    /** An HTML fragment to be available for the execution. Useful primarily when
   12.51 -     * executing in a browser via {@link Launcher#createBrowser(java.lang.String)}.
   12.52 -     * @param html the html fragment
   12.53 -     */
   12.54 -    public void setHtmlFragment(String html) {
   12.55 -        this.html = html;
   12.56 -    }
   12.57 -    
   12.58 -    /** HTTP resource to be available during execution. An invocation may
   12.59 -     * perform an HTTP query and obtain a resource relative to the page.
   12.60 -     */
   12.61 -    public void addHttpResource(String relativePath, String mimeType, String[] parameters, InputStream content) {
   12.62 -        if (relativePath == null || mimeType == null || content == null || parameters == null) {
   12.63 -            throw new NullPointerException();
   12.64 -        }
   12.65 -        resources.add(new Resource(content, mimeType, relativePath, parameters));
   12.66 -    }
   12.67 -    
   12.68 -    /** Invokes the associated method. 
   12.69 -     * @return the textual result of the invocation
   12.70 -     */
   12.71 -    public String invoke() throws IOException {
   12.72 -        launcher.runMethod(this);
   12.73 -        return toString();
   12.74 -    }
   12.75 -    
   12.76 -    /** Obtains textual result of the invocation.
   12.77 -     * @return text representing the exception or result value
   12.78 -     */
   12.79 -    @Override
   12.80 -    public String toString() {
   12.81 -        if (exception != null) {
   12.82 -            return exception.toString();
   12.83 -        }
   12.84 -        return result;
   12.85 -    }
   12.86 -    
   12.87 -    /**
   12.88 -     * @param timeOut
   12.89 -     * @throws InterruptedException 
   12.90 -     */
   12.91 -    void await(long timeOut) throws InterruptedException {
   12.92 -        wait.await(timeOut, TimeUnit.MILLISECONDS);
   12.93 -    }
   12.94 -    
   12.95 -    void result(String r, Throwable e) {
   12.96 -        this.result = r;
   12.97 -        this.exception = e;
   12.98 -        wait.countDown();
   12.99 -    }
  12.100 -
  12.101 -
  12.102 -    static final class Resource {
  12.103 -        final InputStream httpContent;
  12.104 -        final String httpType;
  12.105 -        final String httpPath;
  12.106 -        final String[] parameters;
  12.107 -
  12.108 -        Resource(InputStream httpContent, String httpType, String httpPath,
  12.109 -            String[] parameters
  12.110 -        ) {
  12.111 -            httpContent.mark(Integer.MAX_VALUE);
  12.112 -            this.httpContent = httpContent;
  12.113 -            this.httpType = httpType;
  12.114 -            this.httpPath = httpPath;
  12.115 -            this.parameters = parameters;
  12.116 -        }
  12.117 -    }
  12.118 -}
    13.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/JSLauncher.java	Sun Apr 28 09:46:23 2013 +0200
    13.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.3 @@ -1,132 +0,0 @@
    13.4 -/**
    13.5 - * Back 2 Browser Bytecode Translator
    13.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    13.7 - *
    13.8 - * This program is free software: you can redistribute it and/or modify
    13.9 - * it under the terms of the GNU General Public License as published by
   13.10 - * the Free Software Foundation, version 2 of the License.
   13.11 - *
   13.12 - * This program is distributed in the hope that it will be useful,
   13.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
   13.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13.15 - * GNU General Public License for more details.
   13.16 - *
   13.17 - * You should have received a copy of the GNU General Public License
   13.18 - * along with this program. Look for COPYING file in the top folder.
   13.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
   13.20 - */
   13.21 -package org.apidesign.bck2brwsr.launcher;
   13.22 -
   13.23 -import org.apidesign.bck2brwsr.launcher.impl.Console;
   13.24 -import java.io.IOException;
   13.25 -import java.io.InputStream;
   13.26 -import java.net.URL;
   13.27 -import java.util.Enumeration;
   13.28 -import java.util.LinkedHashSet;
   13.29 -import java.util.Set;
   13.30 -import java.util.logging.Level;
   13.31 -import java.util.logging.Logger;
   13.32 -import javax.script.Invocable;
   13.33 -import javax.script.ScriptEngine;
   13.34 -import javax.script.ScriptEngineManager;
   13.35 -import javax.script.ScriptException;
   13.36 -import org.apidesign.vm4brwsr.Bck2Brwsr;
   13.37 -
   13.38 -/**
   13.39 - * Tests execution in Java's internal scripting engine.
   13.40 - */
   13.41 -final class JSLauncher extends Launcher {
   13.42 -    private static final Logger LOG = Logger.getLogger(JSLauncher.class.getName());
   13.43 -    private Set<ClassLoader> loaders = new LinkedHashSet<>();
   13.44 -    private final Res resources = new Res();
   13.45 -    private Invocable code;
   13.46 -    private StringBuilder codeSeq;
   13.47 -    private Object console;
   13.48 -    
   13.49 -    
   13.50 -    @Override InvocationContext runMethod(InvocationContext mi) {
   13.51 -        loaders.add(mi.clazz.getClassLoader());
   13.52 -        try {
   13.53 -            long time = System.currentTimeMillis();
   13.54 -            LOG.log(Level.FINE, "Invoking {0}.{1}", new Object[]{mi.clazz.getName(), mi.methodName});
   13.55 -            String res = code.invokeMethod(
   13.56 -                console,
   13.57 -                "invoke__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2",
   13.58 -                mi.clazz.getName(), mi.methodName).toString();
   13.59 -            time = System.currentTimeMillis() - time;
   13.60 -            LOG.log(Level.FINE, "Resut of {0}.{1} = {2} in {3} ms", new Object[]{mi.clazz.getName(), mi.methodName, res, time});
   13.61 -            mi.result(res, null);
   13.62 -        } catch (ScriptException | NoSuchMethodException ex) {
   13.63 -            mi.result(null, ex);
   13.64 -        }
   13.65 -        return mi;
   13.66 -    }
   13.67 -    
   13.68 -    public void addClassLoader(ClassLoader url) {
   13.69 -        this.loaders.add(url);
   13.70 -    }
   13.71 -
   13.72 -    @Override
   13.73 -    public void initialize() throws IOException {
   13.74 -        try {
   13.75 -            initRhino();
   13.76 -        } catch (Exception ex) {
   13.77 -            if (ex instanceof IOException) {
   13.78 -                throw (IOException)ex;
   13.79 -            }
   13.80 -            if (ex instanceof RuntimeException) {
   13.81 -                throw (RuntimeException)ex;
   13.82 -            }
   13.83 -            throw new IOException(ex);
   13.84 -        }
   13.85 -    }
   13.86 -    
   13.87 -    private void initRhino() throws IOException, ScriptException, NoSuchMethodException {
   13.88 -        StringBuilder sb = new StringBuilder();
   13.89 -        Bck2Brwsr.generate(sb, new Res());
   13.90 -
   13.91 -        ScriptEngineManager sem = new ScriptEngineManager();
   13.92 -        ScriptEngine mach = sem.getEngineByExtension("js");
   13.93 -
   13.94 -        sb.append(
   13.95 -              "\nvar vm = new bck2brwsr(org.apidesign.bck2brwsr.launcher.impl.Console.read);"
   13.96 -            + "\nfunction initVM() { return vm; };"
   13.97 -            + "\n");
   13.98 -
   13.99 -        Object res = mach.eval(sb.toString());
  13.100 -        if (!(mach instanceof Invocable)) {
  13.101 -            throw new IOException("It is invocable object: " + res);
  13.102 -        }
  13.103 -        code = (Invocable) mach;
  13.104 -        codeSeq = sb;
  13.105 -        
  13.106 -        Object vm = code.invokeFunction("initVM");
  13.107 -        console = code.invokeMethod(vm, "loadClass", Console.class.getName());
  13.108 -    }
  13.109 -
  13.110 -    @Override
  13.111 -    public void shutdown() throws IOException {
  13.112 -    }
  13.113 -
  13.114 -    @Override
  13.115 -    public String toString() {
  13.116 -        return codeSeq.toString();
  13.117 -    }
  13.118 -    
  13.119 -    private class Res implements Bck2Brwsr.Resources {
  13.120 -        @Override
  13.121 -        public InputStream get(String resource) throws IOException {
  13.122 -            for (ClassLoader l : loaders) {
  13.123 -                URL u = null;
  13.124 -                Enumeration<URL> en = l.getResources(resource);
  13.125 -                while (en.hasMoreElements()) {
  13.126 -                    u = en.nextElement();
  13.127 -                }
  13.128 -                if (u != null) {
  13.129 -                    return u.openStream();
  13.130 -                }
  13.131 -            }
  13.132 -            throw new IOException("Can't find " + resource);
  13.133 -        }
  13.134 -    }
  13.135 -}
    14.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java	Sun Apr 28 09:46:23 2013 +0200
    14.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.3 @@ -1,116 +0,0 @@
    14.4 -/**
    14.5 - * Back 2 Browser Bytecode Translator
    14.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    14.7 - *
    14.8 - * This program is free software: you can redistribute it and/or modify
    14.9 - * it under the terms of the GNU General Public License as published by
   14.10 - * the Free Software Foundation, version 2 of the License.
   14.11 - *
   14.12 - * This program is distributed in the hope that it will be useful,
   14.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   14.15 - * GNU General Public License for more details.
   14.16 - *
   14.17 - * You should have received a copy of the GNU General Public License
   14.18 - * along with this program. Look for COPYING file in the top folder.
   14.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
   14.20 - */
   14.21 -package org.apidesign.bck2brwsr.launcher;
   14.22 -
   14.23 -import java.io.Closeable;
   14.24 -import java.io.File;
   14.25 -import java.io.IOException;
   14.26 -import org.apidesign.vm4brwsr.Bck2Brwsr;
   14.27 -
   14.28 -/** An abstraction for executing tests in a Bck2Brwsr virtual machine.
   14.29 - * Either in {@linkm Launcher#createJavaScript JavaScript engine}, 
   14.30 - * or in {@linkm Launcher#createBrowser external browser}.
   14.31 - * <p>
   14.32 - * There also are methods to {@link #showDir(java.io.File, java.lang.String) display pages} 
   14.33 - * in an external browser served by internal HTTP server.
   14.34 - *
   14.35 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   14.36 - */
   14.37 -public abstract class Launcher {
   14.38 -
   14.39 -    Launcher() {
   14.40 -    }
   14.41 -
   14.42 -    /** Initializes the launcher. This may mean starting a web browser or
   14.43 -     * initializing execution engine.
   14.44 -     * @throws IOException if something goes wrong
   14.45 -     */
   14.46 -    public abstract void initialize() throws IOException;
   14.47 -    
   14.48 -    /** Shuts down the launcher.
   14.49 -     * @throws IOException if something goes wrong
   14.50 -     */
   14.51 -    public abstract void shutdown() throws IOException;
   14.52 -    
   14.53 -    
   14.54 -    /** Builds an invocation context. The context can later be customized
   14.55 -     * and {@link InvocationContext#invoke() invoked}.
   14.56 -     * 
   14.57 -     * @param clazz the class to execute method from
   14.58 -     * @param method the method to execute
   14.59 -     * @return the context pointing to the selected method
   14.60 -     */
   14.61 -    public InvocationContext createInvocation(Class<?> clazz, String method) {
   14.62 -        return new InvocationContext(this, clazz, method);
   14.63 -    }
   14.64 -    
   14.65 -
   14.66 -    /** Creates launcher that uses internal JavaScript engine (Rhino).
   14.67 -     * @return the launcher
   14.68 -     */
   14.69 -    public static Launcher createJavaScript() {
   14.70 -        final JSLauncher l = new JSLauncher();
   14.71 -        l.addClassLoader(Bck2Brwsr.class.getClassLoader());
   14.72 -        return l;
   14.73 -    }
   14.74 -    
   14.75 -    /** Creates launcher that is using external browser.
   14.76 -     * 
   14.77 -     * @param cmd <code>null</code> to use <code>java.awt.Desktop</code> to show the launcher
   14.78 -     *    or a string to execute in an external process (with a parameter to the URL)
   14.79 -     * @return launcher executing in external browser.
   14.80 -     */
   14.81 -    public static Launcher createBrowser(String cmd) {
   14.82 -        final Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(cmd);
   14.83 -        l.addClassLoader(Bck2Brwsr.class.getClassLoader());
   14.84 -        l.setTimeout(180000);
   14.85 -        return l;
   14.86 -    }
   14.87 -    
   14.88 -    /** Starts an HTTP server which provides access to classes and resources
   14.89 -     * available in the <code>classes</code> URL and shows a start page
   14.90 -     * available as {@link ClassLoader#getResource(java.lang.String)} from the
   14.91 -     * provide classloader. Opens a browser with URL showing the start page.
   14.92 -     * 
   14.93 -     * @param classes classloader offering access to classes and resources
   14.94 -     * @param startpage page to show in the browser
   14.95 -     * @return interface that allows one to stop the server
   14.96 -     * @throws IOException if something goes wrong
   14.97 -     */
   14.98 -    public static Closeable showURL(ClassLoader classes, String startpage) throws IOException {
   14.99 -        Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null);
  14.100 -        l.addClassLoader(classes);
  14.101 -        l.showURL(startpage);
  14.102 -        return l;
  14.103 -    }
  14.104 -    /** Starts an HTTP server which provides access to certain directory.
  14.105 -     * The <code>startpage</code> should be relative location inside the root 
  14.106 -     * directory. Opens a browser with URL showing the start page.
  14.107 -     * 
  14.108 -     * @param directory the root directory on disk
  14.109 -     * @param startpage relative path from the root to the page
  14.110 -     * @exception IOException if something goes wrong.
  14.111 -     */
  14.112 -    public static Closeable showDir(File directory, String startpage) throws IOException {
  14.113 -        Bck2BrwsrLauncher l = new Bck2BrwsrLauncher(null);
  14.114 -        l.showDirectory(directory, startpage);
  14.115 -        return l;
  14.116 -    }
  14.117 -
  14.118 -    abstract InvocationContext runMethod(InvocationContext c) throws IOException; 
  14.119 -}
    15.1 --- a/rt/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/impl/Console.java	Sun Apr 28 09:46:23 2013 +0200
    15.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.3 @@ -1,356 +0,0 @@
    15.4 -/**
    15.5 - * Back 2 Browser Bytecode Translator
    15.6 - * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    15.7 - *
    15.8 - * This program is free software: you can redistribute it and/or modify
    15.9 - * it under the terms of the GNU General Public License as published by
   15.10 - * the Free Software Foundation, version 2 of the License.
   15.11 - *
   15.12 - * This program is distributed in the hope that it will be useful,
   15.13 - * but WITHOUT ANY WARRANTY; without even the implied warranty of
   15.14 - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   15.15 - * GNU General Public License for more details.
   15.16 - *
   15.17 - * You should have received a copy of the GNU General Public License
   15.18 - * along with this program. Look for COPYING file in the top folder.
   15.19 - * If not, see http://opensource.org/licenses/GPL-2.0.
   15.20 - */
   15.21 -package org.apidesign.bck2brwsr.launcher.impl;
   15.22 -
   15.23 -import java.io.IOException;
   15.24 -import java.io.InputStream;
   15.25 -import java.io.UnsupportedEncodingException;
   15.26 -import java.lang.reflect.InvocationTargetException;
   15.27 -import java.lang.reflect.Method;
   15.28 -import java.lang.reflect.Modifier;
   15.29 -import java.net.URL;
   15.30 -import java.util.Enumeration;
   15.31 -import org.apidesign.bck2brwsr.core.JavaScriptBody;
   15.32 -
   15.33 -/**
   15.34 - *
   15.35 - * @author Jaroslav Tulach <jtulach@netbeans.org>
   15.36 - */
   15.37 -public class Console {
   15.38 -    private Console() {
   15.39 -    }
   15.40 -    static {
   15.41 -        turnAssetionStatusOn();
   15.42 -    }
   15.43 -    
   15.44 -    @JavaScriptBody(args = {"id", "attr"}, body = 
   15.45 -        "return window.document.getElementById(id)[attr].toString();")
   15.46 -    private static native Object getAttr(String id, String attr);
   15.47 -    @JavaScriptBody(args = {"elem", "attr"}, body = 
   15.48 -        "return elem[attr].toString();")
   15.49 -    private static native Object getAttr(Object elem, String attr);
   15.50 -
   15.51 -    @JavaScriptBody(args = {"id", "attr", "value"}, body = 
   15.52 -        "window.document.getElementById(id)[attr] = value;")
   15.53 -    private static native void setAttr(String id, String attr, Object value);
   15.54 -    @JavaScriptBody(args = {"elem", "attr", "value"}, body = 
   15.55 -        "elem[attr] = value;")
   15.56 -    private static native void setAttr(Object id, String attr, Object value);
   15.57 -    
   15.58 -    @JavaScriptBody(args = {}, body = "return; window.close();")
   15.59 -    private static native void closeWindow();
   15.60 -
   15.61 -    private static Object textArea;
   15.62 -    private static Object statusArea;
   15.63 -    
   15.64 -    private static void log(String newText) {
   15.65 -        if (textArea == null) {
   15.66 -            return;
   15.67 -        }
   15.68 -        String attr = "value";
   15.69 -        setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText);
   15.70 -        setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight"));
   15.71 -    }
   15.72 -    
   15.73 -    private static void beginTest(Case c) {
   15.74 -        Object[] arr = new Object[2];
   15.75 -        beginTest(c.getClassName() + "." + c.getMethodName(), c, arr);
   15.76 -        textArea = arr[0];
   15.77 -        statusArea = arr[1];
   15.78 -    }
   15.79 -    
   15.80 -    private static void finishTest(Case c, Object res) {
   15.81 -        if ("null".equals(res)) {
   15.82 -            setAttr(statusArea, "innerHTML", "Success");
   15.83 -        } else {
   15.84 -            setAttr(statusArea, "innerHTML", "Result " + res);
   15.85 -        }
   15.86 -        statusArea = null;
   15.87 -        textArea = null;
   15.88 -    }
   15.89 -
   15.90 -    @JavaScriptBody(args = { "test", "c", "arr" }, body = 
   15.91 -          "var ul = window.document.getElementById('bck2brwsr.result');\n"
   15.92 -        + "var li = window.document.createElement('li');\n"
   15.93 -        + "var span = window.document.createElement('span');"
   15.94 -        + "span.innerHTML = test + ' - ';\n"
   15.95 -        + "var details = window.document.createElement('a');\n"
   15.96 -        + "details.innerHTML = 'Details';\n"
   15.97 -        + "details.href = '#';\n"
   15.98 -        + "var p = window.document.createElement('p');\n"
   15.99 -        + "var status = window.document.createElement('a');\n"
  15.100 -        + "status.innerHTML = 'running';"
  15.101 -        + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n"
  15.102 -        + "status.onclick = function() { c.again__V_3Ljava_lang_Object_2(arr); }\n"
  15.103 -        + "var pre = window.document.createElement('textarea');\n"
  15.104 -        + "pre.cols = 100;"
  15.105 -        + "pre.rows = 10;"
  15.106 -        + "li.appendChild(span);\n"
  15.107 -        + "li.appendChild(status);\n"
  15.108 -        + "var span = window.document.createElement('span');"
  15.109 -        + "span.innerHTML = ' ';\n"
  15.110 -        + "li.appendChild(span);\n"
  15.111 -        + "li.appendChild(details);\n"
  15.112 -        + "p.appendChild(pre);\n"
  15.113 -        + "ul.appendChild(li);\n"
  15.114 -        + "arr[0] = pre;\n"
  15.115 -        + "arr[1] = status;\n"
  15.116 -    )
  15.117 -    private static native void beginTest(String test, Case c, Object[] arr);
  15.118 -    
  15.119 -    @JavaScriptBody(args = { "url", "callback", "arr" }, body = ""
  15.120 -        + "var request = new XMLHttpRequest();\n"
  15.121 -        + "request.open('GET', url, true);\n"
  15.122 -        + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
  15.123 -        + "request.onreadystatechange = function() {\n"
  15.124 -        + "  if (this.readyState!==4) return;\n"
  15.125 -        + "  arr[0] = this.responseText;\n"
  15.126 -        + "  callback.run__V();\n"
  15.127 -        + "};"
  15.128 -        + "request.send();"
  15.129 -    )
  15.130 -    private static native void loadText(String url, Runnable callback, String[] arr) throws IOException;
  15.131 -    
  15.132 -    public static void harness(String url) throws IOException {
  15.133 -        log("Connecting to " + url);
  15.134 -        Request r = new Request(url);
  15.135 -    }
  15.136 -    
  15.137 -    private static class Request implements Runnable {
  15.138 -        private final String[] arr = { null };
  15.139 -        private final String url;
  15.140 -        private Case c;
  15.141 -        private int retries;
  15.142 -
  15.143 -        private Request(String url) throws IOException {
  15.144 -            this.url = url;
  15.145 -            loadText(url, this, arr);
  15.146 -        }
  15.147 -        private Request(String url, String u) throws IOException {
  15.148 -            this.url = url;
  15.149 -            loadText(u, this, arr);
  15.150 -        }
  15.151 -        
  15.152 -        @Override
  15.153 -        public void run() {
  15.154 -            try {
  15.155 -                if (c == null) {
  15.156 -                    String data = arr[0];
  15.157 -
  15.158 -                    if (data == null) {
  15.159 -                        log("Some error exiting");
  15.160 -                        closeWindow();
  15.161 -                        return;
  15.162 -                    }
  15.163 -
  15.164 -                    if (data.isEmpty()) {
  15.165 -                        log("No data, exiting");
  15.166 -                        closeWindow();
  15.167 -                        return;
  15.168 -                    }
  15.169 -
  15.170 -                    c = Case.parseData(data);
  15.171 -                    beginTest(c);
  15.172 -                    log("Got \"" + data + "\"");
  15.173 -                } else {
  15.174 -                    log("Processing \"" + arr[0] + "\" for " + retries + " time");
  15.175 -                }
  15.176 -                Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest();
  15.177 -                finishTest(c, result);
  15.178 -                
  15.179 -                String u = url + "?request=" + c.getRequestId() + "&result=" + result;
  15.180 -                new Request(url, u);
  15.181 -            } catch (Exception ex) {
  15.182 -                if (ex instanceof InterruptedException) {
  15.183 -                    log("Re-scheduling in 100ms");
  15.184 -                    schedule(this, 100);
  15.185 -                    return;
  15.186 -                }
  15.187 -                log(ex.getClass().getName() + ":" + ex.getMessage());
  15.188 -            }
  15.189 -        }
  15.190 -    }
  15.191 -    
  15.192 -    private static String encodeURL(String r) throws UnsupportedEncodingException {
  15.193 -        final String SPECIAL = "%$&+,/:;=?@";
  15.194 -        StringBuilder sb = new StringBuilder();
  15.195 -        byte[] utf8 = r.getBytes("UTF-8");
  15.196 -        for (int i = 0; i < utf8.length; i++) {
  15.197 -            int ch = utf8[i] & 0xff;
  15.198 -            if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) {
  15.199 -                final String numbers = "0" + Integer.toHexString(ch);
  15.200 -                sb.append("%").append(numbers.substring(numbers.length() - 2));
  15.201 -            } else {
  15.202 -                if (ch == 32) {
  15.203 -                    sb.append("+");
  15.204 -                } else {
  15.205 -                    sb.append((char)ch);
  15.206 -                }
  15.207 -            }
  15.208 -        }
  15.209 -        return sb.toString();
  15.210 -    }
  15.211 -    
  15.212 -    static String invoke(String clazz, String method) throws 
  15.213 -    ClassNotFoundException, InvocationTargetException, IllegalAccessException, 
  15.214 -    InstantiationException, InterruptedException {
  15.215 -        final Object r = new Case(null).invokeMethod(clazz, method);
  15.216 -        return r == null ? "null" : r.toString().toString();
  15.217 -    }
  15.218 -
  15.219 -    /** Helper method that inspects the classpath and loads given resource
  15.220 -     * (usually a class file). Used while running tests in Rhino.
  15.221 -     * 
  15.222 -     * @param name resource name to find
  15.223 -     * @return the array of bytes in the given resource
  15.224 -     * @throws IOException I/O in case something goes wrong
  15.225 -     */
  15.226 -    public static byte[] read(String name) throws IOException {
  15.227 -        URL u = null;
  15.228 -        Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
  15.229 -        while (en.hasMoreElements()) {
  15.230 -            u = en.nextElement();
  15.231 -        }
  15.232 -        if (u == null) {
  15.233 -            throw new IOException("Can't find " + name);
  15.234 -        }
  15.235 -        try (InputStream is = u.openStream()) {
  15.236 -            byte[] arr;
  15.237 -            arr = new byte[is.available()];
  15.238 -            int offset = 0;
  15.239 -            while (offset < arr.length) {
  15.240 -                int len = is.read(arr, offset, arr.length - offset);
  15.241 -                if (len == -1) {
  15.242 -                    throw new IOException("Can't read " + name);
  15.243 -                }
  15.244 -                offset += len;
  15.245 -            }
  15.246 -            return arr;
  15.247 -        }
  15.248 -    }
  15.249 -   
  15.250 -    @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;")
  15.251 -    private static void turnAssetionStatusOn() {
  15.252 -    }
  15.253 -
  15.254 -    @JavaScriptBody(args = {"r", "time"}, body =
  15.255 -        "return window.setTimeout(function() { r.run__V(); }, time);")
  15.256 -    private static native Object schedule(Runnable r, int time);
  15.257 -    
  15.258 -    private static final class Case {
  15.259 -        private final Object data;
  15.260 -        private Object inst;
  15.261 -
  15.262 -        private Case(Object data) {
  15.263 -            this.data = data;
  15.264 -        }
  15.265 -        
  15.266 -        public static Case parseData(String s) {
  15.267 -            return new Case(toJSON(s));
  15.268 -        }
  15.269 -        
  15.270 -        public String getMethodName() {
  15.271 -            return value("methodName", data);
  15.272 -        }
  15.273 -
  15.274 -        public String getClassName() {
  15.275 -            return value("className", data);
  15.276 -        }
  15.277 -        
  15.278 -        public String getRequestId() {
  15.279 -            return value("request", data);
  15.280 -        }
  15.281 -
  15.282 -        public String getHtmlFragment() {
  15.283 -            return value("html", data);
  15.284 -        }
  15.285 -        
  15.286 -        void again(Object[] arr) {
  15.287 -            try {
  15.288 -                textArea = arr[0];
  15.289 -                statusArea = arr[1];
  15.290 -                setAttr(textArea, "value", "");
  15.291 -                runTest();
  15.292 -            } catch (Exception ex) {
  15.293 -                log(ex.getClass().getName() + ":" + ex.getMessage());
  15.294 -            }
  15.295 -        }
  15.296 -
  15.297 -        private Object runTest() throws IllegalAccessException, 
  15.298 -        IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException, 
  15.299 -        InvocationTargetException, InstantiationException, InterruptedException {
  15.300 -            if (this.getHtmlFragment() != null) {
  15.301 -                setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment());
  15.302 -            }
  15.303 -            log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId());
  15.304 -            Object result = invokeMethod(this.getClassName(), this.getMethodName());
  15.305 -            setAttr("bck2brwsr.fragment", "innerHTML", "");
  15.306 -            log("Result: " + result);
  15.307 -            result = encodeURL("" + result);
  15.308 -            log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
  15.309 -            return result;
  15.310 -        }
  15.311 -
  15.312 -        private Object invokeMethod(String clazz, String method)
  15.313 -        throws ClassNotFoundException, InvocationTargetException,
  15.314 -        InterruptedException, IllegalAccessException, IllegalArgumentException,
  15.315 -        InstantiationException {
  15.316 -            Method found = null;
  15.317 -            Class<?> c = Class.forName(clazz);
  15.318 -            for (Method m : c.getMethods()) {
  15.319 -                if (m.getName().equals(method)) {
  15.320 -                    found = m;
  15.321 -                }
  15.322 -            }
  15.323 -            Object res;
  15.324 -            if (found != null) {
  15.325 -                try {
  15.326 -                    if ((found.getModifiers() & Modifier.STATIC) != 0) {
  15.327 -                        res = found.invoke(null);
  15.328 -                    } else {
  15.329 -                        if (inst == null) {
  15.330 -                            inst = c.newInstance();
  15.331 -                        }
  15.332 -                        res = found.invoke(inst);
  15.333 -                    }
  15.334 -                } catch (Throwable ex) {
  15.335 -                    if (ex instanceof InvocationTargetException) {
  15.336 -                        ex = ((InvocationTargetException) ex).getTargetException();
  15.337 -                    }
  15.338 -                    if (ex instanceof InterruptedException) {
  15.339 -                        throw (InterruptedException)ex;
  15.340 -                    }
  15.341 -                    res = ex.getClass().getName() + ":" + ex.getMessage();
  15.342 -                }
  15.343 -            } else {
  15.344 -                res = "Can't find method " + method + " in " + clazz;
  15.345 -            }
  15.346 -            return res;
  15.347 -        }
  15.348 -        
  15.349 -        @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');")
  15.350 -        private static native Object toJSON(String s);
  15.351 -        
  15.352 -        @JavaScriptBody(args = {"p", "d"}, body = 
  15.353 -              "var v = d[p];\n"
  15.354 -            + "if (typeof v === 'undefined') return null;\n"
  15.355 -            + "return v.toString();"
  15.356 -        )
  15.357 -        private static native String value(String p, Object d);
  15.358 -    }
  15.359 -}
    16.1 --- a/rt/launcher/src/main/resources/org/apidesign/bck2brwsr/launcher/harness.xhtml	Sun Apr 28 09:46:23 2013 +0200
    16.2 +++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.3 @@ -1,43 +0,0 @@
    16.4 -<?xml version="1.0" encoding="UTF-8"?>
    16.5 -<!--
    16.6 -
    16.7 -    Back 2 Browser Bytecode Translator
    16.8 -    Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
    16.9 -
   16.10 -    This program is free software: you can redistribute it and/or modify
   16.11 -    it under the terms of the GNU General Public License as published by
   16.12 -    the Free Software Foundation, version 2 of the License.
   16.13 -
   16.14 -    This program is distributed in the hope that it will be useful,
   16.15 -    but WITHOUT ANY WARRANTY; without even the implied warranty of
   16.16 -    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   16.17 -    GNU General Public License for more details.
   16.18 -
   16.19 -    You should have received a copy of the GNU General Public License
   16.20 -    along with this program. Look for COPYING file in the top folder.
   16.21 -    If not, see http://opensource.org/licenses/GPL-2.0.
   16.22 -
   16.23 --->
   16.24 -<!DOCTYPE html>
   16.25 -<html xmlns="http://www.w3.org/1999/xhtml">
   16.26 -    <head>
   16.27 -        <title>Bck2Brwsr Harness</title>
   16.28 -    </head>
   16.29 -    <body>
   16.30 -        <script src="/bck2brwsr.js"></script>
   16.31 -        <script>
   16.32 -            var vm = bck2brwsr();
   16.33 -        </script>
   16.34 -        
   16.35 -        <h1>Bck2Brwsr Execution Harness</h1>
   16.36 -        
   16.37 -        <ul id="bck2brwsr.result" style="width: 100%;" >
   16.38 -        </ul>
   16.39 -
   16.40 -        <div id="bck2brwsr.fragment"/>
   16.41 -        
   16.42 -        <script type="text/javascript">
   16.43 -            vm.loadClass('org.apidesign.bck2brwsr.launcher.impl.Console').harness__VLjava_lang_String_2('$U/../data');
   16.44 -        </script>
   16.45 -    </body>
   16.46 -</html>
    17.1 --- a/rt/pom.xml	Sun Apr 28 09:46:23 2013 +0200
    17.2 +++ b/rt/pom.xml	Sun Apr 28 10:14:31 2013 +0200
    17.3 @@ -14,7 +14,6 @@
    17.4    <modules>
    17.5      <module>core</module>
    17.6      <module>emul</module>
    17.7 -    <module>launcher</module>
    17.8      <module>archetype</module>
    17.9      <module>mojo</module>
   17.10      <module>vm</module>