jaroslav@323: /** jaroslav@323: * Back 2 Browser Bytecode Translator jaroslav@323: * Copyright (C) 2012 Jaroslav Tulach jaroslav@323: * jaroslav@323: * This program is free software: you can redistribute it and/or modify jaroslav@323: * it under the terms of the GNU General Public License as published by jaroslav@323: * the Free Software Foundation, version 2 of the License. jaroslav@323: * jaroslav@323: * This program is distributed in the hope that it will be useful, jaroslav@323: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@323: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@323: * GNU General Public License for more details. jaroslav@323: * jaroslav@323: * You should have received a copy of the GNU General Public License jaroslav@323: * along with this program. Look for COPYING file in the top folder. jaroslav@323: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@323: */ jaroslav@323: package org.apidesign.bck2brwsr.launcher; jaroslav@323: jaroslav@382: import java.io.Closeable; jaroslav@365: import java.io.File; jaroslav@331: import java.io.IOException; jaroslav@323: import java.io.InputStream; jaroslav@356: import java.io.InterruptedIOException; jaroslav@323: import java.io.OutputStream; jaroslav@800: import java.io.UnsupportedEncodingException; jaroslav@323: import java.io.Writer; jaroslav@323: import java.net.URI; jaroslav@348: import java.net.URISyntaxException; jaroslav@323: import java.net.URL; jaroslav@348: import java.util.ArrayList; jaroslav@361: import java.util.Arrays; jaroslav@323: import java.util.Enumeration; jaroslav@348: import java.util.LinkedHashSet; jaroslav@348: import java.util.List; jaroslav@348: import java.util.Set; jaroslav@371: import java.util.concurrent.BlockingQueue; jaroslav@342: import java.util.concurrent.CountDownLatch; jaroslav@371: import java.util.concurrent.LinkedBlockingQueue; jaroslav@349: import java.util.concurrent.TimeUnit; jaroslav@362: import java.util.logging.Level; jaroslav@362: import java.util.logging.Logger; jaroslav@667: import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource; jaroslav@323: import org.apidesign.vm4brwsr.Bck2Brwsr; jaroslav@323: import org.glassfish.grizzly.PortRange; jaroslav@323: import org.glassfish.grizzly.http.server.HttpHandler; jaroslav@323: import org.glassfish.grizzly.http.server.HttpServer; jaroslav@323: import org.glassfish.grizzly.http.server.NetworkListener; jaroslav@323: import org.glassfish.grizzly.http.server.Request; jaroslav@323: import org.glassfish.grizzly.http.server.Response; jaroslav@323: import org.glassfish.grizzly.http.server.ServerConfiguration; jaroslav@745: import org.glassfish.grizzly.http.util.HttpStatus; jaroslav@323: jaroslav@323: /** jaroslav@357: * Lightweight server to launch Bck2Brwsr applications and tests. jaroslav@357: * Supports execution in native browser as well as Java's internal jaroslav@357: * execution engine. jaroslav@323: */ jaroslav@382: final class Bck2BrwsrLauncher extends Launcher implements Closeable { jaroslav@362: private static final Logger LOG = Logger.getLogger(Bck2BrwsrLauncher.class.getName()); jaroslav@622: private static final InvocationContext END = new InvocationContext(null, null, null); jaroslav@622: private final Set loaders = new LinkedHashSet<>(); jaroslav@622: private final BlockingQueue methods = new LinkedBlockingQueue<>(); jaroslav@349: private long timeOut; jaroslav@357: private final Res resources = new Res(); jaroslav@382: private final String cmd; jaroslav@371: private Object[] brwsr; jaroslav@371: private HttpServer server; jaroslav@371: private CountDownLatch wait; jaroslav@613: jaroslav@382: public Bck2BrwsrLauncher(String cmd) { jaroslav@382: this.cmd = cmd; jaroslav@382: } jaroslav@348: jaroslav@382: @Override jaroslav@622: InvocationContext runMethod(InvocationContext c) throws IOException { jaroslav@622: loaders.add(c.clazz.getClassLoader()); jaroslav@348: methods.add(c); jaroslav@371: try { jaroslav@371: c.await(timeOut); jaroslav@371: } catch (InterruptedException ex) { jaroslav@371: throw new IOException(ex); jaroslav@371: } jaroslav@348: return c; jaroslav@348: } jaroslav@348: jaroslav@349: public void setTimeout(long ms) { jaroslav@349: timeOut = ms; jaroslav@349: } jaroslav@348: jaroslav@357: public void addClassLoader(ClassLoader url) { jaroslav@357: this.loaders.add(url); jaroslav@357: } jaroslav@348: jaroslav@382: public void showURL(String startpage) throws IOException { jaroslav@371: if (!startpage.startsWith("/")) { jaroslav@371: startpage = "/" + startpage; jaroslav@371: } jaroslav@613: HttpServer s = initServer(".", true); jaroslav@742: int last = startpage.lastIndexOf('/'); jaroslav@742: String simpleName = startpage.substring(last); jaroslav@742: s.getServerConfiguration().addHttpHandler(new Page(resources, startpage), simpleName); jaroslav@371: s.getServerConfiguration().addHttpHandler(new Page(resources, null), "/"); jaroslav@382: try { jaroslav@742: launchServerAndBrwsr(s, simpleName); jaroslav@382: } catch (URISyntaxException | InterruptedException ex) { jaroslav@382: throw new IOException(ex); jaroslav@382: } jaroslav@371: } jaroslav@348: jaroslav@613: void showDirectory(File dir, String startpage) throws IOException { jaroslav@613: if (!startpage.startsWith("/")) { jaroslav@613: startpage = "/" + startpage; jaroslav@613: } jaroslav@613: HttpServer s = initServer(dir.getPath(), false); jaroslav@613: try { jaroslav@613: launchServerAndBrwsr(s, startpage); jaroslav@613: } catch (URISyntaxException | InterruptedException ex) { jaroslav@613: throw new IOException(ex); jaroslav@613: } jaroslav@613: } jaroslav@613: jaroslav@382: @Override jaroslav@371: public void initialize() throws IOException { jaroslav@356: try { jaroslav@371: executeInBrowser(); jaroslav@356: } catch (InterruptedException ex) { jaroslav@356: final InterruptedIOException iio = new InterruptedIOException(ex.getMessage()); jaroslav@356: iio.initCause(ex); jaroslav@356: throw iio; jaroslav@356: } catch (Exception ex) { jaroslav@356: if (ex instanceof IOException) { jaroslav@356: throw (IOException)ex; jaroslav@356: } jaroslav@356: if (ex instanceof RuntimeException) { jaroslav@356: throw (RuntimeException)ex; jaroslav@356: } jaroslav@356: throw new IOException(ex); jaroslav@356: } jaroslav@356: } jaroslav@356: jaroslav@613: private HttpServer initServer(String path, boolean addClasses) throws IOException { jaroslav@613: HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535)); jaroslav@357: jaroslav@371: final ServerConfiguration conf = s.getServerConfiguration(); jaroslav@613: if (addClasses) { jaroslav@741: conf.addHttpHandler(new VM(resources), "/bck2brwsr.js"); jaroslav@613: conf.addHttpHandler(new Classes(resources), "/classes/"); jaroslav@613: } jaroslav@371: return s; jaroslav@357: } jaroslav@357: jaroslav@357: private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException { jaroslav@371: wait = new CountDownLatch(1); jaroslav@613: server = initServer(".", true); jaroslav@623: final ServerConfiguration conf = server.getServerConfiguration(); jaroslav@623: jaroslav@623: class DynamicResourceHandler extends HttpHandler { jaroslav@623: private final InvocationContext ic; jaroslav@623: public DynamicResourceHandler(InvocationContext ic) { jaroslav@667: if (ic == null || ic.resources.isEmpty()) { jaroslav@623: throw new NullPointerException(); jaroslav@623: } jaroslav@623: this.ic = ic; jaroslav@667: for (Resource r : ic.resources) { jaroslav@667: conf.addHttpHandler(this, r.httpPath); jaroslav@667: } jaroslav@623: } jaroslav@623: jaroslav@623: public void close() { jaroslav@623: conf.removeHttpHandler(this); jaroslav@623: } jaroslav@623: jaroslav@623: @Override jaroslav@623: public void service(Request request, Response response) throws Exception { jaroslav@667: for (Resource r : ic.resources) { jaroslav@667: if (r.httpPath.equals(request.getRequestURI())) { jaroslav@667: LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI()); jaroslav@667: response.setContentType(r.httpType); jaroslav@667: copyStream(r.httpContent, response.getOutputStream(), null); jaroslav@667: } jaroslav@623: } jaroslav@623: } jaroslav@623: } jaroslav@623: jaroslav@357: conf.addHttpHandler(new Page(resources, jaroslav@357: "org/apidesign/bck2brwsr/launcher/harness.xhtml" jaroslav@357: ), "/execute"); jaroslav@623: jaroslav@332: conf.addHttpHandler(new HttpHandler() { jaroslav@332: int cnt; jaroslav@622: List cases = new ArrayList<>(); jaroslav@623: DynamicResourceHandler prev; jaroslav@332: @Override jaroslav@332: public void service(Request request, Response response) throws Exception { jaroslav@342: String id = request.getParameter("request"); jaroslav@342: String value = request.getParameter("result"); jaroslav@800: if (value != null && value.indexOf((char)0xC5) != -1) { jaroslav@800: value = toUTF8(value); jaroslav@800: } jaroslav@364: jaroslav@709: jaroslav@709: InvocationContext mi = null; jaroslav@709: int caseNmbr = -1; jaroslav@709: jaroslav@342: if (id != null && value != null) { jaroslav@364: LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value}); jaroslav@381: value = decodeURL(value); jaroslav@709: int indx = Integer.parseInt(id); jaroslav@709: cases.get(indx).result(value, null); jaroslav@709: if (++indx < cases.size()) { jaroslav@709: mi = cases.get(indx); jaroslav@709: LOG.log(Level.INFO, "Re-executing case {0}", indx); jaroslav@709: caseNmbr = indx; jaroslav@709: } jaroslav@709: } else { jaroslav@709: if (!cases.isEmpty()) { jaroslav@709: LOG.info("Re-executing test cases"); jaroslav@709: mi = cases.get(0); jaroslav@709: caseNmbr = 0; jaroslav@709: } jaroslav@342: } jaroslav@342: jaroslav@623: if (prev != null) { jaroslav@623: prev.close(); jaroslav@623: prev = null; jaroslav@623: } jaroslav@623: jaroslav@709: if (mi == null) { jaroslav@709: mi = methods.take(); jaroslav@709: caseNmbr = cnt++; jaroslav@709: } jaroslav@372: if (mi == END) { jaroslav@342: response.getWriter().write(""); jaroslav@342: wait.countDown(); jaroslav@342: cnt = 0; jaroslav@372: LOG.log(Level.INFO, "End of data reached. Exiting."); jaroslav@342: return; jaroslav@342: } jaroslav@342: jaroslav@667: if (!mi.resources.isEmpty()) { jaroslav@623: prev = new DynamicResourceHandler(mi); jaroslav@623: } jaroslav@623: jaroslav@371: cases.add(mi); jaroslav@622: final String cn = mi.clazz.getName(); jaroslav@371: final String mn = mi.methodName; jaroslav@709: LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn}); jaroslav@332: response.getWriter().write("{" jaroslav@364: + "className: '" + cn + "', " jaroslav@364: + "methodName: '" + mn + "', " jaroslav@709: + "request: " + caseNmbr jaroslav@526: ); jaroslav@526: if (mi.html != null) { jaroslav@526: response.getWriter().write(", html: '"); jaroslav@526: response.getWriter().write(encodeJSON(mi.html)); jaroslav@526: response.getWriter().write("'"); jaroslav@526: } jaroslav@526: response.getWriter().write("}"); jaroslav@332: } jaroslav@342: }, "/data"); jaroslav@357: jaroslav@371: this.brwsr = launchServerAndBrwsr(server, "/execute"); jaroslav@371: } jaroslav@371: jaroslav@526: private static String encodeJSON(String in) { jaroslav@526: StringBuilder sb = new StringBuilder(); jaroslav@526: for (int i = 0; i < in.length(); i++) { jaroslav@526: char ch = in.charAt(i); jaroslav@526: if (ch < 32 || ch == '\'' || ch == '"') { jaroslav@526: sb.append("\\u"); jaroslav@526: String hs = "0000" + Integer.toHexString(ch); jaroslav@526: hs = hs.substring(hs.length() - 4); jaroslav@526: sb.append(hs); jaroslav@526: } else { jaroslav@526: sb.append(ch); jaroslav@526: } jaroslav@526: } jaroslav@526: return sb.toString(); jaroslav@526: } jaroslav@526: jaroslav@382: @Override jaroslav@382: public void shutdown() throws IOException { jaroslav@372: methods.offer(END); jaroslav@363: for (;;) { jaroslav@371: int prev = methods.size(); jaroslav@382: try { jaroslav@389: if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) { jaroslav@382: break; jaroslav@382: } jaroslav@382: } catch (InterruptedException ex) { jaroslav@382: throw new IOException(ex); jaroslav@363: } jaroslav@371: if (prev == methods.size()) { jaroslav@363: LOG.log( jaroslav@363: Level.WARNING, jaroslav@363: "Timeout and no test has been executed meanwhile (at {0}). Giving up.", jaroslav@371: methods.size() jaroslav@363: ); jaroslav@363: break; jaroslav@363: } jaroslav@363: LOG.log(Level.INFO, jaroslav@363: "Timeout, but tests got from {0} to {1}. Trying again.", jaroslav@371: new Object[]{prev, methods.size()} jaroslav@363: ); jaroslav@363: } jaroslav@365: stopServerAndBrwsr(server, brwsr); jaroslav@323: } jaroslav@331: jaroslav@342: static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException { jaroslav@331: for (;;) { jaroslav@331: int ch = is.read(); jaroslav@331: if (ch == -1) { jaroslav@331: break; jaroslav@331: } jaroslav@523: if (ch == '$' && params.length > 0) { jaroslav@331: int cnt = is.read() - '0'; jaroslav@342: if (cnt == 'U' - '0') { jaroslav@714: os.write(baseURL.getBytes("UTF-8")); jaroslav@762: } else { jaroslav@762: if (cnt >= 0 && cnt < params.length) { jaroslav@762: os.write(params[cnt].getBytes("UTF-8")); jaroslav@762: } else { jaroslav@762: os.write('$'); jaroslav@762: os.write(cnt + '0'); jaroslav@762: } jaroslav@331: } jaroslav@331: } else { jaroslav@331: os.write(ch); jaroslav@331: } jaroslav@331: } jaroslav@331: } jaroslav@356: jaroslav@365: private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException { jaroslav@357: server.start(); jaroslav@357: NetworkListener listener = server.getListeners().iterator().next(); jaroslav@357: int port = listener.getPort(); jaroslav@357: jaroslav@357: URI uri = new URI("http://localhost:" + port + page); jaroslav@362: LOG.log(Level.INFO, "Showing {0}", uri); jaroslav@433: if (cmd == null) { jaroslav@433: try { jaroslav@536: LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] { jaroslav@536: System.getProperty("java.vm.name"), jaroslav@536: System.getProperty("java.vm.vendor"), jaroslav@536: System.getProperty("java.vm.version"), jaroslav@536: }); jaroslav@433: java.awt.Desktop.getDesktop().browse(uri); jaroslav@433: LOG.log(Level.INFO, "Desktop.browse successfully finished"); jaroslav@433: return null; jaroslav@433: } catch (UnsupportedOperationException ex) { jaroslav@518: LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage()); jaroslav@518: LOG.log(Level.FINE, null, ex); jaroslav@433: } jaroslav@369: } jaroslav@369: { jaroslav@382: String cmdName = cmd == null ? "xdg-open" : cmd; jaroslav@382: String[] cmdArr = { jaroslav@382: cmdName, uri.toString() jaroslav@357: }; jaroslav@382: LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr)); jaroslav@382: final Process process = Runtime.getRuntime().exec(cmdArr); jaroslav@369: return new Object[] { process, null }; jaroslav@357: } jaroslav@361: } jaroslav@800: private static String toUTF8(String value) throws UnsupportedEncodingException { jaroslav@800: byte[] arr = new byte[value.length()]; jaroslav@800: for (int i = 0; i < arr.length; i++) { jaroslav@800: arr[i] = (byte)value.charAt(i); jaroslav@800: } jaroslav@800: return new String(arr, "UTF-8"); jaroslav@800: } jaroslav@361: jaroslav@381: private static String decodeURL(String s) { jaroslav@381: for (;;) { jaroslav@381: int pos = s.indexOf('%'); jaroslav@381: if (pos == -1) { jaroslav@381: return s; jaroslav@381: } jaroslav@381: int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16); jaroslav@381: s = s.substring(0, pos) + (char)i + s.substring(pos + 2); jaroslav@381: } jaroslav@381: } jaroslav@381: jaroslav@382: private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException { jaroslav@389: if (brwsr == null) { jaroslav@389: return; jaroslav@389: } jaroslav@365: Process process = (Process)brwsr[0]; jaroslav@365: jaroslav@365: server.stop(); jaroslav@365: InputStream stdout = process.getInputStream(); jaroslav@365: InputStream stderr = process.getErrorStream(); jaroslav@365: drain("StdOut", stdout); jaroslav@365: drain("StdErr", stderr); jaroslav@365: process.destroy(); jaroslav@382: int res; jaroslav@382: try { jaroslav@382: res = process.waitFor(); jaroslav@382: } catch (InterruptedException ex) { jaroslav@382: throw new IOException(ex); jaroslav@382: } jaroslav@365: LOG.log(Level.INFO, "Exit code: {0}", res); jaroslav@365: jaroslav@365: deleteTree((File)brwsr[1]); jaroslav@365: } jaroslav@365: jaroslav@361: private static void drain(String name, InputStream is) throws IOException { jaroslav@361: int av = is.available(); jaroslav@361: if (av > 0) { jaroslav@362: StringBuilder sb = new StringBuilder(); jaroslav@362: sb.append("v== ").append(name).append(" ==v\n"); jaroslav@361: while (av-- > 0) { jaroslav@362: sb.append((char)is.read()); jaroslav@361: } jaroslav@362: sb.append("\n^== ").append(name).append(" ==^"); jaroslav@362: LOG.log(Level.INFO, sb.toString()); jaroslav@361: } jaroslav@357: } jaroslav@357: jaroslav@365: private void deleteTree(File file) { jaroslav@369: if (file == null) { jaroslav@369: return; jaroslav@369: } jaroslav@365: File[] arr = file.listFiles(); jaroslav@365: if (arr != null) { jaroslav@365: for (File s : arr) { jaroslav@365: deleteTree(s); jaroslav@365: } jaroslav@365: } jaroslav@365: file.delete(); jaroslav@365: } jaroslav@365: jaroslav@382: @Override jaroslav@382: public void close() throws IOException { jaroslav@382: shutdown(); jaroslav@382: } jaroslav@382: jaroslav@348: private class Res implements Bck2Brwsr.Resources { jaroslav@348: @Override jaroslav@348: public InputStream get(String resource) throws IOException { jaroslav@348: for (ClassLoader l : loaders) { jaroslav@348: URL u = null; jaroslav@348: Enumeration en = l.getResources(resource); jaroslav@348: while (en.hasMoreElements()) { jaroslav@348: u = en.nextElement(); jaroslav@348: } jaroslav@348: if (u != null) { jaroslav@348: return u.openStream(); jaroslav@348: } jaroslav@348: } jaroslav@348: throw new IOException("Can't find " + resource); jaroslav@348: } jaroslav@348: } jaroslav@330: jaroslav@332: private static class Page extends HttpHandler { jaroslav@332: private final String resource; jaroslav@331: private final String[] args; jaroslav@357: private final Res res; jaroslav@331: jaroslav@357: public Page(Res res, String resource, String... args) { jaroslav@357: this.res = res; jaroslav@332: this.resource = resource; jaroslav@526: this.args = args.length == 0 ? new String[] { "$0" } : args; jaroslav@330: } jaroslav@330: jaroslav@330: @Override jaroslav@330: public void service(Request request, Response response) throws Exception { jaroslav@357: String r = resource; jaroslav@357: if (r == null) { jaroslav@357: r = request.getHttpHandlerPath(); jaroslav@741: } jaroslav@741: if (r.startsWith("/")) { jaroslav@741: r = r.substring(1); jaroslav@357: } jaroslav@523: String[] replace = {}; jaroslav@433: if (r.endsWith(".html")) { jaroslav@357: response.setContentType("text/html"); jaroslav@433: LOG.info("Content type text/html"); jaroslav@523: replace = args; jaroslav@433: } jaroslav@433: if (r.endsWith(".xhtml")) { jaroslav@433: response.setContentType("application/xhtml+xml"); jaroslav@433: LOG.info("Content type application/xhtml+xml"); jaroslav@523: replace = args; jaroslav@357: } jaroslav@330: OutputStream os = response.getOutputStream(); jaroslav@357: try (InputStream is = res.get(r)) { jaroslav@523: copyStream(is, os, request.getRequestURL().toString(), replace); jaroslav@357: } catch (IOException ex) { jaroslav@357: response.setDetailMessage(ex.getLocalizedMessage()); jaroslav@357: response.setError(); jaroslav@357: response.setStatus(404); jaroslav@357: } jaroslav@330: } jaroslav@330: } jaroslav@330: jaroslav@330: private static class VM extends HttpHandler { jaroslav@580: private final String bck2brwsr; jaroslav@330: jaroslav@580: public VM(Res loader) throws IOException { jaroslav@580: StringBuilder sb = new StringBuilder(); jaroslav@580: Bck2Brwsr.generate(sb, loader); jaroslav@583: sb.append( jaroslav@741: "(function WrapperVM(global) {" jaroslav@741: + " function ldCls(res) {\n" jaroslav@741: + " var request = new XMLHttpRequest();\n" jaroslav@741: + " request.open('GET', '/classes/' + res, false);\n" jaroslav@741: + " request.send();\n" jaroslav@745: + " if (request.status !== 200) return null;\n" jaroslav@741: + " var arr = eval('(' + request.responseText + ')');\n" jaroslav@741: + " return arr;\n" jaroslav@741: + " }\n" jaroslav@741: + " var prevvm = global.bck2brwsr;\n" jaroslav@741: + " global.bck2brwsr = function() {\n" jaroslav@745: + " var args = Array.prototype.slice.apply(arguments);\n" jaroslav@745: + " args.unshift(ldCls);\n" jaroslav@745: + " return prevvm.apply(null, args);\n" jaroslav@741: + " };\n" jaroslav@741: + "})(this);\n" jaroslav@583: ); jaroslav@580: this.bck2brwsr = sb.toString(); jaroslav@330: } jaroslav@330: jaroslav@330: @Override jaroslav@330: public void service(Request request, Response response) throws Exception { jaroslav@330: response.setCharacterEncoding("UTF-8"); jaroslav@330: response.setContentType("text/javascript"); jaroslav@580: response.getWriter().write(bck2brwsr); jaroslav@330: } jaroslav@330: } jaroslav@330: jaroslav@330: private static class Classes extends HttpHandler { jaroslav@348: private final Res loader; jaroslav@330: jaroslav@348: public Classes(Res loader) { jaroslav@330: this.loader = loader; jaroslav@330: } jaroslav@330: jaroslav@330: @Override jaroslav@330: public void service(Request request, Response response) throws Exception { jaroslav@330: String res = request.getHttpHandlerPath(); jaroslav@330: if (res.startsWith("/")) { jaroslav@330: res = res.substring(1); jaroslav@330: } jaroslav@348: try (InputStream is = loader.get(res)) { jaroslav@348: response.setContentType("text/javascript"); jaroslav@348: Writer w = response.getWriter(); jaroslav@348: w.append("["); jaroslav@348: for (int i = 0;; i++) { jaroslav@348: int b = is.read(); jaroslav@348: if (b == -1) { jaroslav@348: break; jaroslav@348: } jaroslav@348: if (i > 0) { jaroslav@348: w.append(", "); jaroslav@348: } jaroslav@348: if (i % 20 == 0) { jaroslav@348: w.write("\n"); jaroslav@348: } jaroslav@348: if (b > 127) { jaroslav@348: b = b - 256; jaroslav@348: } jaroslav@348: w.append(Integer.toString(b)); jaroslav@348: } jaroslav@348: w.append("\n]"); jaroslav@348: } catch (IOException ex) { jaroslav@745: response.setStatus(HttpStatus.NOT_FOUND_404); jaroslav@348: response.setError(); jaroslav@348: response.setDetailMessage(ex.getMessage()); jaroslav@330: } jaroslav@330: } jaroslav@330: } jaroslav@323: }