Mojo for execution in a browser launcher
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Thu, 20 Dec 2012 16:17:31 +0100
branchlauncher
changeset 357dc375a56fd15
parent 356 e078953818d2
child 358 f6a165f7f00f
Mojo for execution in a browser
javaquery/demo-calculator/nbactions.xml
javaquery/demo-calculator/pom.xml
javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml
launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java
mojo/pom.xml
mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java
     1.1 --- a/javaquery/demo-calculator/nbactions.xml	Thu Dec 20 11:03:34 2012 +0100
     1.2 +++ b/javaquery/demo-calculator/nbactions.xml	Thu Dec 20 16:17:31 2012 +0100
     1.3 @@ -23,7 +23,7 @@
     1.4              <actionName>run</actionName>
     1.5              <goals>
     1.6                  <goal>process-classes</goal>
     1.7 -                <goal>org.codehaus.mojo:exec-maven-plugin:1.2.1:exec</goal>
     1.8 +                <goal>org.apidesign.bck2brwsr:mojo:0.3-SNAPSHOT:brwsr</goal>
     1.9              </goals>
    1.10          </action>
    1.11      </actions>
     2.1 --- a/javaquery/demo-calculator/pom.xml	Thu Dec 20 11:03:34 2012 +0100
     2.2 +++ b/javaquery/demo-calculator/pom.xml	Thu Dec 20 16:17:31 2012 +0100
     2.3 @@ -16,34 +16,19 @@
     2.4    </properties>
     2.5    <build>
     2.6        <plugins>
     2.7 -          <plugin>
     2.8 -              <groupId>org.apidesign.bck2brwsr</groupId>
     2.9 -              <artifactId>mojo</artifactId>
    2.10 -              <version>0.3-SNAPSHOT</version>
    2.11 -              <executions>
    2.12 -                  <execution>
    2.13 -                      <goals>
    2.14 -                          <goal>j2js</goal>
    2.15 -                      </goals>
    2.16 -                  </execution>
    2.17 -              </executions>
    2.18 -          </plugin>
    2.19              <plugin>
    2.20 -                <groupId>org.codehaus.mojo</groupId>
    2.21 -                <artifactId>exec-maven-plugin</artifactId>
    2.22 -                <version>1.2.1</version>
    2.23 +                <groupId>org.apidesign.bck2brwsr</groupId>
    2.24 +                <artifactId>mojo</artifactId>
    2.25 +                <version>0.3-SNAPSHOT</version>
    2.26                  <executions>
    2.27                      <execution>
    2.28                          <goals>
    2.29 -                            <goal>exec</goal>
    2.30 +                            <goal>brwsr</goal>
    2.31                          </goals>
    2.32                      </execution>
    2.33                  </executions>
    2.34                  <configuration>
    2.35 -                    <executable>xdg-open</executable>
    2.36 -                    <arguments>
    2.37 -                        <argument>${project.build.directory}/classes/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml</argument>
    2.38 -                    </arguments>
    2.39 +                    <startpage>org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml</startpage>
    2.40                  </configuration>
    2.41              </plugin>
    2.42           <plugin>
     3.1 --- a/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml	Thu Dec 20 11:03:34 2012 +0100
     3.2 +++ b/javaquery/demo-calculator/src/main/resources/org/apidesign/bck2brwsr/mavenhtml/Calculator.xhtml	Thu Dec 20 16:17:31 2012 +0100
     3.3 @@ -77,7 +77,11 @@
     3.4              </tbody>
     3.5          </table>
     3.6  
     3.7 -        <script src="bootjava.js"/>
     3.8 +        <script src="/bck2brwsr.js"></script>
     3.9 +        <script src="/vm.js"></script>
    3.10 +        <script type="text/javascript">
    3.11 +            vm.loadClass('org.apidesign.bck2brwsr.mavenhtml.Calculator').class__V();
    3.12 +        </script>
    3.13          
    3.14          <hr/>
    3.15      <pre>
     4.1 --- a/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java	Thu Dec 20 11:03:34 2012 +0100
     4.2 +++ b/launcher/src/main/java/org/apidesign/bck2brwsr/launcher/Bck2BrwsrLauncher.java	Thu Dec 20 16:17:31 2012 +0100
     4.3 @@ -33,8 +33,6 @@
     4.4  import java.util.Set;
     4.5  import java.util.concurrent.CountDownLatch;
     4.6  import java.util.concurrent.TimeUnit;
     4.7 -import java.util.logging.Level;
     4.8 -import java.util.logging.Logger;
     4.9  import javax.script.Invocable;
    4.10  import javax.script.ScriptEngine;
    4.11  import javax.script.ScriptEngineManager;
    4.12 @@ -50,13 +48,17 @@
    4.13  import org.glassfish.grizzly.http.server.ServerConfiguration;
    4.14  
    4.15  /**
    4.16 - * Lightweight server to launch Bck2Brwsr applications in real browser.
    4.17 + * Lightweight server to launch Bck2Brwsr applications and tests.
    4.18 + * Supports execution in native browser as well as Java's internal 
    4.19 + * execution engine.
    4.20   */
    4.21  public class Bck2BrwsrLauncher {
    4.22      private Set<ClassLoader> loaders = new LinkedHashSet<>();
    4.23      private List<MethodInvocation> methods = new ArrayList<>();
    4.24      private long timeOut;
    4.25      private String sen;
    4.26 +    private String showURL;
    4.27 +    private final Res resources = new Res();
    4.28      
    4.29      
    4.30      public MethodInvocation addMethod(Class<?> clazz, String method) {
    4.31 @@ -73,20 +75,24 @@
    4.32      public void setScriptEngineName(String sen) {
    4.33          this.sen = sen;
    4.34      }
    4.35 +
    4.36 +    public void setStartPage(String startpage) {
    4.37 +        if (!startpage.startsWith("/")) {
    4.38 +            startpage = "/" + startpage;
    4.39 +        }
    4.40 +        this.showURL = startpage;
    4.41 +    }
    4.42 +
    4.43 +    public void addClassLoader(ClassLoader url) {
    4.44 +        this.loaders.add(url);
    4.45 +    }
    4.46      
    4.47      public static void main( String[] args ) throws Exception {
    4.48          Bck2BrwsrLauncher l = new Bck2BrwsrLauncher();
    4.49 -        
    4.50 -        final MethodInvocation[] cases = { 
    4.51 -            l.addMethod(Console.class, "welcome"),
    4.52 -            l.addMethod(Console.class, "multiply"),
    4.53 -        };
    4.54 -        
    4.55 +        l.setStartPage("org/apidesign/bck2brwsr/launcher/console.xhtml");
    4.56 +        l.addClassLoader(Bck2BrwsrLauncher.class.getClassLoader());
    4.57          l.execute();
    4.58 -        
    4.59 -        for (MethodInvocation c : cases) {
    4.60 -            System.err.println(c.className + "." + c.methodName + " = " + c.result);
    4.61 -        }
    4.62 +        System.in.read();
    4.63      }
    4.64  
    4.65  
    4.66 @@ -94,6 +100,10 @@
    4.67          try {
    4.68              if (sen != null) {
    4.69                  executeRhino();
    4.70 +            } else if (showURL != null) {
    4.71 +                HttpServer server = initServer();
    4.72 +                server.getServerConfiguration().addHttpHandler(new Page(resources, null), "/");
    4.73 +                launchServerAndBrwsr(server, showURL);
    4.74              } else {
    4.75                  executeInBrowser();
    4.76              }
    4.77 @@ -142,21 +152,29 @@
    4.78          }
    4.79      }
    4.80      
    4.81 -    private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
    4.82 -        final CountDownLatch wait = new CountDownLatch(1);
    4.83 -        final MethodInvocation[] cases = this.methods.toArray(new MethodInvocation[0]);
    4.84 -        
    4.85 +    private HttpServer initServer() {
    4.86          HttpServer server = HttpServer.createSimpleServer(".", new PortRange(8080, 65535));
    4.87 -        
    4.88 -        Res resources = new Res();
    4.89 -        
    4.90 +
    4.91          final ServerConfiguration conf = server.getServerConfiguration();
    4.92 -        conf.addHttpHandler(new Page("console.xhtml", 
    4.93 +        conf.addHttpHandler(new Page(resources, 
    4.94 +            "org/apidesign/bck2brwsr/launcher/console.xhtml",
    4.95              "org.apidesign.bck2brwsr.launcher.Console", "welcome", "false"
    4.96          ), "/console");
    4.97          conf.addHttpHandler(new VM(resources), "/bck2brwsr.js");
    4.98          conf.addHttpHandler(new VMInit(), "/vm.js");
    4.99          conf.addHttpHandler(new Classes(resources), "/classes/");
   4.100 +        return server;
   4.101 +    }
   4.102 +    
   4.103 +    private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
   4.104 +        final CountDownLatch wait = new CountDownLatch(1);
   4.105 +        final MethodInvocation[] cases = this.methods.toArray(new MethodInvocation[0]);
   4.106 +        
   4.107 +        HttpServer server = initServer();
   4.108 +        ServerConfiguration conf = server.getServerConfiguration();
   4.109 +        conf.addHttpHandler(new Page(resources, 
   4.110 +            "org/apidesign/bck2brwsr/launcher/harness.xhtml"
   4.111 +        ), "/execute");
   4.112          conf.addHttpHandler(new HttpHandler() {
   4.113              int cnt;
   4.114              @Override
   4.115 @@ -183,21 +201,8 @@
   4.116                  cnt++;
   4.117              }
   4.118          }, "/data");
   4.119 -        conf.addHttpHandler(new Page("harness.xhtml"), "/");
   4.120 -        
   4.121 -        server.start();
   4.122 -        NetworkListener listener = server.getListeners().iterator().next();
   4.123 -        int port = listener.getPort();
   4.124 -        
   4.125 -        URI uri = new URI("http://localhost:" + port + "/execute");
   4.126 -        try {
   4.127 -            Desktop.getDesktop().browse(uri);
   4.128 -        } catch (UnsupportedOperationException ex) {
   4.129 -            String[] cmd = { 
   4.130 -                "xdg-open", uri.toString()
   4.131 -            };
   4.132 -            Runtime.getRuntime().exec(cmd).waitFor();
   4.133 -        }
   4.134 +
   4.135 +        launchServerAndBrwsr(server, "/execute");
   4.136          
   4.137          wait.await(timeOut, TimeUnit.MILLISECONDS);
   4.138          server.stop();
   4.139 @@ -223,6 +228,23 @@
   4.140          }
   4.141      }
   4.142  
   4.143 +    private void launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
   4.144 +        server.start();
   4.145 +        NetworkListener listener = server.getListeners().iterator().next();
   4.146 +        int port = listener.getPort();
   4.147 +        
   4.148 +        URI uri = new URI("http://localhost:" + port + page);
   4.149 +        try {
   4.150 +            Desktop.getDesktop().browse(uri);
   4.151 +        } catch (UnsupportedOperationException ex) {
   4.152 +            String[] cmd = { 
   4.153 +                "xdg-open", uri.toString()
   4.154 +            };
   4.155 +            Runtime.getRuntime().exec(cmd).waitFor();
   4.156 +        }
   4.157 +        System.err.println("Showing " + uri);
   4.158 +    }
   4.159 +
   4.160      private class Res implements Bck2Brwsr.Resources {
   4.161          @Override
   4.162          public InputStream get(String resource) throws IOException {
   4.163 @@ -243,18 +265,34 @@
   4.164      private static class Page extends HttpHandler {
   4.165          private final String resource;
   4.166          private final String[] args;
   4.167 +        private final Res res;
   4.168          
   4.169 -        public Page(String resource, String... args) {
   4.170 +        public Page(Res res, String resource, String... args) {
   4.171 +            this.res = res;
   4.172              this.resource = resource;
   4.173              this.args = args;
   4.174          }
   4.175  
   4.176          @Override
   4.177          public void service(Request request, Response response) throws Exception {
   4.178 -            response.setContentType("text/html");
   4.179 +            String r = resource;
   4.180 +            if (r == null) {
   4.181 +                r = request.getHttpHandlerPath();
   4.182 +                if (r.startsWith("/")) {
   4.183 +                    r = r.substring(1);
   4.184 +                }
   4.185 +            }
   4.186 +            if (r.endsWith(".html") || r.endsWith(".xhtml")) {
   4.187 +                response.setContentType("text/html");
   4.188 +            }
   4.189              OutputStream os = response.getOutputStream();
   4.190 -            InputStream is = Bck2BrwsrLauncher.class.getResourceAsStream(resource);
   4.191 -            copyStream(is, os, request.getRequestURL().toString(), args);
   4.192 +            try (InputStream is = res.get(r)) {
   4.193 +                copyStream(is, os, request.getRequestURL().toString(), args);
   4.194 +            } catch (IOException ex) {
   4.195 +                response.setDetailMessage(ex.getLocalizedMessage());
   4.196 +                response.setError();
   4.197 +                response.setStatus(404);
   4.198 +            }
   4.199          }
   4.200      }
   4.201  
   4.202 @@ -283,7 +321,7 @@
   4.203              response.getWriter().append(
   4.204                  "function ldCls(res) {\n"
   4.205                  + "  var request = new XMLHttpRequest();\n"
   4.206 -                + "  request.open('GET', 'classes/' + res, false);\n"
   4.207 +                + "  request.open('GET', '/classes/' + res, false);\n"
   4.208                  + "  request.send();\n"
   4.209                  + "  var arr = eval('(' + request.responseText + ')');\n"
   4.210                  + "  return arr;\n"
     5.1 --- a/mojo/pom.xml	Thu Dec 20 11:03:34 2012 +0100
     5.2 +++ b/mojo/pom.xml	Thu Dec 20 16:17:31 2012 +0100
     5.3 @@ -77,5 +77,10 @@
     5.4        <version>3.0.2</version>
     5.5        <type>jar</type>
     5.6      </dependency>
     5.7 +    <dependency>
     5.8 +        <groupId>${project.groupId}</groupId>
     5.9 +        <artifactId>launcher</artifactId>
    5.10 +      <version>${project.version}</version>
    5.11 +    </dependency>
    5.12  </dependencies>
    5.13  </project>
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/mojo/src/main/java/org/apidesign/bck2brwsr/mojo/BrswrMojo.java	Thu Dec 20 16:17:31 2012 +0100
     6.3 @@ -0,0 +1,114 @@
     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.mojo;
    6.22 +
    6.23 +import org.apache.maven.plugin.AbstractMojo;
    6.24 +
    6.25 +import java.io.File;
    6.26 +import java.io.FileWriter;
    6.27 +import java.io.IOException;
    6.28 +import java.net.MalformedURLException;
    6.29 +import java.net.URL;
    6.30 +import java.net.URLClassLoader;
    6.31 +import java.util.ArrayList;
    6.32 +import java.util.Collection;
    6.33 +import java.util.List;
    6.34 +import org.apache.maven.artifact.Artifact;
    6.35 +import org.apache.maven.plugin.MojoExecutionException;
    6.36 +import org.apache.maven.plugins.annotations.LifecyclePhase;
    6.37 +import org.apache.maven.plugins.annotations.Mojo;
    6.38 +import org.apache.maven.plugins.annotations.Parameter;
    6.39 +import org.apache.maven.project.MavenProject;
    6.40 +import org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher;
    6.41 +import org.apidesign.vm4brwsr.Bck2Brwsr;
    6.42 +
    6.43 +/** Executes given HTML page in a browser. */
    6.44 +@Mojo(name="brwsr", defaultPhase=LifecyclePhase.DEPLOY)
    6.45 +public class BrswrMojo extends AbstractMojo {
    6.46 +    public BrswrMojo() {
    6.47 +    }
    6.48 +    /** Resource to show as initial page */
    6.49 +    @Parameter
    6.50 +    private String startpage;
    6.51 +    
    6.52 +    @Parameter(defaultValue="${project}")
    6.53 +    private MavenProject prj;
    6.54 +    
    6.55 +    /** Root of the class files */
    6.56 +    @Parameter(defaultValue="${project.build.directory}/classes")
    6.57 +    private File classes;
    6.58 +
    6.59 +    @Override
    6.60 +    public void execute() throws MojoExecutionException {
    6.61 +        if (startpage == null) {
    6.62 +            throw new MojoExecutionException("You have to provide a start page");
    6.63 +        }
    6.64 +
    6.65 +        try {
    6.66 +            URLClassLoader url = buildClassLoader(classes, prj.getDependencyArtifacts());
    6.67 +            
    6.68 +            Bck2BrwsrLauncher httpServer = new Bck2BrwsrLauncher();
    6.69 +            httpServer.setStartPage(startpage);
    6.70 +            httpServer.addClassLoader(url);
    6.71 +            httpServer.execute();
    6.72 +            
    6.73 +            System.in.read();
    6.74 +        } catch (IOException ex) {
    6.75 +            throw new MojoExecutionException("Can't show the browser", ex);
    6.76 +        }
    6.77 +    }
    6.78 +
    6.79 +    private static File findNonEmptyFolder(File dir) throws MojoExecutionException {
    6.80 +        if (!dir.isDirectory()) {
    6.81 +            throw new MojoExecutionException("Not a directory " + dir);
    6.82 +        }
    6.83 +        File[] arr = dir.listFiles();
    6.84 +        if (arr.length == 1 && arr[0].isDirectory()) {
    6.85 +            return findNonEmptyFolder(arr[0]);
    6.86 +        }
    6.87 +        return dir;
    6.88 +    }
    6.89 +
    6.90 +    private static long collectAllClasses(String prefix, File toCheck, List<String> arr) {
    6.91 +        File[] files = toCheck.listFiles();
    6.92 +        if (files != null) {
    6.93 +            long newest = 0L;
    6.94 +            for (File f : files) {
    6.95 +                long lastModified = collectAllClasses(prefix + f.getName() + "/", f, arr);
    6.96 +                if (newest < lastModified) {
    6.97 +                    newest = lastModified;
    6.98 +                }
    6.99 +            }
   6.100 +            return newest;
   6.101 +        } else if (toCheck.getName().endsWith(".class")) {
   6.102 +            arr.add(prefix.substring(0, prefix.length() - 7));
   6.103 +            return toCheck.lastModified();
   6.104 +        } else {
   6.105 +            return 0L;
   6.106 +        }
   6.107 +    }
   6.108 +
   6.109 +    private static URLClassLoader buildClassLoader(File root, Collection<Artifact> deps) throws MalformedURLException {
   6.110 +        List<URL> arr = new ArrayList<URL>();
   6.111 +        arr.add(root.toURI().toURL());
   6.112 +        for (Artifact a : deps) {
   6.113 +            arr.add(a.getFile().toURI().toURL());
   6.114 +        }
   6.115 +        return new URLClassLoader(arr.toArray(new URL[0]), BrswrMojo.class.getClassLoader());
   6.116 +    }
   6.117 +}