jaroslav@1033: /** jaroslav@1033: * Back 2 Browser Bytecode Translator jaroslav@1033: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1033: * jaroslav@1033: * This program is free software: you can redistribute it and/or modify jaroslav@1033: * it under the terms of the GNU General Public License as published by jaroslav@1033: * the Free Software Foundation, version 2 of the License. jaroslav@1033: * jaroslav@1033: * This program is distributed in the hope that it will be useful, jaroslav@1033: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1033: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1033: * GNU General Public License for more details. jaroslav@1033: * jaroslav@1033: * You should have received a copy of the GNU General Public License jaroslav@1033: * along with this program. Look for COPYING file in the top folder. jaroslav@1033: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1033: */ jaroslav@1033: package org.apidesign.bck2brwsr.launcher; jaroslav@1033: jaroslav@1188: import java.io.ByteArrayInputStream; jaroslav@1249: import java.io.ByteArrayOutputStream; jaroslav@1033: import java.io.Closeable; jaroslav@1033: import java.io.File; jaroslav@1033: import java.io.IOException; jaroslav@1033: import java.io.InputStream; jaroslav@1033: import java.io.InterruptedIOException; jaroslav@1033: import java.io.OutputStream; jaroslav@1088: import java.io.Reader; jaroslav@1033: import java.io.UnsupportedEncodingException; jaroslav@1033: import java.io.Writer; jaroslav@1489: import java.net.JarURLConnection; jaroslav@1033: import java.net.URI; jaroslav@1033: import java.net.URISyntaxException; jaroslav@1033: import java.net.URL; jaroslav@1033: import java.util.ArrayList; jaroslav@1033: import java.util.Arrays; jaroslav@1033: import java.util.Enumeration; jaroslav@1530: import java.util.HashSet; jaroslav@1033: import java.util.LinkedHashSet; jaroslav@1033: import java.util.List; jaroslav@1033: import java.util.Set; jaroslav@1033: import java.util.concurrent.BlockingQueue; jaroslav@1076: import java.util.concurrent.Callable; jaroslav@1033: import java.util.concurrent.CountDownLatch; jaroslav@1033: import java.util.concurrent.LinkedBlockingQueue; jaroslav@1033: import java.util.concurrent.TimeUnit; jaroslav@1489: import java.util.jar.JarFile; jaroslav@1033: import java.util.logging.Level; jaroslav@1033: import java.util.logging.Logger; jaroslav@1033: import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; jaroslav@1033: import org.glassfish.grizzly.PortRange; jaroslav@1033: import org.glassfish.grizzly.http.server.HttpHandler; jaroslav@1033: import org.glassfish.grizzly.http.server.HttpServer; jaroslav@1033: import org.glassfish.grizzly.http.server.NetworkListener; jaroslav@1033: import org.glassfish.grizzly.http.server.Request; jaroslav@1033: import org.glassfish.grizzly.http.server.Response; jaroslav@1033: import org.glassfish.grizzly.http.server.ServerConfiguration; jaroslav@1271: import org.glassfish.grizzly.http.server.StaticHttpHandler; jaroslav@1033: import org.glassfish.grizzly.http.util.HttpStatus; jaroslav@1092: import org.glassfish.grizzly.threadpool.ThreadPoolConfig; jaroslav@1249: import org.glassfish.grizzly.websockets.WebSocket; jaroslav@1249: import org.glassfish.grizzly.websockets.WebSocketAddOn; jaroslav@1249: import org.glassfish.grizzly.websockets.WebSocketApplication; jaroslav@1249: import org.glassfish.grizzly.websockets.WebSocketEngine; jaroslav@1033: jaroslav@1033: /** jaroslav@1033: * Lightweight server to launch Bck2Brwsr applications and tests. jaroslav@1033: * Supports execution in native browser as well as Java's internal jaroslav@1033: * execution engine. jaroslav@1033: */ jaroslav@1076: abstract class BaseHTTPLauncher extends Launcher implements Closeable, Callable { jaroslav@1088: static final Logger LOG = Logger.getLogger(BaseHTTPLauncher.class.getName()); jaroslav@1033: private static final InvocationContext END = new InvocationContext(null, null, null); jaroslav@1165: private final Set loaders = new LinkedHashSet(); jaroslav@1165: private final BlockingQueue methods = new LinkedBlockingQueue(); jaroslav@1033: private long timeOut; jaroslav@1033: private final Res resources = new Res(); jaroslav@1033: private final String cmd; jaroslav@1033: private Object[] brwsr; jaroslav@1033: private HttpServer server; jaroslav@1033: private CountDownLatch wait; jaroslav@1033: jaroslav@1041: public BaseHTTPLauncher(String cmd) { jaroslav@1033: this.cmd = cmd; jaroslav@1043: addClassLoader(BaseHTTPLauncher.class.getClassLoader()); jaroslav@1033: setTimeout(180000); jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: InvocationContext runMethod(InvocationContext c) throws IOException { jaroslav@1033: loaders.add(c.clazz.getClassLoader()); jaroslav@1033: methods.add(c); jaroslav@1033: try { jaroslav@1033: c.await(timeOut); jaroslav@1033: } catch (InterruptedException ex) { jaroslav@1033: throw new IOException(ex); jaroslav@1033: } jaroslav@1033: return c; jaroslav@1033: } jaroslav@1033: jaroslav@1033: public void setTimeout(long ms) { jaroslav@1033: timeOut = ms; jaroslav@1033: } jaroslav@1033: jaroslav@1033: public void addClassLoader(ClassLoader url) { jaroslav@1033: this.loaders.add(url); jaroslav@1033: } jaroslav@1041: jaroslav@1041: ClassLoader[] loaders() { jaroslav@1041: return loaders.toArray(new ClassLoader[loaders.size()]); jaroslav@1041: } jaroslav@1033: jaroslav@1033: public void showURL(String startpage) throws IOException { jaroslav@1033: if (!startpage.startsWith("/")) { jaroslav@1033: startpage = "/" + startpage; jaroslav@1033: } jaroslav@1271: HttpServer s = initServer(".", true, ""); jaroslav@1033: int last = startpage.lastIndexOf('/'); jaroslav@1033: String prefix = startpage.substring(0, last); jaroslav@1033: String simpleName = startpage.substring(last); jaroslav@1033: s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/"); jaroslav@1076: server = s; jaroslav@1033: try { jaroslav@1033: launchServerAndBrwsr(s, simpleName); jaroslav@1165: } catch (Exception ex) { jaroslav@1033: throw new IOException(ex); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1273: void showDirectory(File dir, String startpage, boolean addClasses) throws IOException { jaroslav@1033: if (!startpage.startsWith("/")) { jaroslav@1033: startpage = "/" + startpage; jaroslav@1033: } jaroslav@1271: String prefix = ""; jaroslav@1271: int last = startpage.lastIndexOf('/'); jaroslav@1271: if (last >= 0) { jaroslav@1271: prefix = startpage.substring(0, last); jaroslav@1271: } jaroslav@1273: HttpServer s = initServer(dir.getPath(), addClasses, prefix); jaroslav@1033: try { jaroslav@1033: launchServerAndBrwsr(s, startpage); jaroslav@1165: } catch (Exception ex) { jaroslav@1033: throw new IOException(ex); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: public void initialize() throws IOException { jaroslav@1033: try { jaroslav@1033: executeInBrowser(); jaroslav@1033: } catch (InterruptedException ex) { jaroslav@1033: final InterruptedIOException iio = new InterruptedIOException(ex.getMessage()); jaroslav@1033: iio.initCause(ex); jaroslav@1033: throw iio; jaroslav@1033: } catch (Exception ex) { jaroslav@1033: if (ex instanceof IOException) { jaroslav@1033: throw (IOException)ex; jaroslav@1033: } jaroslav@1033: if (ex instanceof RuntimeException) { jaroslav@1033: throw (RuntimeException)ex; jaroslav@1033: } jaroslav@1033: throw new IOException(ex); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1271: private HttpServer initServer(String path, boolean addClasses, String vmPrefix) throws IOException { jaroslav@1271: HttpServer s = HttpServer.createSimpleServer(null, new PortRange(8080, 65535)); jaroslav@1253: /* jaroslav@1092: ThreadPoolConfig fewThreads = ThreadPoolConfig.defaultConfig().copy(). jaroslav@1092: setPoolName("Fx/Bck2 Brwsr"). jaroslav@1253: setCorePoolSize(3). jaroslav@1092: setMaxPoolSize(5); jaroslav@1092: ThreadPoolConfig oneKernel = ThreadPoolConfig.defaultConfig().copy(). jaroslav@1092: setPoolName("Kernel Fx/Bck2"). jaroslav@1253: setCorePoolSize(3). jaroslav@1092: setMaxPoolSize(3); jaroslav@1092: for (NetworkListener nl : s.getListeners()) { jaroslav@1092: nl.getTransport().setWorkerThreadPoolConfig(fewThreads); jaroslav@1092: nl.getTransport().setKernelThreadPoolConfig(oneKernel); jaroslav@1092: } jaroslav@1500: */ jaroslav@1033: final ServerConfiguration conf = s.getServerConfiguration(); jaroslav@1271: VMAndPages vm = new VMAndPages(); jaroslav@1271: conf.addHttpHandler(vm, "/"); jaroslav@1271: if (vmPrefix != null) { jaroslav@1271: vm.registerVM(vmPrefix + "/bck2brwsr.js"); jaroslav@1271: } jaroslav@1271: if (path != null) { jaroslav@1271: vm.addDocRoot(path); jaroslav@1271: } jaroslav@1033: if (addClasses) { jaroslav@1033: conf.addHttpHandler(new Classes(resources), "/classes/"); jaroslav@1033: } jaroslav@1253: final WebSocketAddOn addon = new WebSocketAddOn(); jaroslav@1253: for (NetworkListener listener : s.getListeners()) { jaroslav@1253: listener.registerAddOn(addon); jaroslav@1253: } jaroslav@1033: return s; jaroslav@1033: } jaroslav@1033: jaroslav@1033: private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { jaroslav@1033: wait = new CountDownLatch(1); jaroslav@1271: server = initServer(".", true, ""); jaroslav@1033: final ServerConfiguration conf = server.getServerConfiguration(); jaroslav@1033: jaroslav@1188: class DynamicResourceHandler extends HttpHandler { jaroslav@1033: private final InvocationContext ic; jaroslav@1188: private int resourcesCount; jaroslav@1235: DynamicResourceHandler delegate; jaroslav@1033: public DynamicResourceHandler(InvocationContext ic) { jaroslav@1033: this.ic = ic; jaroslav@1033: for (Resource r : ic.resources) { jaroslav@1033: conf.addHttpHandler(this, r.httpPath); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1235: public void close(DynamicResourceHandler del) { jaroslav@1033: conf.removeHttpHandler(this); jaroslav@1235: delegate = del; jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: public void service(Request request, Response response) throws Exception { jaroslav@1235: if (delegate != null) { jaroslav@1235: delegate.service(request, response); jaroslav@1235: return; jaroslav@1235: } jaroslav@1235: jaroslav@1188: if ("/dynamic".equals(request.getRequestURI())) { jaroslav@1249: boolean webSocket = false; jaroslav@1188: String mimeType = request.getParameter("mimeType"); jaroslav@1188: List params = new ArrayList(); jaroslav@1188: for (int i = 0; ; i++) { jaroslav@1188: String p = request.getParameter("param" + i); jaroslav@1188: if (p == null) { jaroslav@1188: break; jaroslav@1188: } jaroslav@1188: params.add(p); jaroslav@1249: if ("protocol:ws".equals(p)) { jaroslav@1249: webSocket = true; jaroslav@1249: continue; jaroslav@1249: } } jaroslav@1188: final String cnt = request.getParameter("content"); jaroslav@1188: String mangle = cnt.replace("%20", " ").replace("%0A", "\n"); jaroslav@1188: ByteArrayInputStream is = new ByteArrayInputStream(mangle.getBytes("UTF-8")); jaroslav@1249: URI url; jaroslav@1249: final Resource res = new Resource(is, mimeType, "/dynamic/res" + ++resourcesCount, params.toArray(new String[params.size()])); jaroslav@1249: if (webSocket) { jaroslav@1249: url = registerWebSocket(res); jaroslav@1249: } else { jaroslav@1249: url = registerResource(res); jaroslav@1249: } jaroslav@1188: response.getWriter().write(url.toString()); jaroslav@1188: response.getWriter().write("\n"); jaroslav@1188: return; jaroslav@1188: } jaroslav@1188: jaroslav@1033: for (Resource r : ic.resources) { jaroslav@1033: if (r.httpPath.equals(request.getRequestURI())) { jaroslav@1033: LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); jaroslav@1033: response.setContentType(r.httpType); jaroslav@1033: r.httpContent.reset(); jaroslav@1033: String[] params = null; jaroslav@1033: if (r.parameters.length != 0) { jaroslav@1033: params = new String[r.parameters.length]; jaroslav@1033: for (int i = 0; i < r.parameters.length; i++) { jaroslav@1033: params[i] = request.getParameter(r.parameters[i]); jaroslav@1088: if (params[i] == null) { jaroslav@1088: if ("http.method".equals(r.parameters[i])) { jaroslav@1088: params[i] = request.getMethod().toString(); jaroslav@1088: } else if ("http.requestBody".equals(r.parameters[i])) { jaroslav@1088: Reader rdr = request.getReader(); jaroslav@1088: StringBuilder sb = new StringBuilder(); jaroslav@1088: for (;;) { jaroslav@1088: int ch = rdr.read(); jaroslav@1088: if (ch == -1) { jaroslav@1088: break; jaroslav@1088: } jaroslav@1088: sb.append((char)ch); jaroslav@1088: } jaroslav@1088: params[i] = sb.toString(); jaroslav@1088: } jaroslav@1088: } jaroslav@1088: if (params[i] == null) { jaroslav@1088: params[i] = "null"; jaroslav@1088: } jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: copyStream(r.httpContent, response.getOutputStream(), null, params); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: } jaroslav@1249: jaroslav@1249: private URI registerWebSocket(Resource r) { jaroslav@1249: WebSocketEngine.getEngine().register("", r.httpPath, new WS(r)); jaroslav@1249: return pageURL("ws", server, r.httpPath); jaroslav@1249: } jaroslav@1186: jaroslav@1188: private URI registerResource(Resource r) { jaroslav@1186: if (!ic.resources.contains(r)) { jaroslav@1186: ic.resources.add(r); jaroslav@1186: conf.addHttpHandler(this, r.httpPath); jaroslav@1186: } jaroslav@1249: return pageURL("http", server, r.httpPath); jaroslav@1186: } jaroslav@1033: } jaroslav@1033: jaroslav@1088: conf.addHttpHandler(new Page(resources, harnessResource()), "/execute"); jaroslav@1033: jaroslav@1033: conf.addHttpHandler(new HttpHandler() { jaroslav@1033: int cnt; jaroslav@1165: List cases = new ArrayList(); jaroslav@1033: DynamicResourceHandler prev; jaroslav@1033: @Override jaroslav@1033: public void service(Request request, Response response) throws Exception { jaroslav@1033: String id = request.getParameter("request"); jaroslav@1033: String value = request.getParameter("result"); jaroslav@1033: if (value != null && value.indexOf((char)0xC5) != -1) { jaroslav@1033: value = toUTF8(value); jaroslav@1033: } jaroslav@1033: jaroslav@1033: jaroslav@1033: InvocationContext mi = null; jaroslav@1033: int caseNmbr = -1; jaroslav@1033: jaroslav@1033: if (id != null && value != null) { jaroslav@1033: LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); jaroslav@1033: value = decodeURL(value); jaroslav@1033: int indx = Integer.parseInt(id); jaroslav@1033: cases.get(indx).result(value, null); jaroslav@1033: if (++indx < cases.size()) { jaroslav@1033: mi = cases.get(indx); jaroslav@1033: LOG.log(Level.INFO, "Re-executing case {0}", indx); jaroslav@1033: caseNmbr = indx; jaroslav@1033: } jaroslav@1033: } else { jaroslav@1033: if (!cases.isEmpty()) { jaroslav@1033: LOG.info("Re-executing test cases"); jaroslav@1033: mi = cases.get(0); jaroslav@1033: caseNmbr = 0; jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: if (mi == null) { jaroslav@1033: mi = methods.take(); jaroslav@1033: caseNmbr = cnt++; jaroslav@1033: } jaroslav@1033: if (mi == END) { jaroslav@1033: response.getWriter().write(""); jaroslav@1033: wait.countDown(); jaroslav@1033: cnt = 0; jaroslav@1033: LOG.log(Level.INFO, "End of data reached. Exiting."); jaroslav@1033: return; jaroslav@1033: } jaroslav@1235: final DynamicResourceHandler newRH = new DynamicResourceHandler(mi); jaroslav@1235: if (prev != null) { jaroslav@1235: prev.close(newRH); jaroslav@1235: } jaroslav@1235: prev = newRH; jaroslav@1188: conf.addHttpHandler(prev, "/dynamic"); jaroslav@1033: jaroslav@1033: cases.add(mi); jaroslav@1033: final String cn = mi.clazz.getName(); jaroslav@1033: final String mn = mi.methodName; jaroslav@1033: LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); jaroslav@1033: response.getWriter().write("{" jaroslav@1033: + "className: '" + cn + "', " jaroslav@1033: + "methodName: '" + mn + "', " jaroslav@1033: + "request: " + caseNmbr jaroslav@1033: ); jaroslav@1033: if (mi.html != null) { jaroslav@1033: response.getWriter().write(", html: '"); jaroslav@1033: response.getWriter().write(encodeJSON(mi.html)); jaroslav@1033: response.getWriter().write("'"); jaroslav@1033: } jaroslav@1033: response.getWriter().write("}"); jaroslav@1033: } jaroslav@1033: }, "/data"); jaroslav@1033: jaroslav@1033: this.brwsr = launchServerAndBrwsr(server, "/execute"); jaroslav@1033: } jaroslav@1033: jaroslav@1033: private static String encodeJSON(String in) { jaroslav@1033: StringBuilder sb = new StringBuilder(); jaroslav@1033: for (int i = 0; i < in.length(); i++) { jaroslav@1033: char ch = in.charAt(i); jaroslav@1033: if (ch < 32 || ch == '\'' || ch == '"') { jaroslav@1033: sb.append("\\u"); jaroslav@1033: String hs = "0000" + Integer.toHexString(ch); jaroslav@1033: hs = hs.substring(hs.length() - 4); jaroslav@1033: sb.append(hs); jaroslav@1033: } else { jaroslav@1033: sb.append(ch); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: return sb.toString(); jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: public void shutdown() throws IOException { jaroslav@1033: methods.offer(END); jaroslav@1033: for (;;) { jaroslav@1033: int prev = methods.size(); jaroslav@1033: try { jaroslav@1033: if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) { jaroslav@1033: break; jaroslav@1033: } jaroslav@1033: } catch (InterruptedException ex) { jaroslav@1033: throw new IOException(ex); jaroslav@1033: } jaroslav@1033: if (prev == methods.size()) { jaroslav@1033: LOG.log( jaroslav@1033: Level.WARNING, jaroslav@1033: "Timeout and no test has been executed meanwhile (at {0}). Giving up.", jaroslav@1033: methods.size() jaroslav@1033: ); jaroslav@1033: break; jaroslav@1033: } jaroslav@1033: LOG.log(Level.INFO, jaroslav@1033: "Timeout, but tests got from {0} to {1}. Trying again.", jaroslav@1033: new Object[]{prev, methods.size()} jaroslav@1033: ); jaroslav@1033: } jaroslav@1033: stopServerAndBrwsr(server, brwsr); jaroslav@1033: } jaroslav@1033: jaroslav@1033: static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { jaroslav@1033: for (;;) { jaroslav@1033: int ch = is.read(); jaroslav@1033: if (ch == -1) { jaroslav@1033: break; jaroslav@1033: } jaroslav@1033: if (ch == '$' && params.length > 0) { jaroslav@1033: int cnt = is.read() - '0'; jaroslav@1033: if (baseURL != null && cnt == 'U' - '0') { jaroslav@1033: os.write(baseURL.getBytes("UTF-8")); jaroslav@1033: } else { jaroslav@1033: if (cnt >= 0 && cnt < params.length) { jaroslav@1033: os.write(params[cnt].getBytes("UTF-8")); jaroslav@1033: } else { jaroslav@1033: os.write('$'); jaroslav@1033: os.write(cnt + '0'); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: } else { jaroslav@1033: os.write(ch); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException { jaroslav@1033: server.start(); jaroslav@1249: URI uri = pageURL("http", server, page); jaroslav@1041: return showBrwsr(uri); jaroslav@1033: } jaroslav@1033: private static String toUTF8(String value) throws UnsupportedEncodingException { jaroslav@1033: byte[] arr = new byte[value.length()]; jaroslav@1033: for (int i = 0; i < arr.length; i++) { jaroslav@1033: arr[i] = (byte)value.charAt(i); jaroslav@1033: } jaroslav@1033: return new String(arr, "UTF-8"); jaroslav@1033: } jaroslav@1033: jaroslav@1033: private static String decodeURL(String s) { jaroslav@1033: for (;;) { jaroslav@1033: int pos = s.indexOf('%'); jaroslav@1033: if (pos == -1) { jaroslav@1033: return s; jaroslav@1033: } jaroslav@1033: int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16); jaroslav@1033: s = s.substring(0, pos) + (char)i + s.substring(pos + 2); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException { jaroslav@1033: if (brwsr == null) { jaroslav@1033: return; jaroslav@1033: } jaroslav@1033: Process process = (Process)brwsr[0]; jaroslav@1033: jaroslav@1033: server.stop(); jaroslav@1033: InputStream stdout = process.getInputStream(); jaroslav@1033: InputStream stderr = process.getErrorStream(); jaroslav@1033: drain("StdOut", stdout); jaroslav@1033: drain("StdErr", stderr); jaroslav@1033: process.destroy(); jaroslav@1033: int res; jaroslav@1033: try { jaroslav@1033: res = process.waitFor(); jaroslav@1033: } catch (InterruptedException ex) { jaroslav@1033: throw new IOException(ex); jaroslav@1033: } jaroslav@1033: LOG.log(Level.INFO, "Exit code: {0}", res); jaroslav@1033: jaroslav@1033: deleteTree((File)brwsr[1]); jaroslav@1033: } jaroslav@1033: jaroslav@1033: private static void drain(String name, InputStream is) throws IOException { jaroslav@1033: int av = is.available(); jaroslav@1033: if (av > 0) { jaroslav@1033: StringBuilder sb = new StringBuilder(); jaroslav@1033: sb.append("v== ").append(name).append(" ==v\n"); jaroslav@1033: while (av-- > 0) { jaroslav@1033: sb.append((char)is.read()); jaroslav@1033: } jaroslav@1033: sb.append("\n^== ").append(name).append(" ==^"); jaroslav@1033: LOG.log(Level.INFO, sb.toString()); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: private void deleteTree(File file) { jaroslav@1033: if (file == null) { jaroslav@1033: return; jaroslav@1033: } jaroslav@1033: File[] arr = file.listFiles(); jaroslav@1033: if (arr != null) { jaroslav@1033: for (File s : arr) { jaroslav@1033: deleteTree(s); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: file.delete(); jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1076: public HttpServer call() throws Exception { jaroslav@1076: return server; jaroslav@1076: } jaroslav@1076: jaroslav@1076: @Override jaroslav@1033: public void close() throws IOException { jaroslav@1033: shutdown(); jaroslav@1033: } jaroslav@1033: jaroslav@1041: protected Object[] showBrwsr(URI uri) throws IOException { jaroslav@1041: LOG.log(Level.INFO, "Showing {0}", uri); jaroslav@1041: if (cmd == null) { jaroslav@1041: try { jaroslav@1041: LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] { jaroslav@1041: System.getProperty("java.vm.name"), jaroslav@1041: System.getProperty("java.vm.vendor"), jaroslav@1041: System.getProperty("java.vm.version"), jaroslav@1041: }); jaroslav@1041: java.awt.Desktop.getDesktop().browse(uri); jaroslav@1041: LOG.log(Level.INFO, "Desktop.browse successfully finished"); jaroslav@1041: return null; jaroslav@1041: } catch (UnsupportedOperationException ex) { jaroslav@1041: LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage()); jaroslav@1041: LOG.log(Level.FINE, null, ex); jaroslav@1041: } jaroslav@1041: } jaroslav@1041: { jaroslav@1041: String cmdName = cmd == null ? "xdg-open" : cmd; jaroslav@1041: String[] cmdArr = { jaroslav@1041: cmdName, uri.toString() jaroslav@1041: }; jaroslav@1041: LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); jaroslav@1041: final Process process = Runtime.getRuntime().exec(cmdArr); jaroslav@1041: return new Object[] { process, null }; jaroslav@1041: } jaroslav@1041: } jaroslav@1041: jaroslav@1088: abstract void generateBck2BrwsrJS(StringBuilder sb, Res loader) throws IOException; jaroslav@1088: abstract String harnessResource(); jaroslav@1599: String compileJar(URL jar) throws IOException { jaroslav@1489: return null; jaroslav@1489: } jaroslav@1505: String compileFromClassPath(URL f, Res loader) throws IOException { jaroslav@1489: return null; jaroslav@1489: } jaroslav@1041: jaroslav@1249: private static URI pageURL(String protocol, HttpServer server, final String page) { jaroslav@1186: NetworkListener listener = server.getListeners().iterator().next(); jaroslav@1186: int port = listener.getPort(); jaroslav@1186: try { jaroslav@1249: return new URI(protocol + "://localhost:" + port + page); jaroslav@1186: } catch (URISyntaxException ex) { jaroslav@1186: throw new IllegalStateException(ex); jaroslav@1186: } jaroslav@1186: } jaroslav@1186: jaroslav@1489: final class Res { jaroslav@1530: private final Set ignore = new HashSet(); jaroslav@1530: jaroslav@1599: String compileJar(URL jarURL) throws IOException { jaroslav@1599: String ret = BaseHTTPLauncher.this.compileJar(jarURL); jaroslav@1530: ignore.add(jarURL); jaroslav@1530: return ret; jaroslav@1489: } jaroslav@1505: String compileFromClassPath(URL f) throws IOException { jaroslav@1504: return BaseHTTPLauncher.this.compileFromClassPath(f, this); jaroslav@1489: } jaroslav@1513: public URL get(String resource, int skip) throws IOException { jaroslav@1375: if (!resource.endsWith(".class")) { jaroslav@1375: return getResource(resource, skip); jaroslav@1375: } jaroslav@1098: URL u = null; jaroslav@1033: for (ClassLoader l : loaders) { jaroslav@1033: Enumeration en = l.getResources(resource); jaroslav@1033: while (en.hasMoreElements()) { jaroslav@1033: u = en.nextElement(); jaroslav@1098: if (u.toExternalForm().matches("^.*emul.*rt\\.jar.*$")) { jaroslav@1489: return u; jaroslav@1098: } jaroslav@1033: } jaroslav@1098: } jaroslav@1098: if (u != null) { jaroslav@1098: if (u.toExternalForm().contains("rt.jar")) { jaroslav@1347: LOG.log(Level.WARNING, "No fallback to bootclasspath for {0}", u); jaroslav@1347: return null; jaroslav@1033: } jaroslav@1489: return u; jaroslav@1033: } jaroslav@1033: throw new IOException("Can't find " + resource); jaroslav@1033: } jaroslav@1513: private URL getResource(String resource, int skip) throws IOException { jaroslav@1375: for (ClassLoader l : loaders) { jaroslav@1375: Enumeration en = l.getResources(resource); jaroslav@1375: while (en.hasMoreElements()) { jaroslav@1375: final URL now = en.nextElement(); jaroslav@1383: if (now.toExternalForm().contains("sisu-inject-bean")) { jaroslav@1383: // certainly we don't want this resource, as that jaroslav@1383: // module is not compiled with target 1.6, currently jaroslav@1383: continue; jaroslav@1383: } jaroslav@1530: if (now.getProtocol().equals("jar")) { jaroslav@1530: JarURLConnection juc = (JarURLConnection) now.openConnection(); jaroslav@1530: if (ignore.contains(juc.getJarFileURL())) { jaroslav@1530: continue; jaroslav@1530: } jaroslav@1530: } jaroslav@1375: if (--skip < 0) { jaroslav@1513: return now; jaroslav@1375: } jaroslav@1375: } jaroslav@1375: } jaroslav@1383: throw new IOException("Not found (anymore of) " + resource); jaroslav@1375: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: private static class Page extends HttpHandler { jaroslav@1033: final String resource; jaroslav@1033: private final String[] args; jaroslav@1033: private final Res res; jaroslav@1033: jaroslav@1033: public Page(Res res, String resource, String... args) { jaroslav@1033: this.res = res; jaroslav@1033: this.resource = resource; jaroslav@1033: this.args = args.length == 0 ? new String[] { "$0" } : args; jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: public void service(Request request, Response response) throws Exception { jaroslav@1033: String r = computePage(request); jaroslav@1033: if (r.startsWith("/")) { jaroslav@1033: r = r.substring(1); jaroslav@1033: } jaroslav@1033: String[] replace = {}; jaroslav@1033: if (r.endsWith(".html")) { jaroslav@1033: response.setContentType("text/html"); jaroslav@1033: LOG.info("Content type text/html"); jaroslav@1033: replace = args; jaroslav@1033: } jaroslav@1033: if (r.endsWith(".xhtml")) { jaroslav@1033: response.setContentType("application/xhtml+xml"); jaroslav@1033: LOG.info("Content type application/xhtml+xml"); jaroslav@1033: replace = args; jaroslav@1033: } jaroslav@1033: OutputStream os = response.getOutputStream(); jaroslav@1165: try { jaroslav@1513: InputStream is = res.get(r, 0).openStream(); jaroslav@1033: copyStream(is, os, request.getRequestURL().toString(), replace); jaroslav@1033: } catch (IOException ex) { jaroslav@1033: response.setDetailMessage(ex.getLocalizedMessage()); jaroslav@1033: response.setError(); jaroslav@1033: response.setStatus(404); jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: protected String computePage(Request request) { jaroslav@1033: String r = resource; jaroslav@1033: if (r == null) { jaroslav@1033: r = request.getHttpHandlerPath(); jaroslav@1033: } jaroslav@1033: return r; jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: private static class SubTree extends Page { jaroslav@1033: jaroslav@1033: public SubTree(Res res, String resource, String... args) { jaroslav@1033: super(res, resource, args); jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: protected String computePage(Request request) { jaroslav@1033: return resource + request.getHttpHandlerPath(); jaroslav@1033: } jaroslav@1033: jaroslav@1033: jaroslav@1033: } jaroslav@1033: jaroslav@1271: private class VMAndPages extends StaticHttpHandler { jaroslav@1271: private String vmResource; jaroslav@1271: jaroslav@1271: public VMAndPages() { jaroslav@1271: super((String[]) null); jaroslav@1271: } jaroslav@1271: jaroslav@1033: @Override jaroslav@1033: public void service(Request request, Response response) throws Exception { jaroslav@1271: if (request.getRequestURI().equals(vmResource)) { jaroslav@1271: response.setCharacterEncoding("UTF-8"); jaroslav@1271: response.setContentType("text/javascript"); jaroslav@1271: StringBuilder sb = new StringBuilder(); jaroslav@1271: generateBck2BrwsrJS(sb, BaseHTTPLauncher.this.resources); jaroslav@1271: response.getWriter().write(sb.toString()); jaroslav@1271: } else { jaroslav@1271: super.service(request, response); jaroslav@1271: } jaroslav@1271: } jaroslav@1271: jaroslav@1271: private void registerVM(String vmResource) { jaroslav@1271: this.vmResource = vmResource; jaroslav@1033: } jaroslav@1033: } jaroslav@1033: jaroslav@1033: private static class Classes extends HttpHandler { jaroslav@1033: private final Res loader; jaroslav@1033: jaroslav@1033: public Classes(Res loader) { jaroslav@1033: this.loader = loader; jaroslav@1033: } jaroslav@1033: jaroslav@1033: @Override jaroslav@1033: public void service(Request request, Response response) throws Exception { jaroslav@1033: String res = request.getHttpHandlerPath(); jaroslav@1033: if (res.startsWith("/")) { jaroslav@1033: res = res.substring(1); jaroslav@1033: } jaroslav@1513: String skip = request.getParameter("skip"); jaroslav@1513: int skipCnt = skip == null ? 0 : Integer.parseInt(skip); jaroslav@1513: URL url = loader.get(res, skipCnt); jaroslav@1530: if (url != null && !res.equals("META-INF/MANIFEST.MF")) try { jaroslav@1526: response.setCharacterEncoding("UTF-8"); jaroslav@1521: if (url.getProtocol().equals("jar")) { jaroslav@1521: JarURLConnection juc = (JarURLConnection) url.openConnection(); jaroslav@1599: String s = loader.compileJar(juc.getJarFileURL()); jaroslav@1521: if (s != null) { jaroslav@1521: Writer w = response.getWriter(); jaroslav@1521: w.append(s); jaroslav@1521: w.close(); jaroslav@1521: return; jaroslav@1521: } jaroslav@1489: } jaroslav@1521: if (url.getProtocol().equals("file")) { jaroslav@1614: final String filePart = url.getFile(); jaroslav@1614: if (filePart.endsWith(res)) { jaroslav@1614: url = new URL( jaroslav@1614: url.getProtocol(), jaroslav@1614: url.getHost(), jaroslav@1614: url.getPort(), jaroslav@1614: filePart.substring(0, filePart.length() - res.length()) jaroslav@1614: ); jaroslav@1614: } jaroslav@1521: String s = loader.compileFromClassPath(url); jaroslav@1521: if (s != null) { jaroslav@1521: Writer w = response.getWriter(); jaroslav@1521: w.append(s); jaroslav@1521: w.close(); jaroslav@1521: return; jaroslav@1521: } jaroslav@1489: } jaroslav@1521: } catch (IOException ex) { jaroslav@1521: LOG.log(Level.SEVERE, "Cannot handle " + res, ex); jaroslav@1489: } jaroslav@1165: InputStream is = null; jaroslav@1165: try { jaroslav@1530: if (url == null) { jaroslav@1530: throw new IOException("Resource not found"); jaroslav@1530: } jaroslav@1530: is = url.openStream(); jaroslav@1033: response.setContentType("text/javascript"); jaroslav@1033: Writer w = response.getWriter(); jaroslav@1489: w.append("(["); jaroslav@1033: for (int i = 0;; i++) { jaroslav@1033: int b = is.read(); jaroslav@1033: if (b == -1) { jaroslav@1033: break; jaroslav@1033: } jaroslav@1033: if (i > 0) { jaroslav@1033: w.append(", "); jaroslav@1033: } jaroslav@1033: if (i % 20 == 0) { jaroslav@1033: w.write("\n"); jaroslav@1033: } jaroslav@1033: if (b > 127) { jaroslav@1033: b = b - 256; jaroslav@1033: } jaroslav@1033: w.append(Integer.toString(b)); jaroslav@1033: } jaroslav@1489: w.append("\n])"); jaroslav@1530: } catch (IOException ex) { jaroslav@1033: response.setStatus(HttpStatus.NOT_FOUND_404); jaroslav@1033: response.setError(); jaroslav@1033: response.setDetailMessage(ex.getMessage()); jaroslav@1530: } finally { jaroslav@1165: if (is != null) { jaroslav@1165: is.close(); jaroslav@1165: } jaroslav@1530: } jaroslav@1033: } jaroslav@1513: jaroslav@1033: } jaroslav@1249: private static class WS extends WebSocketApplication { jaroslav@1249: jaroslav@1249: private final Resource r; jaroslav@1249: jaroslav@1249: private WS(Resource r) { jaroslav@1249: this.r = r; jaroslav@1249: } jaroslav@1249: jaroslav@1249: @Override jaroslav@1249: public void onMessage(WebSocket socket, String text) { jaroslav@1249: try { jaroslav@1249: r.httpContent.reset(); jaroslav@1249: ByteArrayOutputStream out = new ByteArrayOutputStream(); jaroslav@1249: copyStream(r.httpContent, out, null, text); jaroslav@1249: String s = new String(out.toByteArray(), "UTF-8"); jaroslav@1249: socket.send(s); jaroslav@1249: } catch (IOException ex) { jaroslav@1513: LOG.log(Level.WARNING, null, ex); jaroslav@1249: } jaroslav@1249: } jaroslav@1249: jaroslav@1249: }}