1.1 --- a/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Sun Apr 28 17:11:12 2013 +0200
1.2 +++ b/launcher/api/src/main/java/org/apidesign/bck2brwsr/launcher/Launcher.java Sun Apr 28 17:42:49 2013 +0200
1.3 @@ -79,12 +79,20 @@
1.4 * @return launcher executing in external browser.
1.5 */
1.6 public static Launcher createBrowser(String cmd) {
1.7 + String msg = "Trying to create browser '" + cmd + "'";
1.8 try {
1.9 - Class<?> c = loadClass("org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher");
1.10 + Class<?> c;
1.11 + if ("fx".equals(cmd)) { // NOI18N
1.12 + msg = "Please include org.apidesign.bck2brwsr:launcher.fx dependency!";
1.13 + c = loadClass("org.apidesign.bck2brwsr.launcher.FXBrwsrLauncher"); // NOI18N
1.14 + } else {
1.15 + msg = "Please include org.apidesign.bck2brwsr:launcher.html dependency!";
1.16 + c = loadClass("org.apidesign.bck2brwsr.launcher.Bck2BrwsrLauncher"); // NOI18N
1.17 + }
1.18 Constructor<?> cnstr = c.getConstructor(String.class);
1.19 return (Launcher) cnstr.newInstance(cmd);
1.20 } catch (Exception ex) {
1.21 - throw new IllegalStateException("Please include org.apidesign.bck2brwsr:launcher.html dependency!", ex);
1.22 + throw new IllegalStateException(msg, ex);
1.23 }
1.24 }
1.25
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/launcher/fx/pom.xml Sun Apr 28 17:42:49 2013 +0200
2.3 @@ -0,0 +1,54 @@
2.4 +<?xml version="1.0"?>
2.5 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
2.6 + <modelVersion>4.0.0</modelVersion>
2.7 + <parent>
2.8 + <groupId>org.apidesign.bck2brwsr</groupId>
2.9 + <artifactId>launcher-pom</artifactId>
2.10 + <version>0.7-SNAPSHOT</version>
2.11 + </parent>
2.12 + <groupId>org.apidesign.bck2brwsr</groupId>
2.13 + <artifactId>launcher.fx</artifactId>
2.14 + <version>0.7-SNAPSHOT</version>
2.15 + <name>FXBrwsr Launcher</name>
2.16 + <url>http://maven.apache.org</url>
2.17 + <build>
2.18 + <plugins>
2.19 + <plugin>
2.20 + <groupId>org.apache.maven.plugins</groupId>
2.21 + <artifactId>maven-compiler-plugin</artifactId>
2.22 + <version>2.3.2</version>
2.23 + <configuration>
2.24 + <source>1.7</source>
2.25 + <target>1.7</target>
2.26 + </configuration>
2.27 + </plugin>
2.28 + </plugins>
2.29 + </build>
2.30 + <properties>
2.31 + <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
2.32 + </properties>
2.33 + <dependencies>
2.34 + <dependency>
2.35 + <groupId>${project.groupId}</groupId>
2.36 + <artifactId>launcher</artifactId>
2.37 + <version>${project.version}</version>
2.38 + </dependency>
2.39 + <dependency>
2.40 + <groupId>org.glassfish.grizzly</groupId>
2.41 + <artifactId>grizzly-http-server</artifactId>
2.42 + <version>2.2.19</version>
2.43 + </dependency>
2.44 + <dependency>
2.45 + <groupId>com.oracle</groupId>
2.46 + <artifactId>javafx</artifactId>
2.47 + <version>2.2</version>
2.48 + <scope>system</scope>
2.49 + <systemPath>${java.home}/lib/jfxrt.jar</systemPath>
2.50 + </dependency>
2.51 + <dependency>
2.52 + <groupId>${project.groupId}</groupId>
2.53 + <artifactId>vm4brwsr</artifactId>
2.54 + <version>${project.version}</version>
2.55 + </dependency>
2.56 + </dependencies>
2.57 +</project>
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/BaseHTTPLauncher.java Sun Apr 28 17:42:49 2013 +0200
3.3 @@ -0,0 +1,609 @@
3.4 +/**
3.5 + * Back 2 Browser Bytecode Translator
3.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
3.7 + *
3.8 + * This program is free software: you can redistribute it and/or modify
3.9 + * it under the terms of the GNU General Public License as published by
3.10 + * the Free Software Foundation, version 2 of the License.
3.11 + *
3.12 + * This program is distributed in the hope that it will be useful,
3.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
3.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3.15 + * GNU General Public License for more details.
3.16 + *
3.17 + * You should have received a copy of the GNU General Public License
3.18 + * along with this program. Look for COPYING file in the top folder.
3.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
3.20 + */
3.21 +package org.apidesign.bck2brwsr.launcher;
3.22 +
3.23 +import java.io.Closeable;
3.24 +import java.io.File;
3.25 +import java.io.IOException;
3.26 +import java.io.InputStream;
3.27 +import java.io.InterruptedIOException;
3.28 +import java.io.OutputStream;
3.29 +import java.io.UnsupportedEncodingException;
3.30 +import java.io.Writer;
3.31 +import java.net.URI;
3.32 +import java.net.URISyntaxException;
3.33 +import java.net.URL;
3.34 +import java.util.ArrayList;
3.35 +import java.util.Arrays;
3.36 +import java.util.Enumeration;
3.37 +import java.util.LinkedHashSet;
3.38 +import java.util.List;
3.39 +import java.util.Set;
3.40 +import java.util.concurrent.BlockingQueue;
3.41 +import java.util.concurrent.CountDownLatch;
3.42 +import java.util.concurrent.LinkedBlockingQueue;
3.43 +import java.util.concurrent.TimeUnit;
3.44 +import java.util.logging.Level;
3.45 +import java.util.logging.Logger;
3.46 +import org.apidesign.bck2brwsr.launcher.InvocationContext.Resource;
3.47 +import org.apidesign.vm4brwsr.Bck2Brwsr;
3.48 +import org.glassfish.grizzly.PortRange;
3.49 +import org.glassfish.grizzly.http.server.HttpHandler;
3.50 +import org.glassfish.grizzly.http.server.HttpServer;
3.51 +import org.glassfish.grizzly.http.server.NetworkListener;
3.52 +import org.glassfish.grizzly.http.server.Request;
3.53 +import org.glassfish.grizzly.http.server.Response;
3.54 +import org.glassfish.grizzly.http.server.ServerConfiguration;
3.55 +import org.glassfish.grizzly.http.util.HttpStatus;
3.56 +
3.57 +/**
3.58 + * Lightweight server to launch Bck2Brwsr applications and tests.
3.59 + * Supports execution in native browser as well as Java's internal
3.60 + * execution engine.
3.61 + */
3.62 +class BaseHTTPLauncher extends Launcher implements Closeable {
3.63 + private static final Logger LOG = Logger.getLogger(BaseHTTPLauncher.class.getName());
3.64 + private static final InvocationContext END = new InvocationContext(null, null, null);
3.65 + private final Set<ClassLoader> loaders = new LinkedHashSet<>();
3.66 + private final BlockingQueue<InvocationContext> methods = new LinkedBlockingQueue<>();
3.67 + private long timeOut;
3.68 + private final Res resources = new Res();
3.69 + private final String cmd;
3.70 + private Object[] brwsr;
3.71 + private HttpServer server;
3.72 + private CountDownLatch wait;
3.73 +
3.74 + public BaseHTTPLauncher(String cmd) {
3.75 + this.cmd = cmd;
3.76 + addClassLoader(Bck2Brwsr.class.getClassLoader());
3.77 + setTimeout(180000);
3.78 + }
3.79 +
3.80 + @Override
3.81 + InvocationContext runMethod(InvocationContext c) throws IOException {
3.82 + loaders.add(c.clazz.getClassLoader());
3.83 + methods.add(c);
3.84 + try {
3.85 + c.await(timeOut);
3.86 + } catch (InterruptedException ex) {
3.87 + throw new IOException(ex);
3.88 + }
3.89 + return c;
3.90 + }
3.91 +
3.92 + public void setTimeout(long ms) {
3.93 + timeOut = ms;
3.94 + }
3.95 +
3.96 + public void addClassLoader(ClassLoader url) {
3.97 + this.loaders.add(url);
3.98 + }
3.99 +
3.100 + ClassLoader[] loaders() {
3.101 + return loaders.toArray(new ClassLoader[loaders.size()]);
3.102 + }
3.103 +
3.104 + public void showURL(String startpage) throws IOException {
3.105 + if (!startpage.startsWith("/")) {
3.106 + startpage = "/" + startpage;
3.107 + }
3.108 + HttpServer s = initServer(".", true);
3.109 + int last = startpage.lastIndexOf('/');
3.110 + String prefix = startpage.substring(0, last);
3.111 + String simpleName = startpage.substring(last);
3.112 + s.getServerConfiguration().addHttpHandler(new SubTree(resources, prefix), "/");
3.113 + try {
3.114 + launchServerAndBrwsr(s, simpleName);
3.115 + } catch (URISyntaxException | InterruptedException ex) {
3.116 + throw new IOException(ex);
3.117 + }
3.118 + }
3.119 +
3.120 + void showDirectory(File dir, String startpage) throws IOException {
3.121 + if (!startpage.startsWith("/")) {
3.122 + startpage = "/" + startpage;
3.123 + }
3.124 + HttpServer s = initServer(dir.getPath(), false);
3.125 + try {
3.126 + launchServerAndBrwsr(s, startpage);
3.127 + } catch (URISyntaxException | InterruptedException ex) {
3.128 + throw new IOException(ex);
3.129 + }
3.130 + }
3.131 +
3.132 + @Override
3.133 + public void initialize() throws IOException {
3.134 + try {
3.135 + executeInBrowser();
3.136 + } catch (InterruptedException ex) {
3.137 + final InterruptedIOException iio = new InterruptedIOException(ex.getMessage());
3.138 + iio.initCause(ex);
3.139 + throw iio;
3.140 + } catch (Exception ex) {
3.141 + if (ex instanceof IOException) {
3.142 + throw (IOException)ex;
3.143 + }
3.144 + if (ex instanceof RuntimeException) {
3.145 + throw (RuntimeException)ex;
3.146 + }
3.147 + throw new IOException(ex);
3.148 + }
3.149 + }
3.150 +
3.151 + private HttpServer initServer(String path, boolean addClasses) throws IOException {
3.152 + HttpServer s = HttpServer.createSimpleServer(path, new PortRange(8080, 65535));
3.153 +
3.154 + final ServerConfiguration conf = s.getServerConfiguration();
3.155 + if (addClasses) {
3.156 + conf.addHttpHandler(new VM(), "/bck2brwsr.js");
3.157 + conf.addHttpHandler(new Classes(resources), "/classes/");
3.158 + }
3.159 + return s;
3.160 + }
3.161 +
3.162 + private void executeInBrowser() throws InterruptedException, URISyntaxException, IOException {
3.163 + wait = new CountDownLatch(1);
3.164 + server = initServer(".", true);
3.165 + final ServerConfiguration conf = server.getServerConfiguration();
3.166 +
3.167 + class DynamicResourceHandler extends HttpHandler {
3.168 + private final InvocationContext ic;
3.169 + public DynamicResourceHandler(InvocationContext ic) {
3.170 + if (ic == null || ic.resources.isEmpty()) {
3.171 + throw new NullPointerException();
3.172 + }
3.173 + this.ic = ic;
3.174 + for (Resource r : ic.resources) {
3.175 + conf.addHttpHandler(this, r.httpPath);
3.176 + }
3.177 + }
3.178 +
3.179 + public void close() {
3.180 + conf.removeHttpHandler(this);
3.181 + }
3.182 +
3.183 + @Override
3.184 + public void service(Request request, Response response) throws Exception {
3.185 + for (Resource r : ic.resources) {
3.186 + if (r.httpPath.equals(request.getRequestURI())) {
3.187 + LOG.log(Level.INFO, "Serving HttpResource for {0}", request.getRequestURI());
3.188 + response.setContentType(r.httpType);
3.189 + r.httpContent.reset();
3.190 + String[] params = null;
3.191 + if (r.parameters.length != 0) {
3.192 + params = new String[r.parameters.length];
3.193 + for (int i = 0; i < r.parameters.length; i++) {
3.194 + params[i] = request.getParameter(r.parameters[i]);
3.195 + }
3.196 + }
3.197 +
3.198 + copyStream(r.httpContent, response.getOutputStream(), null, params);
3.199 + }
3.200 + }
3.201 + }
3.202 + }
3.203 +
3.204 + conf.addHttpHandler(new Page(resources,
3.205 + "org/apidesign/bck2brwsr/launcher/fximpl/harness.xhtml"
3.206 + ), "/execute");
3.207 +
3.208 + conf.addHttpHandler(new HttpHandler() {
3.209 + int cnt;
3.210 + List<InvocationContext> cases = new ArrayList<>();
3.211 + DynamicResourceHandler prev;
3.212 + @Override
3.213 + public void service(Request request, Response response) throws Exception {
3.214 + String id = request.getParameter("request");
3.215 + String value = request.getParameter("result");
3.216 + if (value != null && value.indexOf((char)0xC5) != -1) {
3.217 + value = toUTF8(value);
3.218 + }
3.219 +
3.220 +
3.221 + InvocationContext mi = null;
3.222 + int caseNmbr = -1;
3.223 +
3.224 + if (id != null && value != null) {
3.225 + LOG.log(Level.INFO, "Received result for case {0} = {1}", new Object[]{id, value});
3.226 + value = decodeURL(value);
3.227 + int indx = Integer.parseInt(id);
3.228 + cases.get(indx).result(value, null);
3.229 + if (++indx < cases.size()) {
3.230 + mi = cases.get(indx);
3.231 + LOG.log(Level.INFO, "Re-executing case {0}", indx);
3.232 + caseNmbr = indx;
3.233 + }
3.234 + } else {
3.235 + if (!cases.isEmpty()) {
3.236 + LOG.info("Re-executing test cases");
3.237 + mi = cases.get(0);
3.238 + caseNmbr = 0;
3.239 + }
3.240 + }
3.241 +
3.242 + if (prev != null) {
3.243 + prev.close();
3.244 + prev = null;
3.245 + }
3.246 +
3.247 + if (mi == null) {
3.248 + mi = methods.take();
3.249 + caseNmbr = cnt++;
3.250 + }
3.251 + if (mi == END) {
3.252 + response.getWriter().write("");
3.253 + wait.countDown();
3.254 + cnt = 0;
3.255 + LOG.log(Level.INFO, "End of data reached. Exiting.");
3.256 + return;
3.257 + }
3.258 +
3.259 + if (!mi.resources.isEmpty()) {
3.260 + prev = new DynamicResourceHandler(mi);
3.261 + }
3.262 +
3.263 + cases.add(mi);
3.264 + final String cn = mi.clazz.getName();
3.265 + final String mn = mi.methodName;
3.266 + LOG.log(Level.INFO, "Request for {0} case. Sending {1}.{2}", new Object[]{caseNmbr, cn, mn});
3.267 + response.getWriter().write("{"
3.268 + + "className: '" + cn + "', "
3.269 + + "methodName: '" + mn + "', "
3.270 + + "request: " + caseNmbr
3.271 + );
3.272 + if (mi.html != null) {
3.273 + response.getWriter().write(", html: '");
3.274 + response.getWriter().write(encodeJSON(mi.html));
3.275 + response.getWriter().write("'");
3.276 + }
3.277 + response.getWriter().write("}");
3.278 + }
3.279 + }, "/data");
3.280 +
3.281 + this.brwsr = launchServerAndBrwsr(server, "/execute");
3.282 + }
3.283 +
3.284 + private static String encodeJSON(String in) {
3.285 + StringBuilder sb = new StringBuilder();
3.286 + for (int i = 0; i < in.length(); i++) {
3.287 + char ch = in.charAt(i);
3.288 + if (ch < 32 || ch == '\'' || ch == '"') {
3.289 + sb.append("\\u");
3.290 + String hs = "0000" + Integer.toHexString(ch);
3.291 + hs = hs.substring(hs.length() - 4);
3.292 + sb.append(hs);
3.293 + } else {
3.294 + sb.append(ch);
3.295 + }
3.296 + }
3.297 + return sb.toString();
3.298 + }
3.299 +
3.300 + @Override
3.301 + public void shutdown() throws IOException {
3.302 + methods.offer(END);
3.303 + for (;;) {
3.304 + int prev = methods.size();
3.305 + try {
3.306 + if (wait != null && wait.await(timeOut, TimeUnit.MILLISECONDS)) {
3.307 + break;
3.308 + }
3.309 + } catch (InterruptedException ex) {
3.310 + throw new IOException(ex);
3.311 + }
3.312 + if (prev == methods.size()) {
3.313 + LOG.log(
3.314 + Level.WARNING,
3.315 + "Timeout and no test has been executed meanwhile (at {0}). Giving up.",
3.316 + methods.size()
3.317 + );
3.318 + break;
3.319 + }
3.320 + LOG.log(Level.INFO,
3.321 + "Timeout, but tests got from {0} to {1}. Trying again.",
3.322 + new Object[]{prev, methods.size()}
3.323 + );
3.324 + }
3.325 + stopServerAndBrwsr(server, brwsr);
3.326 + }
3.327 +
3.328 + static void copyStream(InputStream is, OutputStream os, String baseURL, String... params) throws IOException {
3.329 + for (;;) {
3.330 + int ch = is.read();
3.331 + if (ch == -1) {
3.332 + break;
3.333 + }
3.334 + if (ch == '$' && params.length > 0) {
3.335 + int cnt = is.read() - '0';
3.336 + if (baseURL != null && cnt == 'U' - '0') {
3.337 + os.write(baseURL.getBytes("UTF-8"));
3.338 + } else {
3.339 + if (cnt >= 0 && cnt < params.length) {
3.340 + os.write(params[cnt].getBytes("UTF-8"));
3.341 + } else {
3.342 + os.write('$');
3.343 + os.write(cnt + '0');
3.344 + }
3.345 + }
3.346 + } else {
3.347 + os.write(ch);
3.348 + }
3.349 + }
3.350 + }
3.351 +
3.352 + private Object[] launchServerAndBrwsr(HttpServer server, final String page) throws IOException, URISyntaxException, InterruptedException {
3.353 + server.start();
3.354 + NetworkListener listener = server.getListeners().iterator().next();
3.355 + int port = listener.getPort();
3.356 +
3.357 + URI uri = new URI("http://localhost:" + port + page);
3.358 + return showBrwsr(uri);
3.359 + }
3.360 + private static String toUTF8(String value) throws UnsupportedEncodingException {
3.361 + byte[] arr = new byte[value.length()];
3.362 + for (int i = 0; i < arr.length; i++) {
3.363 + arr[i] = (byte)value.charAt(i);
3.364 + }
3.365 + return new String(arr, "UTF-8");
3.366 + }
3.367 +
3.368 + private static String decodeURL(String s) {
3.369 + for (;;) {
3.370 + int pos = s.indexOf('%');
3.371 + if (pos == -1) {
3.372 + return s;
3.373 + }
3.374 + int i = Integer.parseInt(s.substring(pos + 1, pos + 2), 16);
3.375 + s = s.substring(0, pos) + (char)i + s.substring(pos + 2);
3.376 + }
3.377 + }
3.378 +
3.379 + private void stopServerAndBrwsr(HttpServer server, Object[] brwsr) throws IOException {
3.380 + if (brwsr == null) {
3.381 + return;
3.382 + }
3.383 + Process process = (Process)brwsr[0];
3.384 +
3.385 + server.stop();
3.386 + InputStream stdout = process.getInputStream();
3.387 + InputStream stderr = process.getErrorStream();
3.388 + drain("StdOut", stdout);
3.389 + drain("StdErr", stderr);
3.390 + process.destroy();
3.391 + int res;
3.392 + try {
3.393 + res = process.waitFor();
3.394 + } catch (InterruptedException ex) {
3.395 + throw new IOException(ex);
3.396 + }
3.397 + LOG.log(Level.INFO, "Exit code: {0}", res);
3.398 +
3.399 + deleteTree((File)brwsr[1]);
3.400 + }
3.401 +
3.402 + private static void drain(String name, InputStream is) throws IOException {
3.403 + int av = is.available();
3.404 + if (av > 0) {
3.405 + StringBuilder sb = new StringBuilder();
3.406 + sb.append("v== ").append(name).append(" ==v\n");
3.407 + while (av-- > 0) {
3.408 + sb.append((char)is.read());
3.409 + }
3.410 + sb.append("\n^== ").append(name).append(" ==^");
3.411 + LOG.log(Level.INFO, sb.toString());
3.412 + }
3.413 + }
3.414 +
3.415 + private void deleteTree(File file) {
3.416 + if (file == null) {
3.417 + return;
3.418 + }
3.419 + File[] arr = file.listFiles();
3.420 + if (arr != null) {
3.421 + for (File s : arr) {
3.422 + deleteTree(s);
3.423 + }
3.424 + }
3.425 + file.delete();
3.426 + }
3.427 +
3.428 + @Override
3.429 + public void close() throws IOException {
3.430 + shutdown();
3.431 + }
3.432 +
3.433 + protected Object[] showBrwsr(URI uri) throws IOException {
3.434 + LOG.log(Level.INFO, "Showing {0}", uri);
3.435 + if (cmd == null) {
3.436 + try {
3.437 + LOG.log(Level.INFO, "Trying Desktop.browse on {0} {2} by {1}", new Object[] {
3.438 + System.getProperty("java.vm.name"),
3.439 + System.getProperty("java.vm.vendor"),
3.440 + System.getProperty("java.vm.version"),
3.441 + });
3.442 + java.awt.Desktop.getDesktop().browse(uri);
3.443 + LOG.log(Level.INFO, "Desktop.browse successfully finished");
3.444 + return null;
3.445 + } catch (UnsupportedOperationException ex) {
3.446 + LOG.log(Level.INFO, "Desktop.browse not supported: {0}", ex.getMessage());
3.447 + LOG.log(Level.FINE, null, ex);
3.448 + }
3.449 + }
3.450 + {
3.451 + String cmdName = cmd == null ? "xdg-open" : cmd;
3.452 + String[] cmdArr = {
3.453 + cmdName, uri.toString()
3.454 + };
3.455 + LOG.log(Level.INFO, "Launching {0}", Arrays.toString(cmdArr));
3.456 + final Process process = Runtime.getRuntime().exec(cmdArr);
3.457 + return new Object[] { process, null };
3.458 + }
3.459 + }
3.460 +
3.461 + void generateBck2BrwsrJS(StringBuilder sb, Bck2Brwsr.Resources loader) throws IOException {
3.462 + Bck2Brwsr.generate(sb, loader);
3.463 + sb.append(
3.464 + "(function WrapperVM(global) {"
3.465 + + " function ldCls(res) {\n"
3.466 + + " var request = new XMLHttpRequest();\n"
3.467 + + " request.open('GET', '/classes/' + res, false);\n"
3.468 + + " request.send();\n"
3.469 + + " if (request.status !== 200) return null;\n"
3.470 + + " var arr = eval('(' + request.responseText + ')');\n"
3.471 + + " return arr;\n"
3.472 + + " }\n"
3.473 + + " var prevvm = global.bck2brwsr;\n"
3.474 + + " global.bck2brwsr = function() {\n"
3.475 + + " var args = Array.prototype.slice.apply(arguments);\n"
3.476 + + " args.unshift(ldCls);\n"
3.477 + + " return prevvm.apply(null, args);\n"
3.478 + + " };\n"
3.479 + + "})(this);\n");
3.480 + }
3.481 +
3.482 + private class Res implements Bck2Brwsr.Resources {
3.483 + @Override
3.484 + public InputStream get(String resource) throws IOException {
3.485 + for (ClassLoader l : loaders) {
3.486 + URL u = null;
3.487 + Enumeration<URL> en = l.getResources(resource);
3.488 + while (en.hasMoreElements()) {
3.489 + u = en.nextElement();
3.490 + }
3.491 + if (u != null) {
3.492 + return u.openStream();
3.493 + }
3.494 + }
3.495 + throw new IOException("Can't find " + resource);
3.496 + }
3.497 + }
3.498 +
3.499 + private static class Page extends HttpHandler {
3.500 + final String resource;
3.501 + private final String[] args;
3.502 + private final Res res;
3.503 +
3.504 + public Page(Res res, String resource, String... args) {
3.505 + this.res = res;
3.506 + this.resource = resource;
3.507 + this.args = args.length == 0 ? new String[] { "$0" } : args;
3.508 + }
3.509 +
3.510 + @Override
3.511 + public void service(Request request, Response response) throws Exception {
3.512 + String r = computePage(request);
3.513 + if (r.startsWith("/")) {
3.514 + r = r.substring(1);
3.515 + }
3.516 + String[] replace = {};
3.517 + if (r.endsWith(".html")) {
3.518 + response.setContentType("text/html");
3.519 + LOG.info("Content type text/html");
3.520 + replace = args;
3.521 + }
3.522 + if (r.endsWith(".xhtml")) {
3.523 + response.setContentType("application/xhtml+xml");
3.524 + LOG.info("Content type application/xhtml+xml");
3.525 + replace = args;
3.526 + }
3.527 + OutputStream os = response.getOutputStream();
3.528 + try (InputStream is = res.get(r)) {
3.529 + copyStream(is, os, request.getRequestURL().toString(), replace);
3.530 + } catch (IOException ex) {
3.531 + response.setDetailMessage(ex.getLocalizedMessage());
3.532 + response.setError();
3.533 + response.setStatus(404);
3.534 + }
3.535 + }
3.536 +
3.537 + protected String computePage(Request request) {
3.538 + String r = resource;
3.539 + if (r == null) {
3.540 + r = request.getHttpHandlerPath();
3.541 + }
3.542 + return r;
3.543 + }
3.544 + }
3.545 +
3.546 + private static class SubTree extends Page {
3.547 +
3.548 + public SubTree(Res res, String resource, String... args) {
3.549 + super(res, resource, args);
3.550 + }
3.551 +
3.552 + @Override
3.553 + protected String computePage(Request request) {
3.554 + return resource + request.getHttpHandlerPath();
3.555 + }
3.556 +
3.557 +
3.558 + }
3.559 +
3.560 + private class VM extends HttpHandler {
3.561 + @Override
3.562 + public void service(Request request, Response response) throws Exception {
3.563 + response.setCharacterEncoding("UTF-8");
3.564 + response.setContentType("text/javascript");
3.565 + StringBuilder sb = new StringBuilder();
3.566 + generateBck2BrwsrJS(sb, BaseHTTPLauncher.this.resources);
3.567 + response.getWriter().write(sb.toString());
3.568 + }
3.569 + }
3.570 +
3.571 + private static class Classes extends HttpHandler {
3.572 + private final Res loader;
3.573 +
3.574 + public Classes(Res loader) {
3.575 + this.loader = loader;
3.576 + }
3.577 +
3.578 + @Override
3.579 + public void service(Request request, Response response) throws Exception {
3.580 + String res = request.getHttpHandlerPath();
3.581 + if (res.startsWith("/")) {
3.582 + res = res.substring(1);
3.583 + }
3.584 + try (InputStream is = loader.get(res)) {
3.585 + response.setContentType("text/javascript");
3.586 + Writer w = response.getWriter();
3.587 + w.append("[");
3.588 + for (int i = 0;; i++) {
3.589 + int b = is.read();
3.590 + if (b == -1) {
3.591 + break;
3.592 + }
3.593 + if (i > 0) {
3.594 + w.append(", ");
3.595 + }
3.596 + if (i % 20 == 0) {
3.597 + w.write("\n");
3.598 + }
3.599 + if (b > 127) {
3.600 + b = b - 256;
3.601 + }
3.602 + w.append(Integer.toString(b));
3.603 + }
3.604 + w.append("\n]");
3.605 + } catch (IOException ex) {
3.606 + response.setStatus(HttpStatus.NOT_FOUND_404);
3.607 + response.setError();
3.608 + response.setDetailMessage(ex.getMessage());
3.609 + }
3.610 + }
3.611 + }
3.612 +}
4.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
4.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/FXBrwsrLauncher.java Sun Apr 28 17:42:49 2013 +0200
4.3 @@ -0,0 +1,99 @@
4.4 +/**
4.5 + * Back 2 Browser Bytecode Translator
4.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
4.7 + *
4.8 + * This program is free software: you can redistribute it and/or modify
4.9 + * it under the terms of the GNU General Public License as published by
4.10 + * the Free Software Foundation, version 2 of the License.
4.11 + *
4.12 + * This program is distributed in the hope that it will be useful,
4.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
4.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4.15 + * GNU General Public License for more details.
4.16 + *
4.17 + * You should have received a copy of the GNU General Public License
4.18 + * along with this program. Look for COPYING file in the top folder.
4.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
4.20 + */
4.21 +package org.apidesign.bck2brwsr.launcher;
4.22 +
4.23 +import org.apidesign.bck2brwsr.launcher.fximpl.FXBrwsr;
4.24 +import java.io.IOException;
4.25 +import java.lang.reflect.Method;
4.26 +import java.net.URI;
4.27 +import java.net.URL;
4.28 +import java.net.URLClassLoader;
4.29 +
4.30 +import java.util.concurrent.Executors;
4.31 +import java.util.logging.Level;
4.32 +import java.util.logging.Logger;
4.33 +import javafx.application.Platform;
4.34 +import org.apidesign.bck2brwsr.launcher.fximpl.JVMBridge;
4.35 +import org.apidesign.vm4brwsr.Bck2Brwsr;
4.36 +
4.37 +/**
4.38 + *
4.39 + * @author Jaroslav Tulach <jtulach@netbeans.org>
4.40 + */
4.41 +final class FXBrwsrLauncher extends BaseHTTPLauncher {
4.42 + private static final Logger LOG = Logger.getLogger(FXBrwsrLauncher.class.getName());
4.43 + static {
4.44 + try {
4.45 + Method m = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
4.46 + m.setAccessible(true);
4.47 + URL l = new URL("file://" + System.getProperty("java.home") + "/lib/jfxrt.jar");
4.48 + LOG.log(Level.INFO, "url : {0}", l);
4.49 + m.invoke(ClassLoader.getSystemClassLoader(), l);
4.50 + } catch (Exception ex) {
4.51 + throw new LinkageError("Can't add jfxrt.jar on the classpath", ex);
4.52 + }
4.53 + }
4.54 +
4.55 + public FXBrwsrLauncher(String ignore) {
4.56 + super(null);
4.57 + }
4.58 +
4.59 + @Override
4.60 + protected Object[] showBrwsr(final URI url) throws IOException {
4.61 + try {
4.62 + LOG.log(Level.INFO, "showBrwsr for {0}", url);
4.63 + JVMBridge.registerClassLoaders(loaders());
4.64 + LOG.info("About to launch WebView");
4.65 + Executors.newSingleThreadExecutor().submit(new Runnable() {
4.66 + @Override
4.67 + public void run() {
4.68 + LOG.log(Level.INFO, "In FX thread. Launching!");
4.69 + try {
4.70 + FXBrwsr.launch(FXBrwsr.class, url.toString());
4.71 + LOG.log(Level.INFO, "Launcher is back. Closing");
4.72 + close();
4.73 + } catch (Throwable ex) {
4.74 + LOG.log(Level.WARNING, "Error launching Web View", ex);
4.75 + }
4.76 + }
4.77 + });
4.78 + } catch (Throwable ex) {
4.79 + LOG.log(Level.WARNING, "Can't open WebView", ex);
4.80 + }
4.81 + return null;
4.82 + }
4.83 +
4.84 + @Override
4.85 + void generateBck2BrwsrJS(StringBuilder sb, Bck2Brwsr.Resources loader) throws IOException {
4.86 + sb.append("(function() {\n"
4.87 + + " var impl = this.bck2brwsr;\n"
4.88 + + " this.bck2brwsr = function() { return impl; };\n"
4.89 + + "})(window);\n"
4.90 + );
4.91 + JVMBridge.onBck2BrwsrLoad();
4.92 + }
4.93 +
4.94 +
4.95 +
4.96 + @Override
4.97 + public void close() throws IOException {
4.98 + super.close();
4.99 + Platform.exit();
4.100 + }
4.101 +
4.102 +}
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Console.java Sun Apr 28 17:42:49 2013 +0200
5.3 @@ -0,0 +1,415 @@
5.4 +/**
5.5 + * Back 2 Browser Bytecode Translator
5.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
5.7 + *
5.8 + * This program is free software: you can redistribute it and/or modify
5.9 + * it under the terms of the GNU General Public License as published by
5.10 + * the Free Software Foundation, version 2 of the License.
5.11 + *
5.12 + * This program is distributed in the hope that it will be useful,
5.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
5.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
5.15 + * GNU General Public License for more details.
5.16 + *
5.17 + * You should have received a copy of the GNU General Public License
5.18 + * along with this program. Look for COPYING file in the top folder.
5.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
5.20 + */
5.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
5.22 +
5.23 +import java.io.IOException;
5.24 +import java.io.InputStream;
5.25 +import java.io.UnsupportedEncodingException;
5.26 +import java.lang.reflect.InvocationTargetException;
5.27 +import java.lang.reflect.Method;
5.28 +import java.lang.reflect.Modifier;
5.29 +import java.net.URL;
5.30 +import java.util.Enumeration;
5.31 +import javafx.scene.web.WebEngine;
5.32 +import netscape.javascript.JSObject;
5.33 +import org.apidesign.bck2brwsr.core.JavaScriptBody;
5.34 +
5.35 +/**
5.36 + *
5.37 + * @author Jaroslav Tulach <jtulach@netbeans.org>
5.38 + */
5.39 +public final class Console {
5.40 + public Console() {
5.41 + }
5.42 +
5.43 + @JavaScriptBody(args = {"elem", "attr"}, body =
5.44 + "return elem[attr].toString();")
5.45 + private static Object getAttr(Object elem, String attr) {
5.46 + return InvokeJS.CObject.call("getAttr", elem, attr);
5.47 + }
5.48 +
5.49 + @JavaScriptBody(args = {"id", "attr", "value"}, body =
5.50 + "window.document.getElementById(id)[attr] = value;")
5.51 + private static void setAttr(String id, String attr, Object value) {
5.52 + InvokeJS.CObject.call("setAttrId", id, attr, value);
5.53 + }
5.54 + @JavaScriptBody(args = {"elem", "attr", "value"}, body =
5.55 + "elem[attr] = value;")
5.56 + private static void setAttr(Object id, String attr, Object value) {
5.57 + InvokeJS.CObject.call("setAttr", id, attr, value);
5.58 + }
5.59 +
5.60 + @JavaScriptBody(args = {}, body = "return; window.close();")
5.61 + private static void closeWindow() {}
5.62 +
5.63 + private static Object textArea;
5.64 + private static Object statusArea;
5.65 +
5.66 + private static void log(String newText) {
5.67 + if (textArea == null) {
5.68 + return;
5.69 + }
5.70 + String attr = "value";
5.71 + setAttr(textArea, attr, getAttr(textArea, attr) + "\n" + newText);
5.72 + setAttr(textArea, "scrollTop", getAttr(textArea, "scrollHeight"));
5.73 + }
5.74 +
5.75 + private static void beginTest(Case c) {
5.76 + Object[] arr = new Object[2];
5.77 + beginTest(c.getClassName() + "." + c.getMethodName(), c, arr);
5.78 + textArea = arr[0];
5.79 + statusArea = arr[1];
5.80 + }
5.81 +
5.82 + private static void finishTest(Case c, Object res) {
5.83 + if ("null".equals(res)) {
5.84 + setAttr(statusArea, "innerHTML", "Success");
5.85 + } else {
5.86 + setAttr(statusArea, "innerHTML", "Result " + res);
5.87 + }
5.88 + statusArea = null;
5.89 + textArea = null;
5.90 + }
5.91 +
5.92 + private static final String BEGIN_TEST =
5.93 + "var ul = window.document.getElementById('bck2brwsr.result');\n"
5.94 + + "var li = window.document.createElement('li');\n"
5.95 + + "var span = window.document.createElement('span');"
5.96 + + "span.innerHTML = test + ' - ';\n"
5.97 + + "var details = window.document.createElement('a');\n"
5.98 + + "details.innerHTML = 'Details';\n"
5.99 + + "details.href = '#';\n"
5.100 + + "var p = window.document.createElement('p');\n"
5.101 + + "var status = window.document.createElement('a');\n"
5.102 + + "status.innerHTML = 'running';"
5.103 + + "details.onclick = function() { li.appendChild(p); li.removeChild(details); status.innerHTML = 'Run Again'; status.href = '#'; };\n"
5.104 + + "status.onclick = function() { c.again(arr); }\n"
5.105 + + "var pre = window.document.createElement('textarea');\n"
5.106 + + "pre.cols = 100;"
5.107 + + "pre.rows = 10;"
5.108 + + "li.appendChild(span);\n"
5.109 + + "li.appendChild(status);\n"
5.110 + + "var span = window.document.createElement('span');"
5.111 + + "span.innerHTML = ' ';\n"
5.112 + + "li.appendChild(span);\n"
5.113 + + "li.appendChild(details);\n"
5.114 + + "p.appendChild(pre);\n"
5.115 + + "ul.appendChild(li);\n"
5.116 + + "arr[0] = pre;\n"
5.117 + + "arr[1] = status;\n";
5.118 +
5.119 + @JavaScriptBody(args = { "test", "c", "arr" }, body = BEGIN_TEST)
5.120 + private static void beginTest(String test, Case c, Object[] arr) {
5.121 + InvokeJS.CObject.call("beginTest", test, c, arr);
5.122 + }
5.123 +
5.124 + private static final String LOAD_TEXT =
5.125 + "var request = new XMLHttpRequest();\n"
5.126 + + "request.open('GET', url, true);\n"
5.127 + + "request.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');\n"
5.128 + + "request.onreadystatechange = function() {\n"
5.129 + + " if (this.readyState!==4) return;\n"
5.130 + + " try {"
5.131 + + " arr[0] = this.responseText;\n"
5.132 + + " callback.run__V();\n"
5.133 + + " } catch (e) { alert(e); }"
5.134 + + "};"
5.135 + + "request.send();";
5.136 + @JavaScriptBody(args = { "url", "callback", "arr" }, body = LOAD_TEXT)
5.137 + private static void loadText(String url, Runnable callback, String[] arr) throws IOException {
5.138 + InvokeJS.CObject.call("loadText", url, new Run(callback), arr);
5.139 + }
5.140 +
5.141 + public static void runHarness(String url) throws IOException {
5.142 + new Console().harness(url);
5.143 + }
5.144 +
5.145 + public void harness(String url) throws IOException {
5.146 + log("Connecting to " + url);
5.147 + Request r = new Request(url);
5.148 + }
5.149 +
5.150 + private static class Request implements Runnable {
5.151 + private final String[] arr = { null };
5.152 + private final String url;
5.153 + private Case c;
5.154 + private int retries;
5.155 +
5.156 + private Request(String url) throws IOException {
5.157 + this.url = url;
5.158 + loadText(url, this, arr);
5.159 + }
5.160 + private Request(String url, String u) throws IOException {
5.161 + this.url = url;
5.162 + loadText(u, this, arr);
5.163 + }
5.164 +
5.165 + @Override
5.166 + public void run() {
5.167 + try {
5.168 + if (c == null) {
5.169 + String data = arr[0];
5.170 +
5.171 + if (data == null) {
5.172 + log("Some error exiting");
5.173 + closeWindow();
5.174 + return;
5.175 + }
5.176 +
5.177 + if (data.isEmpty()) {
5.178 + log("No data, exiting");
5.179 + closeWindow();
5.180 + return;
5.181 + }
5.182 +
5.183 + c = Case.parseData(data);
5.184 + beginTest(c);
5.185 + log("Got \"" + data + "\"");
5.186 + } else {
5.187 + log("Processing \"" + arr[0] + "\" for " + retries + " time");
5.188 + }
5.189 + Object result = retries++ >= 10 ? "java.lang.InterruptedException:timeout" : c.runTest();
5.190 + finishTest(c, result);
5.191 +
5.192 + String u = url + "?request=" + c.getRequestId() + "&result=" + result;
5.193 + new Request(url, u);
5.194 + } catch (Exception ex) {
5.195 + if (ex instanceof InterruptedException) {
5.196 + log("Re-scheduling in 100ms");
5.197 + schedule(this, 100);
5.198 + return;
5.199 + }
5.200 + log(ex.getClass().getName() + ":" + ex.getMessage());
5.201 + }
5.202 + }
5.203 + }
5.204 +
5.205 + private static String encodeURL(String r) throws UnsupportedEncodingException {
5.206 + final String SPECIAL = "%$&+,/:;=?@";
5.207 + StringBuilder sb = new StringBuilder();
5.208 + byte[] utf8 = r.getBytes("UTF-8");
5.209 + for (int i = 0; i < utf8.length; i++) {
5.210 + int ch = utf8[i] & 0xff;
5.211 + if (ch < 32 || ch > 127 || SPECIAL.indexOf(ch) >= 0) {
5.212 + final String numbers = "0" + Integer.toHexString(ch);
5.213 + sb.append("%").append(numbers.substring(numbers.length() - 2));
5.214 + } else {
5.215 + if (ch == 32) {
5.216 + sb.append("+");
5.217 + } else {
5.218 + sb.append((char)ch);
5.219 + }
5.220 + }
5.221 + }
5.222 + return sb.toString();
5.223 + }
5.224 +
5.225 + static String invoke(String clazz, String method) throws
5.226 + ClassNotFoundException, InvocationTargetException, IllegalAccessException,
5.227 + InstantiationException, InterruptedException {
5.228 + final Object r = new Case(null).invokeMethod(clazz, method);
5.229 + return r == null ? "null" : r.toString().toString();
5.230 + }
5.231 +
5.232 + /** Helper method that inspects the classpath and loads given resource
5.233 + * (usually a class file). Used while running tests in Rhino.
5.234 + *
5.235 + * @param name resource name to find
5.236 + * @return the array of bytes in the given resource
5.237 + * @throws IOException I/O in case something goes wrong
5.238 + */
5.239 + public static byte[] read(String name) throws IOException {
5.240 + URL u = null;
5.241 + Enumeration<URL> en = Console.class.getClassLoader().getResources(name);
5.242 + while (en.hasMoreElements()) {
5.243 + u = en.nextElement();
5.244 + }
5.245 + if (u == null) {
5.246 + throw new IOException("Can't find " + name);
5.247 + }
5.248 + try (InputStream is = u.openStream()) {
5.249 + byte[] arr;
5.250 + arr = new byte[is.available()];
5.251 + int offset = 0;
5.252 + while (offset < arr.length) {
5.253 + int len = is.read(arr, offset, arr.length - offset);
5.254 + if (len == -1) {
5.255 + throw new IOException("Can't read " + name);
5.256 + }
5.257 + offset += len;
5.258 + }
5.259 + return arr;
5.260 + }
5.261 + }
5.262 +
5.263 + @JavaScriptBody(args = {}, body = "vm.desiredAssertionStatus = true;")
5.264 + private static void turnAssetionStatusOn() {
5.265 + }
5.266 +
5.267 + @JavaScriptBody(args = {"r", "time"}, body =
5.268 + "return window.setTimeout(function() { r.run__V(); }, time);")
5.269 + private static Object schedule(Runnable r, int time) {
5.270 + return InvokeJS.CObject.call("schedule", new Run(r), time);
5.271 + }
5.272 +
5.273 + private static final class Case {
5.274 + private final Object data;
5.275 + private Object inst;
5.276 +
5.277 + private Case(Object data) {
5.278 + this.data = data;
5.279 + }
5.280 +
5.281 + public static Case parseData(String s) {
5.282 + return new Case(toJSON(s));
5.283 + }
5.284 +
5.285 + public String getMethodName() {
5.286 + return (String) value("methodName", data);
5.287 + }
5.288 +
5.289 + public String getClassName() {
5.290 + return (String) value("className", data);
5.291 + }
5.292 +
5.293 + public int getRequestId() {
5.294 + Object v = value("request", data);
5.295 + if (v instanceof Number) {
5.296 + return ((Number)v).intValue();
5.297 + }
5.298 + return Integer.parseInt(v.toString());
5.299 + }
5.300 +
5.301 + public String getHtmlFragment() {
5.302 + return (String) value("html", data);
5.303 + }
5.304 +
5.305 + void again(Object[] arr) {
5.306 + try {
5.307 + textArea = arr[0];
5.308 + statusArea = arr[1];
5.309 + setAttr(textArea, "value", "");
5.310 + runTest();
5.311 + } catch (Exception ex) {
5.312 + log(ex.getClass().getName() + ":" + ex.getMessage());
5.313 + }
5.314 + }
5.315 +
5.316 + private Object runTest() throws IllegalAccessException,
5.317 + IllegalArgumentException, ClassNotFoundException, UnsupportedEncodingException,
5.318 + InvocationTargetException, InstantiationException, InterruptedException {
5.319 + if (this.getHtmlFragment() != null) {
5.320 + setAttr("bck2brwsr.fragment", "innerHTML", this.getHtmlFragment());
5.321 + }
5.322 + log("Invoking " + this.getClassName() + '.' + this.getMethodName() + " as request: " + this.getRequestId());
5.323 + Object result = invokeMethod(this.getClassName(), this.getMethodName());
5.324 + setAttr("bck2brwsr.fragment", "innerHTML", "");
5.325 + log("Result: " + result);
5.326 + result = encodeURL("" + result);
5.327 + log("Sending back: ...?request=" + this.getRequestId() + "&result=" + result);
5.328 + return result;
5.329 + }
5.330 +
5.331 + private Object invokeMethod(String clazz, String method)
5.332 + throws ClassNotFoundException, InvocationTargetException,
5.333 + InterruptedException, IllegalAccessException, IllegalArgumentException,
5.334 + InstantiationException {
5.335 + Method found = null;
5.336 + Class<?> c = Class.forName(clazz);
5.337 + for (Method m : c.getMethods()) {
5.338 + if (m.getName().equals(method)) {
5.339 + found = m;
5.340 + }
5.341 + }
5.342 + Object res;
5.343 + if (found != null) {
5.344 + try {
5.345 + if ((found.getModifiers() & Modifier.STATIC) != 0) {
5.346 + res = found.invoke(null);
5.347 + } else {
5.348 + if (inst == null) {
5.349 + inst = c.newInstance();
5.350 + }
5.351 + res = found.invoke(inst);
5.352 + }
5.353 + } catch (Throwable ex) {
5.354 + if (ex instanceof InvocationTargetException) {
5.355 + ex = ((InvocationTargetException) ex).getTargetException();
5.356 + }
5.357 + if (ex instanceof InterruptedException) {
5.358 + throw (InterruptedException)ex;
5.359 + }
5.360 + res = ex.getClass().getName() + ":" + ex.getMessage();
5.361 + }
5.362 + } else {
5.363 + res = "Can't find method " + method + " in " + clazz;
5.364 + }
5.365 + return res;
5.366 + }
5.367 +
5.368 + @JavaScriptBody(args = "s", body = "return eval('(' + s + ')');")
5.369 + private static Object toJSON(String s) {
5.370 + return InvokeJS.CObject.call("toJSON", s);
5.371 + }
5.372 +
5.373 + @JavaScriptBody(args = {"p", "d"}, body =
5.374 + "var v = d[p];\n"
5.375 + + "if (typeof v === 'undefined') return null;\n"
5.376 + + "return v.toString();"
5.377 + )
5.378 + private static Object value(String p, Object d) {
5.379 + return ((JSObject)d).getMember(p);
5.380 + }
5.381 + }
5.382 +
5.383 + private static String safe(String txt) {
5.384 + return "try {" + txt + "} catch (err) { alert(err); }";
5.385 + }
5.386 +
5.387 + static {
5.388 + turnAssetionStatusOn();
5.389 + }
5.390 +
5.391 + private static final class InvokeJS {
5.392 + static final JSObject CObject = initJS();
5.393 +
5.394 + @JavaScriptBody(args = { }, body = "return null;")
5.395 + private static JSObject initJS() {
5.396 + WebEngine web = (WebEngine) System.getProperties().get("webEngine");
5.397 + return (JSObject) web.executeScript("(function() {"
5.398 + + "var CObject = {};"
5.399 +
5.400 + + "CObject.getAttr = function(elem, attr) { return elem[attr].toString(); };"
5.401 +
5.402 + + "CObject.setAttrId = function(id, attr, value) { window.document.getElementById(id)[attr] = value; };"
5.403 + + "CObject.setAttr = function(elem, attr, value) { elem[attr] = value; };"
5.404 +
5.405 + + "CObject.beginTest = function(test, c, arr) {" + safe(BEGIN_TEST) + "};"
5.406 +
5.407 + + "CObject.loadText = function(url, callback, arr) {" + safe(LOAD_TEXT.replace("run__V", "run")) + "};"
5.408 +
5.409 + + "CObject.schedule = function(r, time) { return window.setTimeout(function() { r.run(); }, time); };"
5.410 +
5.411 + + "CObject.toJSON = function(s) { return eval('(' + s + ')'); };"
5.412 +
5.413 + + "return CObject;"
5.414 + + "})(this)");
5.415 + }
5.416 + }
5.417 +
5.418 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/FXBrwsr.java Sun Apr 28 17:42:49 2013 +0200
6.3 @@ -0,0 +1,184 @@
6.4 +/**
6.5 + * Back 2 Browser Bytecode Translator
6.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
6.7 + *
6.8 + * This program is free software: you can redistribute it and/or modify
6.9 + * it under the terms of the GNU General Public License as published by
6.10 + * the Free Software Foundation, version 2 of the License.
6.11 + *
6.12 + * This program is distributed in the hope that it will be useful,
6.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
6.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
6.15 + * GNU General Public License for more details.
6.16 + *
6.17 + * You should have received a copy of the GNU General Public License
6.18 + * along with this program. Look for COPYING file in the top folder.
6.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
6.20 + */
6.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
6.22 +
6.23 +import java.util.List;
6.24 +import java.util.TooManyListenersException;
6.25 +import java.util.logging.Level;
6.26 +import java.util.logging.Logger;
6.27 +import javafx.application.Application;
6.28 +import javafx.application.Platform;
6.29 +import javafx.beans.value.ChangeListener;
6.30 +import javafx.beans.value.ObservableValue;
6.31 +import javafx.event.ActionEvent;
6.32 +import javafx.event.EventHandler;
6.33 +import javafx.geometry.HPos;
6.34 +import javafx.geometry.Insets;
6.35 +import javafx.geometry.Pos;
6.36 +import javafx.geometry.VPos;
6.37 +import javafx.scene.Node;
6.38 +import javafx.scene.Scene;
6.39 +import javafx.scene.control.Button;
6.40 +import javafx.scene.layout.ColumnConstraints;
6.41 +import javafx.scene.layout.GridPane;
6.42 +import javafx.scene.layout.Pane;
6.43 +import javafx.scene.layout.Priority;
6.44 +import javafx.scene.layout.VBox;
6.45 +import javafx.scene.text.Text;
6.46 +import javafx.scene.web.WebEngine;
6.47 +import javafx.scene.web.WebEvent;
6.48 +import javafx.scene.web.WebView;
6.49 +import javafx.stage.Modality;
6.50 +import javafx.stage.Stage;
6.51 +import netscape.javascript.JSObject;
6.52 +
6.53 +/**
6.54 + * Demonstrates a WebView object accessing a web page.
6.55 + *
6.56 + * @see javafx.scene.web.WebView
6.57 + * @see javafx.scene.web.WebEngine
6.58 + */
6.59 +public class FXBrwsr extends Application {
6.60 + private static final Logger LOG = Logger.getLogger(FXBrwsr.class.getName());
6.61 +
6.62 + @Override
6.63 + public void start(Stage primaryStage) throws Exception {
6.64 + Pane root = new WebViewPane(getParameters().getUnnamed());
6.65 + primaryStage.setScene(new Scene(root, 1024, 768));
6.66 + LOG.info("Showing the stage");
6.67 + primaryStage.show();
6.68 + LOG.log(Level.INFO, "State shown: {0}", primaryStage.isShowing());
6.69 + }
6.70 +
6.71 + /**
6.72 + * Create a resizable WebView pane
6.73 + */
6.74 + private class WebViewPane extends Pane {
6.75 + private final JVMBridge bridge = new JVMBridge();
6.76 +
6.77 + public WebViewPane(List<String> params) {
6.78 + LOG.log(Level.INFO, "Initializing WebView with {0}", params);
6.79 + VBox.setVgrow(this, Priority.ALWAYS);
6.80 + setMaxWidth(Double.MAX_VALUE);
6.81 + setMaxHeight(Double.MAX_VALUE);
6.82 + WebView view = new WebView();
6.83 + view.setMinSize(500, 400);
6.84 + view.setPrefSize(500, 400);
6.85 + final WebEngine eng = view.getEngine();
6.86 + try {
6.87 + JVMBridge.addBck2BrwsrLoad(new InitBck2Brwsr(eng));
6.88 + } catch (TooManyListenersException ex) {
6.89 + LOG.log(Level.SEVERE, null, ex);
6.90 + }
6.91 +
6.92 + if (params.size() > 0) {
6.93 + LOG.log(Level.INFO, "loading page {0}", params.get(0));
6.94 + eng.load(params.get(0));
6.95 + LOG.fine("back from load");
6.96 + }
6.97 + eng.setOnAlert(new EventHandler<WebEvent<String>>() {
6.98 + @Override
6.99 + public void handle(WebEvent<String> t) {
6.100 + final Stage dialogStage = new Stage();
6.101 + dialogStage.initModality(Modality.WINDOW_MODAL);
6.102 + dialogStage.setTitle("Warning");
6.103 + final Button button = new Button("Close");
6.104 + final Text text = new Text(t.getData());
6.105 +
6.106 + VBox box = new VBox();
6.107 + box.setAlignment(Pos.CENTER);
6.108 + box.setSpacing(10);
6.109 + box.setPadding(new Insets(10));
6.110 + box.getChildren().addAll(text, button);
6.111 +
6.112 + dialogStage.setScene(new Scene(box));
6.113 +
6.114 + button.setCancelButton(true);
6.115 + button.setOnAction(new EventHandler<ActionEvent>() {
6.116 + @Override
6.117 + public void handle(ActionEvent t) {
6.118 + dialogStage.close();
6.119 + }
6.120 + });
6.121 +
6.122 + dialogStage.centerOnScreen();
6.123 + dialogStage.showAndWait();
6.124 + }
6.125 + });
6.126 + GridPane grid = new GridPane();
6.127 + grid.setVgap(5);
6.128 + grid.setHgap(5);
6.129 + GridPane.setConstraints(view, 0, 1, 2, 1, HPos.CENTER, VPos.CENTER, Priority.ALWAYS, Priority.ALWAYS);
6.130 + grid.getColumnConstraints().addAll(new ColumnConstraints(100, 100, Double.MAX_VALUE, Priority.ALWAYS, HPos.CENTER, true), new ColumnConstraints(40, 40, 40, Priority.NEVER, HPos.CENTER, true));
6.131 + grid.getChildren().addAll(view);
6.132 + getChildren().add(grid);
6.133 + }
6.134 +
6.135 + boolean initBck2Brwsr(WebEngine webEngine) {
6.136 + JSObject jsobj = (JSObject) webEngine.executeScript("window");
6.137 + LOG.log(Level.FINE, "window: {0}", jsobj);
6.138 + Object prev = jsobj.getMember("bck2brwsr");
6.139 + if ("undefined".equals(prev)) {
6.140 + System.getProperties().put("webEngine", webEngine);
6.141 + jsobj.setMember("bck2brwsr", bridge);
6.142 + return true;
6.143 + }
6.144 + return false;
6.145 + }
6.146 +
6.147 + @Override
6.148 + protected void layoutChildren() {
6.149 + List<Node> managed = getManagedChildren();
6.150 + double width = getWidth();
6.151 + double height = getHeight();
6.152 + double top = getInsets().getTop();
6.153 + double right = getInsets().getRight();
6.154 + double left = getInsets().getLeft();
6.155 + double bottom = getInsets().getBottom();
6.156 + for (int i = 0; i < managed.size(); i++) {
6.157 + Node child = managed.get(i);
6.158 + layoutInArea(child, left, top, width - left - right, height - top - bottom, 0, Insets.EMPTY, true, true, HPos.CENTER, VPos.CENTER);
6.159 + }
6.160 + }
6.161 +
6.162 + private class InitBck2Brwsr implements ChangeListener<Void>, Runnable {
6.163 + private final WebEngine eng;
6.164 +
6.165 + public InitBck2Brwsr(WebEngine eng) {
6.166 + this.eng = eng;
6.167 + }
6.168 +
6.169 + @Override
6.170 + public synchronized void changed(ObservableValue<? extends Void> ov, Void t, Void t1) {
6.171 + Platform.runLater(this);
6.172 + try {
6.173 + wait();
6.174 + } catch (InterruptedException ex) {
6.175 + LOG.log(Level.SEVERE, null, ex);
6.176 + }
6.177 + }
6.178 +
6.179 + @Override
6.180 + public synchronized void run() {
6.181 + initBck2Brwsr(eng);
6.182 + notifyAll();
6.183 + }
6.184 + }
6.185 + }
6.186 +
6.187 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
7.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/JVMBridge.java Sun Apr 28 17:42:49 2013 +0200
7.3 @@ -0,0 +1,64 @@
7.4 +/**
7.5 + * Back 2 Browser Bytecode Translator
7.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
7.7 + *
7.8 + * This program is free software: you can redistribute it and/or modify
7.9 + * it under the terms of the GNU General Public License as published by
7.10 + * the Free Software Foundation, version 2 of the License.
7.11 + *
7.12 + * This program is distributed in the hope that it will be useful,
7.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
7.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
7.15 + * GNU General Public License for more details.
7.16 + *
7.17 + * You should have received a copy of the GNU General Public License
7.18 + * along with this program. Look for COPYING file in the top folder.
7.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
7.20 + */
7.21 +package org.apidesign.bck2brwsr.launcher.fximpl;
7.22 +
7.23 +import java.util.TooManyListenersException;
7.24 +import javafx.beans.value.ChangeListener;
7.25 +
7.26 +/**
7.27 + *
7.28 + * @author Jaroslav Tulach <jtulach@netbeans.org>
7.29 + */
7.30 +public final class JVMBridge {
7.31 + private static ClassLoader[] ldrs;
7.32 + private static ChangeListener<Void> onBck2BrwsrLoad;
7.33 +
7.34 + public static void registerClassLoaders(ClassLoader[] loaders) {
7.35 + ldrs = loaders.clone();
7.36 + }
7.37 +
7.38 + public static void addBck2BrwsrLoad(ChangeListener<Void> l) throws TooManyListenersException {
7.39 + if (onBck2BrwsrLoad != null) {
7.40 + throw new TooManyListenersException();
7.41 + }
7.42 + onBck2BrwsrLoad = l;
7.43 + }
7.44 +
7.45 + public static void onBck2BrwsrLoad() {
7.46 + ChangeListener<Void> l = onBck2BrwsrLoad;
7.47 + if (l != null) {
7.48 + l.changed(null, null, null);
7.49 + }
7.50 + }
7.51 +
7.52 + public Class<?> loadClass(String name) throws ClassNotFoundException {
7.53 + System.err.println("trying to load " + name);
7.54 + ClassNotFoundException ex = null;
7.55 + if (ldrs != null) for (ClassLoader l : ldrs) {
7.56 + try {
7.57 + return Class.forName(name, true, l);
7.58 + } catch (ClassNotFoundException ex2) {
7.59 + ex = ex2;
7.60 + }
7.61 + }
7.62 + if (ex == null) {
7.63 + ex = new ClassNotFoundException("No loaders");
7.64 + }
7.65 + throw ex;
7.66 + }
7.67 +}
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/Run.java Sun Apr 28 17:42:49 2013 +0200
8.3 @@ -0,0 +1,35 @@
8.4 +/**
8.5 + * Back 2 Browser Bytecode Translator
8.6 + * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
8.7 + *
8.8 + * This program is free software: you can redistribute it and/or modify
8.9 + * it under the terms of the GNU General Public License as published by
8.10 + * the Free Software Foundation, version 2 of the License.
8.11 + *
8.12 + * This program is distributed in the hope that it will be useful,
8.13 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
8.14 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
8.15 + * GNU General Public License for more details.
8.16 + *
8.17 + * You should have received a copy of the GNU General Public License
8.18 + * along with this program. Look for COPYING file in the top folder.
8.19 + * If not, see http://opensource.org/licenses/GPL-2.0.
8.20 + */
8.21 +
8.22 +package org.apidesign.bck2brwsr.launcher.fximpl;
8.23 +
8.24 +/**
8.25 + *
8.26 + * @author Jaroslav Tulach <jtulach@netbeans.org>
8.27 + */
8.28 +public final class Run implements Runnable {
8.29 + private final Runnable r;
8.30 + Run(Runnable r) {
8.31 + this.r = r;
8.32 + }
8.33 +
8.34 + @Override
8.35 + public void run() {
8.36 + r.run();
8.37 + }
8.38 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/launcher/fx/src/main/resources/org/apidesign/bck2brwsr/launcher/fximpl/harness.xhtml Sun Apr 28 17:42:49 2013 +0200
9.3 @@ -0,0 +1,52 @@
9.4 +<?xml version="1.0" encoding="UTF-8"?>
9.5 +<!--
9.6 +
9.7 + Back 2 Browser Bytecode Translator
9.8 + Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
9.9 +
9.10 + This program is free software: you can redistribute it and/or modify
9.11 + it under the terms of the GNU General Public License as published by
9.12 + the Free Software Foundation, version 2 of the License.
9.13 +
9.14 + This program is distributed in the hope that it will be useful,
9.15 + but WITHOUT ANY WARRANTY; without even the implied warranty of
9.16 + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9.17 + GNU General Public License for more details.
9.18 +
9.19 + You should have received a copy of the GNU General Public License
9.20 + along with this program. Look for COPYING file in the top folder.
9.21 + If not, see http://opensource.org/licenses/GPL-2.0.
9.22 +
9.23 +-->
9.24 +<!DOCTYPE html>
9.25 +<html xmlns="http://www.w3.org/1999/xhtml">
9.26 + <head>
9.27 + <title>Bck2Brwsr Harness</title>
9.28 + </head>
9.29 + <body>
9.30 + <script src="/bck2brwsr.js"></script>
9.31 + <script>
9.32 + var vm = bck2brwsr();
9.33 + </script>
9.34 +
9.35 + <h1>Bck2Brwsr Execution Harness</h1>
9.36 +
9.37 + <ul id="bck2brwsr.result" style="width: 100%;" >
9.38 + </ul>
9.39 +
9.40 + <div id="bck2brwsr.fragment"/>
9.41 +
9.42 + <script type="text/javascript">
9.43 + try {
9.44 + (function() {
9.45 + var cls = vm.loadClass('org.apidesign.bck2brwsr.launcher.fximpl.Console');
9.46 + // fxbrwsr mangling
9.47 + var inst = cls.newInstance();
9.48 + inst.harness('$U/../data');
9.49 + })();
9.50 + } catch (err) {
9.51 + alert('Error executing harness: ' + err);
9.52 + }
9.53 + </script>
9.54 + </body>
9.55 +</html>
10.1 --- a/launcher/pom.xml Sun Apr 28 17:11:12 2013 +0200
10.2 +++ b/launcher/pom.xml Sun Apr 28 17:42:49 2013 +0200
10.3 @@ -14,5 +14,6 @@
10.4 <modules>
10.5 <module>api</module>
10.6 <module>http</module>
10.7 + <module>fx</module>
10.8 </modules>
10.9 </project>
10.10 \ No newline at end of file