launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java
branchclosure
changeset 1513 ba912ef24b27
parent 1505 706b66d8c481
parent 1383 5ebf214c2af3
child 1521 6c709f05afa5
     1.1 --- a/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java	Tue Apr 29 14:59:40 2014 +0200
     1.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java	Wed Apr 30 15:04:10 2014 +0200
     1.3 @@ -17,6 +17,8 @@
     1.4   */
     1.5  package org.apidesign.bck2brwsr.launcher;
     1.6  
     1.7 +import java.io.ByteArrayInputStream;
     1.8 +import java.io.ByteArrayOutputStream;
     1.9  import java.io.Closeable;
    1.10  import java.io.File;
    1.11  import java.io.IOException;
    1.12 @@ -52,7 +54,13 @@
    1.13  import org.glassfish.grizzly.http.server.Request;
    1.14  import org.glassfish.grizzly.http.server.Response;
    1.15  import org.glassfish.grizzly.http.server.ServerConfiguration;
    1.16 +import org.glassfish.grizzly.http.server.StaticHttpHandler;
    1.17  import org.glassfish.grizzly.http.util.HttpStatus;
    1.18 +import org.glassfish.grizzly.threadpool.ThreadPoolConfig;
    1.19 +import org.glassfish.grizzly.websockets.WebSocket;
    1.20 +import org.glassfish.grizzly.websockets.WebSocketAddOn;
    1.21 +import org.glassfish.grizzly.websockets.WebSocketApplication;
    1.22 +import org.glassfish.grizzly.websockets.WebSocketEngine;
    1.23  
    1.24  /**
    1.25   * Lightweight server to launch Bck2Brwsr applications and tests.
    1.26 @@ -62,8 +70,8 @@
    1.27  abstract class BaseHTTPLauncher extends Launcher implements Closeable, Callable<HttpServer> {
    1.28      static final Logger LOG = Logger.getLogger(BaseHTTPLauncher.class.getName());
    1.29      private static final InvocationContext END = new InvocationContext(null, null, null);
    1.30 -    private final Set<ClassLoader> loaders = new LinkedHashSet<>();
    1.31 -    private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<>();
    1.32 +    private final Set<ClassLoader> loaders = new LinkedHashSet<ClassLoader>();
    1.33 +    private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<InvocationContext>();
    1.34      private long timeOut;
    1.35      private final Res resources = new Res();
    1.36      private final String cmd;
    1.37 @@ -105,7 +113,7 @@
    1.38          if (!startpage.startsWith("/")) {
    1.39              startpage = "/" + startpage;
    1.40          }
    1.41 -        HttpServer s = initServer(".", true);
    1.42 +        HttpServer s = initServer(".", true, "");
    1.43          int last = startpage.lastIndexOf('/');
    1.44          String prefix = startpage.substring(0, last);
    1.45          String simpleName = startpage.substring(last);
    1.46 @@ -113,19 +121,24 @@
    1.47          server = s;
    1.48          try {
    1.49              launchServerAndBrwsr(s, simpleName);
    1.50 -        } catch (URISyntaxException | InterruptedException ex) {
    1.51 +        } catch (Exception ex) {
    1.52              throw new IOException(ex);
    1.53          }
    1.54      }
    1.55  
    1.56 -    void showDirectory(File dir, String startpage) throws IOException {
    1.57 +    void showDirectory(File dir, String startpage, boolean addClasses) throws IOException {
    1.58          if (!startpage.startsWith("/")) {
    1.59              startpage = "/" + startpage;
    1.60          }
    1.61 -        HttpServer s = initServer(dir.getPath(), false);
    1.62 +        String prefix = "";
    1.63 +        int last = startpage.lastIndexOf('/');
    1.64 +        if (last >= 0) {
    1.65 +            prefix = startpage.substring(0, last);
    1.66 +        }
    1.67 +        HttpServer s = initServer(dir.getPath(), addClasses, prefix);
    1.68          try {
    1.69              launchServerAndBrwsr(s, startpage);
    1.70 -        } catch (URISyntaxException | InterruptedException ex) {
    1.71 +        } catch (Exception ex) {
    1.72              throw new IOException(ex);
    1.73          }
    1.74      }
    1.75 @@ -149,16 +162,16 @@
    1.76          }
    1.77      }
    1.78      
    1.79 -    private HttpServer initServer(String path, boolean addClasses) throws IOException {
    1.80 -        HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535));
    1.81 -/*
    1.82 +    private HttpServer initServer(String path, boolean addClasses, String vmPrefix) throws IOException {
    1.83 +        HttpServer s = HttpServer.createSimpleServer(null, new PortRange(8080, 65535));
    1.84 +        /*
    1.85          ThreadPoolConfig fewThreads = ThreadPoolConfig.defaultConfig().copy().
    1.86              setPoolName("Fx/Bck2 Brwsr").
    1.87 -            setCorePoolSize(1).
    1.88 +            setCorePoolSize(3).
    1.89              setMaxPoolSize(5);
    1.90          ThreadPoolConfig oneKernel = ThreadPoolConfig.defaultConfig().copy().
    1.91              setPoolName("Kernel Fx/Bck2").
    1.92 -            setCorePoolSize(1).
    1.93 +            setCorePoolSize(3).
    1.94              setMaxPoolSize(3);
    1.95          for (NetworkListener nl : s.getListeners()) {
    1.96              nl.getTransport().setWorkerThreadPoolConfig(fewThreads);
    1.97 @@ -166,36 +179,81 @@
    1.98          }
    1.99  */        
   1.100          final ServerConfiguration conf = s.getServerConfiguration();
   1.101 +        VMAndPages vm = new VMAndPages();
   1.102 +        conf.addHttpHandler(vm, "/");
   1.103 +        if (vmPrefix != null) {
   1.104 +            vm.registerVM(vmPrefix + "/bck2brwsr.js");
   1.105 +        }
   1.106 +        if (path != null) {
   1.107 +            vm.addDocRoot(path);
   1.108 +        }
   1.109          if (addClasses) {
   1.110 -            conf.addHttpHandler(new VM(), "/bck2brwsr.js");
   1.111              conf.addHttpHandler(new Classes(resources), "/classes/");
   1.112          }
   1.113 +        final WebSocketAddOn addon = new WebSocketAddOn();
   1.114 +        for (NetworkListener listener : s.getListeners()) {
   1.115 +            listener.registerAddOn(addon);
   1.116 +        }
   1.117          return s;
   1.118      }
   1.119      
   1.120      private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
   1.121          wait = new CountDownLatch(1);
   1.122 -        server = initServer(".", true);
   1.123 +        server = initServer(".", true, "");
   1.124          final ServerConfiguration conf = server.getServerConfiguration();
   1.125          
   1.126          class DynamicResourceHandler extends HttpHandler {
   1.127              private final InvocationContext ic;
   1.128 +            private int resourcesCount;
   1.129 +            DynamicResourceHandler delegate;
   1.130              public DynamicResourceHandler(InvocationContext ic) {
   1.131 -                if (ic == null || ic.resources.isEmpty()) {
   1.132 -                    throw new NullPointerException();
   1.133 -                }
   1.134                  this.ic = ic;
   1.135                  for (Resource r : ic.resources) {
   1.136                      conf.addHttpHandler(this, r.httpPath);
   1.137                  }
   1.138              }
   1.139  
   1.140 -            public void close() {
   1.141 +            public void close(DynamicResourceHandler del) {
   1.142                  conf.removeHttpHandler(this);
   1.143 +                delegate = del;
   1.144              }
   1.145              
   1.146              @Override
   1.147              public void service(Request request, Response response) throws Exception {
   1.148 +                if (delegate != null) {
   1.149 +                    delegate.service(request, response);
   1.150 +                    return;
   1.151 +                }
   1.152 +                
   1.153 +                if ("/dynamic".equals(request.getRequestURI())) {
   1.154 +                    boolean webSocket = false;
   1.155 +                    String mimeType = request.getParameter("mimeType");
   1.156 +                    List<String> params = new ArrayList<String>();
   1.157 +                    for (int i = 0; ; i++) {
   1.158 +                        String p = request.getParameter("param" + i);
   1.159 +                        if (p == null) {
   1.160 +                            break;
   1.161 +                        }
   1.162 +                        params.add(p);
   1.163 +                        if ("protocol:ws".equals(p)) {
   1.164 +                            webSocket = true;
   1.165 +                            continue;
   1.166 +                        }                    }
   1.167 +                    final String cnt = request.getParameter("content");
   1.168 +                    String mangle = cnt.replace("%20", " ").replace("%0A", "\n");
   1.169 +                    ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8"));
   1.170 +                    URI url;
   1.171 +                    final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()]));
   1.172 +                    if (webSocket) {
   1.173 +                        url = registerWebSocket(res);
   1.174 +                    } else {
   1.175 +                        url = registerResource(res);
   1.176 +                    }
   1.177 +                    response.getWriter().write(url.toString());
   1.178 +                    response.getWriter().write("\n");
   1.179 +                    return;
   1.180 +                }
   1.181 +                
   1.182                  for (Resource r : ic.resources) {
   1.183                      if (r.httpPath.equals(request.getRequestURI())) {
   1.184                          LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
   1.185 @@ -232,13 +290,26 @@
   1.186                      }
   1.187                  }
   1.188              }
   1.189 +            
   1.190 +            private URI registerWebSocket(Resource r) {
   1.191 +                WebSocketEngine.getEngine().register("", r.httpPath, new WS(r));
   1.192 +                return pageURL("ws", server, r.httpPath);
   1.193 +            }
   1.194 +
   1.195 +            private URI registerResource(Resource r) {
   1.196 +                if (!ic.resources.contains(r)) {
   1.197 +                    ic.resources.add(r);
   1.198 +                    conf.addHttpHandler(this, r.httpPath);
   1.199 +                }
   1.200 +                return pageURL("http", server, r.httpPath);
   1.201 +            }
   1.202          }
   1.203          
   1.204          conf.addHttpHandler(new Page(resources, harnessResource()), "/execute");
   1.205          
   1.206          conf.addHttpHandler(new HttpHandler() {
   1.207              int cnt;
   1.208 -            List<InvocationContext> cases = new ArrayList<>();
   1.209 +            List<InvocationContext> cases = new ArrayList<InvocationContext>();
   1.210              DynamicResourceHandler prev;
   1.211              @Override
   1.212              public void service(Request request, Response response) throws Exception {
   1.213 @@ -270,11 +341,6 @@
   1.214                      }
   1.215                  }
   1.216                  
   1.217 -                if (prev != null) {
   1.218 -                    prev.close();
   1.219 -                    prev = null;
   1.220 -                }
   1.221 -                
   1.222                  if (mi == null) {
   1.223                      mi = methods.take();
   1.224                      caseNmbr = cnt++;
   1.225 @@ -286,10 +352,12 @@
   1.226                      LOG.log(Level.INFO, "End of data reached. Exiting.");
   1.227                      return;
   1.228                  }
   1.229 -                
   1.230 -                if (!mi.resources.isEmpty()) {
   1.231 -                    prev = new DynamicResourceHandler(mi);
   1.232 +                final DynamicResourceHandler newRH = new DynamicResourceHandler(mi);
   1.233 +                if (prev != null) {
   1.234 +                    prev.close(newRH);
   1.235                  }
   1.236 +                prev = newRH;
   1.237 +                conf.addHttpHandler(prev, "/dynamic");
   1.238                  
   1.239                  cases.add(mi);
   1.240                  final String cn = mi.clazz.getName();
   1.241 @@ -382,10 +450,7 @@
   1.242  
   1.243      private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
   1.244          server.start();
   1.245 -        NetworkListener listener = server.getListeners().iterator().next();
   1.246 -        int port = listener.getPort();
   1.247 -        
   1.248 -        URI uri = new URI("http://localhost:" + port + page);
   1.249 +        URI uri = pageURL("http", server, page);
   1.250          return showBrwsr(uri);
   1.251      }
   1.252      private static String toUTF8(String value) throws UnsupportedEncodingException {
   1.253 @@ -503,6 +568,16 @@
   1.254          return null;
   1.255      }
   1.256  
   1.257 +    private static URI pageURL(String protocol, HttpServer server, final String page) {
   1.258 +        NetworkListener listener = server.getListeners().iterator().next();
   1.259 +        int port = listener.getPort();
   1.260 +        try {
   1.261 +            return new URI(protocol + "://localhost:" + port + page);
   1.262 +        } catch (URISyntaxException ex) {
   1.263 +            throw new IllegalStateException(ex);
   1.264 +        }
   1.265 +    }
   1.266 +
   1.267      final class Res {
   1.268          String compileJar(JarFile jar) throws IOException {
   1.269              return BaseHTTPLauncher.this.compileJar(jar);
   1.270 @@ -510,7 +585,10 @@
   1.271          String compileFromClassPath(URL f) throws IOException {
   1.272              return BaseHTTPLauncher.this.compileFromClassPath(f, this);
   1.273          }
   1.274 -        public URL get(String resource) throws IOException {
   1.275 +        public URL get(String resource, int skip) throws IOException {
   1.276 +            if (!resource.endsWith(".class")) {
   1.277 +                return getResource(resource, skip);
   1.278 +            }
   1.279              URL u = null;
   1.280              for (ClassLoader l : loaders) {
   1.281                  Enumeration<URL> en = l.getResources(resource);
   1.282 @@ -523,12 +601,30 @@
   1.283              }
   1.284              if (u != null) {
   1.285                  if (u.toExternalForm().contains("rt.jar")) {
   1.286 -                    LOG.log(Level.WARNING, "Fallback to bootclasspath for {0}", u);
   1.287 +                    LOG.log(Level.WARNING, "No fallback to bootclasspath for {0}", u);
   1.288 +                    return null;
   1.289                  }
   1.290                  return u;
   1.291              }
   1.292              throw new IOException("Can't find " + resource);
   1.293          }
   1.294 +        private URL getResource(String resource, int skip) throws IOException {
   1.295 +            for (ClassLoader l : loaders) {
   1.296 +                Enumeration<URL> en = l.getResources(resource);
   1.297 +                while (en.hasMoreElements()) {
   1.298 +                    final URL now = en.nextElement();
   1.299 +                    if (now.toExternalForm().contains("sisu-inject-bean")) {
   1.300 +                        // certainly we don't want this resource, as that
   1.301 +                        // module is not compiled with target 1.6, currently
   1.302 +                        continue;
   1.303 +                    }
   1.304 +                    if (--skip < 0) {
   1.305 +                        return now;
   1.306 +                    }
   1.307 +                }
   1.308 +            }
   1.309 +            throw new IOException("Not found (anymore of) " + resource);
   1.310 +        }
   1.311      }
   1.312  
   1.313      private static class Page extends HttpHandler {
   1.314 @@ -560,7 +656,8 @@
   1.315                  replace = args;
   1.316              }
   1.317              OutputStream os = response.getOutputStream();
   1.318 -            try (InputStream is = res.get(r).openStream()) {
   1.319 +            try { 
   1.320 +                InputStream is = res.get(r, 0).openStream();
   1.321                  copyStream(is, os, request.getRequestURL().toString(), replace);
   1.322              } catch (IOException ex) {
   1.323                  response.setDetailMessage(ex.getLocalizedMessage());
   1.324 @@ -592,14 +689,28 @@
   1.325          
   1.326      }
   1.327  
   1.328 -    private class VM extends HttpHandler {
   1.329 +    private class VMAndPages extends StaticHttpHandler {
   1.330 +        private String vmResource;
   1.331 +        
   1.332 +        public VMAndPages() {
   1.333 +            super((String[]) null);
   1.334 +        }
   1.335 +        
   1.336          @Override
   1.337          public void service(Request request, Response response) throws Exception {
   1.338 -            response.setCharacterEncoding("UTF-8");
   1.339 -            response.setContentType("text/javascript");
   1.340 -            StringBuilder sb = new StringBuilder();
   1.341 -            generateBck2BrwsrJS(sb, BaseHTTPLauncher.this.resources);
   1.342 -            response.getWriter().write(sb.toString());
   1.343 +            if (request.getRequestURI().equals(vmResource)) {
   1.344 +                response.setCharacterEncoding("UTF-8");
   1.345 +                response.setContentType("text/javascript");
   1.346 +                StringBuilder sb = new StringBuilder();
   1.347 +                generateBck2BrwsrJS(sb, BaseHTTPLauncher.this.resources);
   1.348 +                response.getWriter().write(sb.toString());
   1.349 +            } else {
   1.350 +                super.service(request, response);
   1.351 +            }
   1.352 +        }
   1.353 +
   1.354 +        private void registerVM(String vmResource) {
   1.355 +            this.vmResource = vmResource;
   1.356          }
   1.357      }
   1.358  
   1.359 @@ -616,7 +727,9 @@
   1.360              if (res.startsWith("/")) {
   1.361                  res = res.substring(1);
   1.362              }
   1.363 -            URL url = loader.get(res);
   1.364 +            String skip = request.getParameter("skip");
   1.365 +            int skipCnt = skip == null ? 0 : Integer.parseInt(skip);
   1.366 +            URL url = loader.get(res, skipCnt);
   1.367              if (url.getProtocol().equals("jar")) {
   1.368                  JarURLConnection juc = (JarURLConnection) url.openConnection();
   1.369                  String s = loader.compileJar(juc.getJarFile());
   1.370 @@ -639,6 +752,13 @@
   1.371              Exception ex = new Exception("Won't server bytes of " + url);
   1.372              /*
   1.373              try (InputStream is = url.openStream()) {
   1.374 +=======
   1.375 +            InputStream is = null;
   1.376 +            try {
   1.377 +                String skip = request.getParameter("skip");
   1.378 +                int skipCnt = skip == null ? 0 : Integer.parseInt(skip);
   1.379 +                is = loader.get(res, skipCnt);
   1.380 +>>>>>>> other
   1.381                  response.setContentType("text/javascript");
   1.382                  Writer w = response.getWriter();
   1.383                  w.append("([");
   1.384 @@ -664,8 +784,33 @@
   1.385                  response.setStatus(HttpStatus.NOT_FOUND_404);
   1.386                  response.setError();
   1.387                  response.setDetailMessage(ex.getMessage());
   1.388 +            } /*finally {
   1.389 +                if (is != null) {
   1.390 +                    is.close();
   1.391 +                }
   1.392 +            }*/
   1.393 +        }
   1.394 +
   1.395 +    }
   1.396 +    private static class WS extends WebSocketApplication {
   1.397 +
   1.398 +        private final Resource r;
   1.399 +
   1.400 +        private WS(Resource r) {
   1.401 +            this.r = r;
   1.402 +        }
   1.403 +
   1.404 +        @Override
   1.405 +        public void onMessage(WebSocket socket, String text) {
   1.406 +            try {
   1.407 +                r.httpContent.reset();
   1.408 +                ByteArrayOutputStream out = new ByteArrayOutputStream();
   1.409 +                copyStream(r.httpContent, out, null, text);
   1.410 +                String s = new String(out.toByteArray(), "UTF-8");
   1.411 +                socket.send(s);
   1.412 +            } catch (IOException ex) {
   1.413 +                LOG.log(Level.WARNING, null, ex);
   1.414              }
   1.415          }
   1.416  
   1.417 -    }
   1.418 -}
   1.419 +    }}