launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Tue, 18 Dec 2012 13:10:56 +0100
branchlauncher
changeset 349 3fe5a86bd123
parent 348 4e9d576780ca
child 356 e078953818d2
permissions -rw-r--r--
Can execute tests in real browser
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.launcher;
    19 
    20 import java.awt.Desktop;
    21 import java.io.IOException;
    22 import java.io.InputStream;
    23 import java.io.OutputStream;
    24 import java.io.Writer;
    25 import java.net.URI;
    26 import java.net.URISyntaxException;
    27 import java.net.URL;
    28 import java.util.ArrayList;
    29 import java.util.Enumeration;
    30 import java.util.LinkedHashSet;
    31 import java.util.List;
    32 import java.util.Set;
    33 import java.util.concurrent.CountDownLatch;
    34 import java.util.concurrent.TimeUnit;
    35 import static org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher.copyStream;
    36 import org.apidesign.vm4brwsr.Bck2Brwsr;
    37 import org.glassfish.grizzly.PortRange;
    38 import org.glassfish.grizzly.http.server.HttpHandler;
    39 import org.glassfish.grizzly.http.server.HttpServer;
    40 import org.glassfish.grizzly.http.server.NetworkListener;
    41 import org.glassfish.grizzly.http.server.Request;
    42 import org.glassfish.grizzly.http.server.Response;
    43 import org.glassfish.grizzly.http.server.ServerConfiguration;
    44 
    45 /**
    46  * Lightweight server to launch Bck2Brwsr applications in real browser.
    47  */
    48 public class Bck2BrwsrLauncher {
    49     private Set<ClassLoader> loaders = new LinkedHashSet<>();
    50     private List<MethodInvocation> methods = new ArrayList<>();
    51     private long timeOut;
    52     
    53     
    54     public MethodInvocation addMethod(Class<?> clazz, String method) {
    55         loaders.add(clazz.getClassLoader());
    56         MethodInvocation c = new MethodInvocation(clazz.getName(), method);
    57         methods.add(c);
    58         return c;
    59     }
    60     
    61     public void setTimeout(long ms) {
    62         timeOut = ms;
    63     }
    64     
    65     public static void main( String[] args ) throws Exception {
    66         Bck2BrwsrLauncher l = new Bck2BrwsrLauncher();
    67         
    68         final MethodInvocation[] cases = { 
    69             l.addMethod(Console.class, "welcome"),
    70             l.addMethod(Console.class, "multiply"),
    71         };
    72         
    73         l.execute();
    74         
    75         for (MethodInvocation c : cases) {
    76             System.err.println(c.className + "." + c.methodName + " = " + c.result);
    77         }
    78     }
    79 
    80 
    81     public void execute() throws URISyntaxException, IOException, InterruptedException {
    82         final CountDownLatch wait = new CountDownLatch(1);
    83         final MethodInvocation[] cases = this.methods.toArray(new MethodInvocation[0]);
    84         
    85         HttpServer server = HttpServer.createSimpleServer(".", new PortRange(8080, 65535));
    86         
    87         Res resources = new Res();
    88         
    89         final ServerConfiguration conf = server.getServerConfiguration();
    90         conf.addHttpHandler(new Page("console.xhtml", 
    91             "org.apidesign.bck2brwsr.launcher.Console", "welcome", "false"
    92         ), "/console");
    93         conf.addHttpHandler(new VM(resources), "/bck2brwsr.js");
    94         conf.addHttpHandler(new VMInit(), "/vm.js");
    95         conf.addHttpHandler(new Classes(resources), "/classes/");
    96         conf.addHttpHandler(new HttpHandler() {
    97             int cnt;
    98             @Override
    99             public void service(Request request, Response response) throws Exception {
   100                 String id = request.getParameter("request");
   101                 String value = request.getParameter("result");
   102                 if (id != null && value != null) {
   103                     value = value.replace("%20", " ");
   104                     cases[Integer.parseInt(id)].result = value;
   105                 }
   106                 
   107                 if (cnt >= cases.length) {
   108                     response.getWriter().write("");
   109                     wait.countDown();
   110                     cnt = 0;
   111                     return;
   112                 }
   113                 
   114                 response.getWriter().write("{"
   115                     + "className: '" + cases[cnt].className + "', "
   116                     + "methodName: '" + cases[cnt].methodName + "', "
   117                     + "request: " + cnt
   118                     + "}");
   119                 cnt++;
   120             }
   121         }, "/data");
   122         conf.addHttpHandler(new Page("harness.xhtml"), "/");
   123         
   124         server.start();
   125         NetworkListener listener = server.getListeners().iterator().next();
   126         int port = listener.getPort();
   127         
   128         URI uri = new URI("http://localhost:" + port + "/execute");
   129         try {
   130             Desktop.getDesktop().browse(uri);
   131         } catch (UnsupportedOperationException ex) {
   132             String[] cmd = { 
   133                 "xdg-open", uri.toString()
   134             };
   135             Runtime.getRuntime().exec(cmd).waitFor();
   136         }
   137         
   138         wait.await(timeOut, TimeUnit.MILLISECONDS);
   139         server.stop();
   140     }
   141     
   142     static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
   143         for (;;) {
   144             int ch = is.read();
   145             if (ch == -1) {
   146                 break;
   147             }
   148             if (ch == '$') {
   149                 int cnt = is.read() - '0';
   150                 if (cnt == 'U' - '0') {
   151                     os.write(baseURL.getBytes());
   152                 }
   153                 if (cnt < params.length) {
   154                     os.write(params[cnt].getBytes());
   155                 }
   156             } else {
   157                 os.write(ch);
   158             }
   159         }
   160     }
   161     
   162     private class Res implements Bck2Brwsr.Resources {
   163         @Override
   164         public InputStream get(String resource) throws IOException {
   165             for (ClassLoader l : loaders) {
   166                 URL u = null;
   167                 Enumeration<URL> en = l.getResources(resource);
   168                 while (en.hasMoreElements()) {
   169                     u = en.nextElement();
   170                 }
   171                 if (u != null) {
   172                     return u.openStream();
   173                 }
   174             }
   175             throw new IOException("Can't find " + resource);
   176         }
   177     }
   178 
   179     private static class Page extends HttpHandler {
   180         private final String resource;
   181         private final String[] args;
   182         
   183         public Page(String resource, String... args) {
   184             this.resource = resource;
   185             this.args = args;
   186         }
   187 
   188         @Override
   189         public void service(Request request, Response response) throws Exception {
   190             response.setContentType("text/html");
   191             OutputStream os = response.getOutputStream();
   192             InputStream is = Bck2BrwsrLauncher.class.getResourceAsStream(resource);
   193             copyStream(is, os, request.getRequestURL().toString(), args);
   194         }
   195     }
   196 
   197     private static class VM extends HttpHandler {
   198         private final Res loader;
   199 
   200         public VM(Res loader) {
   201             this.loader = loader;
   202         }
   203 
   204         @Override
   205         public void service(Request request, Response response) throws Exception {
   206             response.setCharacterEncoding("UTF-8");
   207             response.setContentType("text/javascript");
   208             Bck2Brwsr.generate(response.getWriter(), loader);
   209         }
   210     }
   211     private static class VMInit extends HttpHandler {
   212         public VMInit() {
   213         }
   214 
   215         @Override
   216         public void service(Request request, Response response) throws Exception {
   217             response.setCharacterEncoding("UTF-8");
   218             response.setContentType("text/javascript");
   219             response.getWriter().append(
   220                 "function ldCls(res) {\n"
   221                 + "  var request = new XMLHttpRequest();\n"
   222                 + "  request.open('GET', 'classes/' + res, false);\n"
   223                 + "  request.send();\n"
   224                 + "  var arr = eval('(' + request.responseText + ')');\n"
   225                 + "  return arr;\n"
   226                 + "}\n"
   227                 + "var vm = new bck2brwsr(ldCls);\n");
   228         }
   229     }
   230 
   231     private static class Classes extends HttpHandler {
   232         private final Res loader;
   233 
   234         public Classes(Res loader) {
   235             this.loader = loader;
   236         }
   237 
   238         @Override
   239         public void service(Request request, Response response) throws Exception {
   240             String res = request.getHttpHandlerPath();
   241             if (res.startsWith("/")) {
   242                 res = res.substring(1);
   243             }
   244             try (InputStream is = loader.get(res)) {
   245                 response.setContentType("text/javascript");
   246                 Writer w = response.getWriter();
   247                 w.append("[");
   248                 for (int i = 0;; i++) {
   249                     int b = is.read();
   250                     if (b == -1) {
   251                         break;
   252                     }
   253                     if (i > 0) {
   254                         w.append(", ");
   255                     }
   256                     if (i % 20 == 0) {
   257                         w.write("\n");
   258                     }
   259                     if (b > 127) {
   260                         b = b - 256;
   261                     }
   262                     w.append(Integer.toString(b));
   263                 }
   264                 w.append("\n]");
   265             } catch (IOException ex) {
   266                 response.setError();
   267                 response.setDetailMessage(ex.getMessage());
   268             }
   269         }
   270     }
   271     
   272     public static final class MethodInvocation {
   273         final String className;
   274         final String methodName;
   275         String result;
   276 
   277         MethodInvocation(String className, String methodName) {
   278             this.className = className;
   279             this.methodName = methodName;
   280         }
   281 
   282         @Override
   283         public String toString() {
   284             return result;
   285         }
   286     }
   287 }