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 + }}