jaroslav@1169: /** jaroslav@1169: * Back 2 Browser Bytecode Translator jaroslav@1169: * Copyright (C) 2012 Jaroslav Tulach jaroslav@1169: * jaroslav@1169: * This program is free software: you can redistribute it and/or modify jaroslav@1169: * it under the terms of the GNU General Public License as published by jaroslav@1169: * the Free Software Foundation, version 2 of the License. jaroslav@1169: * jaroslav@1169: * This program is distributed in the hope that it will be useful, jaroslav@1169: * but WITHOUT ANY WARRANTY; without even the implied warranty of jaroslav@1169: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the jaroslav@1169: * GNU General Public License for more details. jaroslav@1169: * jaroslav@1169: * You should have received a copy of the GNU General Public License jaroslav@1169: * along with this program. Look for COPYING file in the top folder. jaroslav@1169: * If not, see http://opensource.org/licenses/GPL-2.0. jaroslav@1169: */ jaroslav@1169: package org.apidesign.bck2brwsr.launcher.fximpl; jaroslav@1169: jaroslav@1169: import com.sun.javafx.scene.web.Debugger; jaroslav@1169: import java.io.File; jaroslav@1169: import java.io.IOException; jaroslav@1169: import java.io.InputStream; jaroslav@1169: import java.io.InterruptedIOException; jaroslav@1169: import java.io.OutputStream; jaroslav@1169: import java.io.UnsupportedEncodingException; jaroslav@1169: import java.lang.reflect.Method; jaroslav@1169: import java.util.concurrent.ArrayBlockingQueue; jaroslav@1169: import java.util.concurrent.Executors; jaroslav@1169: import java.util.concurrent.TimeUnit; jaroslav@1169: import java.util.concurrent.atomic.AtomicReference; jaroslav@1169: import java.util.logging.Level; jaroslav@1169: import java.util.logging.Logger; jaroslav@1169: import javafx.application.Platform; jaroslav@1169: import javafx.util.Callback; jaroslav@1169: jaroslav@1169: /** Simulates WebKit protocol over WebSockets. jaroslav@1169: * jaroslav@1169: * @author Jaroslav Tulach jaroslav@1169: */ jaroslav@1169: abstract class WebDebug extends OutputStream jaroslav@1169: implements Callback, Runnable { jaroslav@1169: private static final Logger LOG = Logger.getLogger(WebDebug.class.getName()); jaroslav@1169: jaroslav@1169: private final Debugger debug; jaroslav@1169: private final StringBuilder cmd = new StringBuilder(); jaroslav@1169: private final ToDbgInputStream toDbg; jaroslav@1169: private final String ud; jaroslav@1169: jaroslav@1169: WebDebug(Debugger debug, String ud) throws Exception { jaroslav@1169: this.debug = debug; jaroslav@1169: this.ud = ud; jaroslav@1169: toDbg = new ToDbgInputStream(); jaroslav@1169: debug.setEnabled(true); jaroslav@1169: debug.setMessageCallback(this); jaroslav@1169: } jaroslav@1169: jaroslav@1169: static WebDebug create(Debugger debug, String ud) throws Exception { jaroslav@1169: WebDebug web = new WebDebug(debug, ud) {}; jaroslav@1169: jaroslav@1169: Executors.newFixedThreadPool(1).execute(web); jaroslav@1169: jaroslav@1169: return web; jaroslav@1169: } jaroslav@1169: jaroslav@1169: @Override jaroslav@1169: public void run() { jaroslav@1169: try { jaroslav@1173: String p = System.getProperty("startpage.file"); jaroslav@1173: File f; jaroslav@1173: if (p != null && (f = new File(p)).exists()) { jaroslav@1173: String[] args = {"--livehtml", f.getAbsolutePath()}; jaroslav@1173: File dir = f.getParentFile(); jaroslav@1173: cliHandler(args, toDbg, this, System.err, dir); jaroslav@1173: } jaroslav@1169: } catch (Exception ex) { jaroslav@1169: LOG.log(Level.SEVERE, null, ex); jaroslav@1169: } jaroslav@1169: } jaroslav@1169: jaroslav@1169: @Override jaroslav@1169: public void close() { jaroslav@1169: try { jaroslav@1169: toDbg.close(); jaroslav@1169: } catch (IOException ex) { jaroslav@1169: LOG.log(Level.SEVERE, null, ex); jaroslav@1169: } jaroslav@1169: } jaroslav@1169: jaroslav@1169: @Override jaroslav@1169: public Void call(String p) { jaroslav@1169: assert p.indexOf('\n') == -1 : "No new line: " + p; jaroslav@1169: LOG.log(Level.INFO, "toDbgr: {0}", p); jaroslav@1169: toDbg.pushMsg(p); jaroslav@1169: return null; jaroslav@1169: } jaroslav@1169: jaroslav@1169: @Override jaroslav@1169: public void write(int b) throws IOException { jaroslav@1169: if (b == '\n') { jaroslav@1169: final String msg = cmd.toString(); jaroslav@1169: Platform.runLater(new Runnable() { jaroslav@1169: @Override jaroslav@1169: public void run() { jaroslav@1169: LOG.log(Level.INFO, "toView: {0}", msg); jaroslav@1169: debug.sendMessage(msg); jaroslav@1169: } jaroslav@1169: }); jaroslav@1169: cmd.setLength(0); jaroslav@1169: } else { jaroslav@1169: if (cmd.length() > 100000) { jaroslav@1169: LOG.log(Level.WARNING, "Too big:\n{0}", cmd); jaroslav@1169: } jaroslav@1169: cmd.append((char)b); jaroslav@1169: } jaroslav@1169: } jaroslav@1169: jaroslav@1169: private void cliHandler( jaroslav@1169: String[] args, InputStream is, OutputStream os, OutputStream err, File dir jaroslav@1169: ) { jaroslav@1169: try { jaroslav@1169: Class main = Class.forName("org.netbeans.MainImpl"); jaroslav@1169: Method m = main.getDeclaredMethod("execute", String[].class, InputStream.class, jaroslav@1169: OutputStream.class, OutputStream.class, AtomicReference.class jaroslav@1169: ); jaroslav@1169: m.setAccessible(true); jaroslav@1169: System.setProperty("netbeans.user", ud); jaroslav@1169: int ret = (Integer)m.invoke(null, args, is, os, err, null); jaroslav@1169: LOG.log(Level.INFO, "Return value: {0}", ret); jaroslav@1169: } catch (Exception ex) { jaroslav@1169: LOG.log(Level.SEVERE, null, ex); jaroslav@1169: } finally { jaroslav@1169: LOG.info("Communication is over"); jaroslav@1169: } jaroslav@1169: jaroslav@1169: } jaroslav@1169: jaroslav@1169: private class ToDbgInputStream extends InputStream { jaroslav@1169: private byte[] current; jaroslav@1169: private int currentPos; jaroslav@1169: private final ArrayBlockingQueue pending = new ArrayBlockingQueue(64); jaroslav@1169: jaroslav@1169: public ToDbgInputStream() { jaroslav@1169: } jaroslav@1169: jaroslav@1169: @Override jaroslav@1169: public int read() throws IOException { jaroslav@1169: return read(null, 0, 1); jaroslav@1169: } jaroslav@1169: jaroslav@1169: @Override jaroslav@1169: public int read(byte[] arr, int offset, int len) throws IOException { jaroslav@1169: if (current == null || current.length <= currentPos) { jaroslav@1169: for (;;) { jaroslav@1169: WebDebug.this.flush(); jaroslav@1169: try { jaroslav@1173: current = pending.poll(5, TimeUnit.MILLISECONDS); jaroslav@1169: if (current == null) { jaroslav@1169: return 0; jaroslav@1169: } jaroslav@1169: break; jaroslav@1169: } catch (InterruptedException ex) { jaroslav@1169: throw (InterruptedIOException)new InterruptedIOException().initCause(ex); jaroslav@1169: } jaroslav@1169: } jaroslav@1169: LOG.info("Will return: " + new String(current)); jaroslav@1169: currentPos = 0; jaroslav@1169: } jaroslav@1169: int cnt = 0; jaroslav@1169: while (len-- > 0 && currentPos < current.length) { jaroslav@1169: final byte nextByte = current[currentPos++]; jaroslav@1169: if (arr == null) { jaroslav@1169: return nextByte; jaroslav@1169: } jaroslav@1169: arr[offset + cnt++] = nextByte; jaroslav@1169: } jaroslav@1169: LOG.log(Level.INFO, "read returns: {0}", new String(arr, offset, cnt)); jaroslav@1169: return cnt; jaroslav@1169: } jaroslav@1169: jaroslav@1169: private void pushMsg(String p) { jaroslav@1169: try { jaroslav@1169: pending.offer((p + '\n').getBytes("UTF-8")); jaroslav@1169: } catch (UnsupportedEncodingException ex) { jaroslav@1169: throw new IllegalStateException(ex); jaroslav@1169: } jaroslav@1169: } jaroslav@1169: } jaroslav@1169: }