launcher/fx/src/main/java/org/apidesign/bck2brwsr/launcher/fximpl/WebDebug.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Sat, 08 Jun 2013 12:09:10 +0200
changeset 1169 c19ac78b940e
child 1173 aa82f9de8e33
permissions -rw-r--r--
LiveHTML WebKitDebugging protocol through CLI that supports --livehtml command
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.bck2brwsr.launcher.fximpl;
    19 
    20 import com.sun.javafx.scene.web.Debugger;
    21 import java.io.File;
    22 import java.io.IOException;
    23 import java.io.InputStream;
    24 import java.io.InterruptedIOException;
    25 import java.io.OutputStream;
    26 import java.io.UnsupportedEncodingException;
    27 import java.lang.reflect.Method;
    28 import java.util.concurrent.ArrayBlockingQueue;
    29 import java.util.concurrent.Executors;
    30 import java.util.concurrent.TimeUnit;
    31 import java.util.concurrent.atomic.AtomicReference;
    32 import java.util.logging.Level;
    33 import java.util.logging.Logger;
    34 import javafx.application.Platform;
    35 import javafx.util.Callback;
    36 
    37 /** Simulates WebKit protocol over WebSockets.
    38  *
    39  * @author Jaroslav Tulach
    40  */
    41 abstract class WebDebug extends OutputStream 
    42 implements Callback<String, Void>, Runnable {
    43     private static final Logger LOG = Logger.getLogger(WebDebug.class.getName());
    44 
    45     private final Debugger debug;
    46     private final StringBuilder cmd = new StringBuilder();
    47     private final ToDbgInputStream toDbg;
    48     private final String ud;
    49     
    50     WebDebug(Debugger debug, String ud) throws Exception {
    51         this.debug = debug;
    52         this.ud = ud;
    53         toDbg = new ToDbgInputStream();
    54         debug.setEnabled(true);
    55         debug.setMessageCallback(this);
    56     }
    57     
    58     static WebDebug create(Debugger debug, String ud) throws Exception {
    59         WebDebug web = new WebDebug(debug, ud) {};
    60         
    61         Executors.newFixedThreadPool(1).execute(web);
    62         
    63         return web;
    64     }
    65 
    66     @Override
    67     public void run() {
    68         try {
    69             String p = "/home/jarda/src/bck2brwsr/html~demo/twitter/src/main/resources/org/apidesign/html/demo/twitter/index.html";
    70             File f = new File(p);
    71             String[] args = {"--livehtml", f.getAbsolutePath()};
    72             File dir = f.getParentFile();
    73             cliHandler(args, toDbg, this, System.err, dir);
    74         } catch (Exception ex) {
    75             LOG.log(Level.SEVERE, null, ex);
    76         }
    77     }
    78 
    79     @Override
    80     public void close() {
    81         try {
    82             toDbg.close();
    83         } catch (IOException ex) {
    84             LOG.log(Level.SEVERE, null, ex);
    85         }
    86     }
    87 
    88     @Override
    89     public Void call(String p) {
    90         assert p.indexOf('\n') == -1 : "No new line: " + p;
    91         LOG.log(Level.INFO, "toDbgr: {0}", p);
    92         toDbg.pushMsg(p);
    93         return null;
    94     }
    95 
    96     @Override
    97     public void write(int b) throws IOException {
    98         if (b == '\n') {
    99             final String msg = cmd.toString();
   100             Platform.runLater(new Runnable() {
   101                 @Override
   102                 public void run() {
   103                     LOG.log(Level.INFO, "toView: {0}", msg);
   104                     debug.sendMessage(msg);
   105                 }
   106             });
   107             cmd.setLength(0);
   108         } else {
   109             if (cmd.length() > 100000) {
   110                 LOG.log(Level.WARNING, "Too big:\n{0}", cmd);
   111             }
   112             cmd.append((char)b);
   113         }
   114     }   
   115 
   116     private void cliHandler(
   117         String[] args, InputStream is, OutputStream os, OutputStream err, File dir
   118     ) {
   119         try {
   120             Class<?> main = Class.forName("org.netbeans.MainImpl");
   121             Method m = main.getDeclaredMethod("execute", String[].class, InputStream.class, 
   122                 OutputStream.class, OutputStream.class, AtomicReference.class
   123             );
   124             m.setAccessible(true);
   125             System.setProperty("netbeans.user", ud);
   126             int ret = (Integer)m.invoke(null, args, is, os, err, null);
   127             LOG.log(Level.INFO, "Return value: {0}", ret);
   128         } catch (Exception ex) {
   129             LOG.log(Level.SEVERE, null, ex);
   130         } finally {
   131             LOG.info("Communication is over");
   132         }
   133         
   134     }
   135 
   136     private class ToDbgInputStream extends InputStream {
   137         private byte[] current;
   138         private int currentPos;
   139         private final ArrayBlockingQueue<byte[]> pending = new ArrayBlockingQueue<byte[]>(64);
   140 
   141         public ToDbgInputStream() {
   142         }
   143 
   144         @Override
   145         public int read() throws IOException {
   146             return read(null, 0, 1);
   147         }
   148         
   149         @Override
   150         public int read(byte[] arr, int offset, int len) throws IOException {
   151             if (current == null || current.length <= currentPos) {
   152                 for (;;) {
   153                     WebDebug.this.flush();
   154                     try {
   155                         current = pending.poll(100, TimeUnit.MILLISECONDS);
   156                         if (current == null) {
   157                             return 0;
   158                         }
   159                         break;
   160                     } catch (InterruptedException ex) {
   161                         throw (InterruptedIOException)new InterruptedIOException().initCause(ex);
   162                     }
   163                 }
   164                 LOG.info("Will return: " + new String(current));
   165                 currentPos = 0;
   166             }
   167             int cnt = 0;
   168             while (len-- > 0 && currentPos < current.length) {
   169                 final byte nextByte = current[currentPos++];
   170                 if (arr == null) {
   171                     return nextByte;
   172                 }
   173                 arr[offset + cnt++] = nextByte;
   174             }
   175             LOG.log(Level.INFO, "read returns: {0}", new String(arr, offset, cnt));
   176             return cnt;
   177         }
   178 
   179         private void pushMsg(String p) {
   180             try {
   181                 pending.offer((p + '\n').getBytes("UTF-8"));
   182             } catch (UnsupportedEncodingException ex) {
   183                 throw new IllegalStateException(ex);
   184             }
   185         }
   186     }
   187 }