Revision: 4616
          http://sourceforge.net/p/vexi/code/4616
Author:   mkpg2
Date:     2013-12-19 18:02:49 +0000 (Thu, 19 Dec 2013)
Log Message:
-----------
Alternative downloadable launcher.
Refactor. Split out common code from the applet.

Modified Paths:
--------------
    trunk/org.vexi-launcher/meta/module.xml
    trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Launcher.java
    trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Splash.java
    trunk/org.vexi-launcher/src/test/java/org/vexi/launcher/TestLauncher.java

Added Paths:
-----------
    trunk/org.vexi-launcher/Launcher.java
    trunk/org.vexi-launcher/LauncherApplet.java
    trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherApplet.java
    trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherMain.java
    trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/
    trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/Problem.java
    
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/StreamReader.java

Added: trunk/org.vexi-launcher/Launcher.java
===================================================================
--- trunk/org.vexi-launcher/Launcher.java                               (rev 0)
+++ trunk/org.vexi-launcher/Launcher.java       2013-12-19 18:02:49 UTC (rev 
4616)
@@ -0,0 +1,463 @@
+package org.vexi.launcher;
+
+import java.applet.Applet;
+import java.awt.Color;
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.vexi.DotVexi;
+
+/**
+ * @author m...@webenableit.co.uk
+ * @contributor char...@webenableit.co.uk
+ *
+ * <p>The launcher is responsible for downloading the Vexi Core
+ *    and the principle application components (.vexi files)
+ *    and launching it as a separate process so that it can
+ *    exist and survive outside of the restrictive environment
+ *    that is imposed upon applets.</p>
+ *    
+ */
+abstract public class Launcher {
+
+       // REMARK although a signed applet doesn't have any known securities
+    // we restrict it to running from domains that we control because in
+    // the event that it is compromised we can update the copy on these 
+    // domains. In this way the fetching of the applet acts as the security
+    // check.
+    abstract public Color getTextColor();
+    abstract public Color getBorderColor();
+    abstract public Color getBarColor();
+    abstract public URL getSplashImageResource();
+    abstract public String[] getPermittedDomains();
+    abstract public Map getCerts(); 
+    abstract public String getVersion();
+    
+    ////////////////
+    // INIT
+    final LauncherApplet applet;
+    DotVexi dotvexi;
+    String fetchCount = "?";
+    int fetchIndex = 1;
+    public Launcher(LauncherApplet applet) {
+       this.applet = applet;
+       }       
+
+    static protected void logflush() {
+        System.err.flush();
+    }
+    
+    static protected void log(String s) {
+        System.err.println(s);
+    }
+    
+    
+    static private String getEnv(String name) {
+        try {
+            return System.getenv(name);
+        } catch(Throwable t) {
+            // REMARK - they deprecated this in 1.4, only to bring it back in 
1.5.
+            // By deprecated we mean it throws an error. So here we fall back 
to
+            // getting it from the commandline.
+            try {
+                String os = System.getProperty("os.name").toLowerCase();
+                Process p;
+                String cmd;
+                if (os.indexOf("windows 9") != -1 || os.indexOf("windows me") 
!= -1) {
+                    cmd = "command.com /c set";
+                } else if (os.indexOf("windows") > -1) {
+                    cmd = "cmd.exe /c set";
+                } else {
+                    cmd = "env";
+                }
+                p = Runtime.getRuntime().exec(cmd);
+                BufferedReader br = new BufferedReader(new 
InputStreamReader(p.getInputStream()));
+                String s;
+                while ((s = br.readLine()) != null) {
+                    if (s.startsWith(name + "=")) {
+                        return s.substring(name.length() + 1);
+                    }
+                }
+            } catch (Throwable t2) {
+                log("Encountered an exception during fallback discovery of: 
'"+name+"'");
+                t2.printStackTrace();
+            }
+            return null;    
+        }
+    }
+    
+    /** generates the basic command string to start java */
+    static private String findInJavaHome(String os_name, String javaHome) {
+        if (javaHome != null && !javaHome.equals("")) {
+            String r = javaHome + File.separatorChar + "bin" + 
File.separatorChar + "java";
+            if (os_name.indexOf("windows") != -1) {
+                r += ".exe";
+            }
+            if (new File(r).exists()) {
+                return r;
+            }
+        }
+        return null;
+    }
+    
+    /** searches for the JVM binary in the usual places 
+     * @throws Problem */
+    static private String findJvmBinary() throws Exception {
+        log("-- Locating the Java Binary --");
+        String jvmBinary = null;
+        String os_name = System.getProperty("os.name", "").toLowerCase();
+        
+        log("Trying Environment Variable 'VEXI_JRE'");
+        jvmBinary = findInJavaHome(os_name,getEnv("VEXI_JRE"));
+        if (jvmBinary != null) {
+            return jvmBinary;
+        }
+        
+        log("Trying Environment Variable 'JAVA_HOME'");
+        jvmBinary = findInJavaHome(os_name,getEnv("JAVA_HOME"));
+        if (jvmBinary != null) {
+            return jvmBinary;
+        }
+        
+        log("Trying System Property 'java.home'");
+        jvmBinary = findInJavaHome(os_name,System.getProperty("java.home"));
+        if (jvmBinary != null) {
+            return jvmBinary;
+        }
+        
+        // check PATH
+        log("Checking to see if java is on the PATH Environment Variable");
+        String path = getEnv("PATH");
+        if (path == null) {
+            path  = getEnv("Path");
+        }
+        if (path!=null) {
+            StringTokenizer st = new StringTokenizer(path, 
File.pathSeparatorChar + "");
+            while (st.hasMoreTokens()) {
+                String s = st.nextToken();
+                if (new File(s + File.separatorChar + "java").exists() || new 
File(s + File.separatorChar + "java.exe").exists() ) {
+                    jvmBinary = s + File.separatorChar + "java";
+                    if (os_name.indexOf("windows") != -1) {
+                        jvmBinary += ".exe";
+                    }
+                    return jvmBinary;
+                }
+            }
+        }
+
+        throw new Problem("Couldn't find a suitable JVM binary! See console 
log for details");
+    }
+
+    long lastDate = -1;
+    protected InputStream getInputStream(String url) throws IOException{
+        final URLConnection uc = connect(url);
+        InputStream is = uc.getInputStream();
+        int contentLength = uc.getContentLength();
+        lastDate = uc.getLastModified();
+        return progressInputStream(is, url, contentLength);
+    }
+
+    public void initDotVexi() {
+        dotvexi = new DotVexi(getCerts()) {
+            
+            protected void log(String s) { Launcher.log(s); }
+    
+            protected InputStream getInputStream(Object fountain, Object 
principal) throws IOException {
+                String url = (String)fountain;
+                return Launcher.this.getInputStream(url);
+            }
+            protected long getRemoteDate(Object fountain, Object principal) {
+                return lastDate;
+            }
+        };
+    }
+    
+    public void initSplash(){
+       Splash.init(getVersion(),getSplashImageResource(), getTextColor(), 
getBorderColor(), getBarColor());        
+    }
+
+    
+    private InputStream progressInputStream(InputStream is, final String 
displayname, final int contentLength ) {
+        final String left = "Downloading: "+displayname; 
+        final String right = fetchIndex+"/"+fetchCount;
+        
+        return new FilterInputStream(new BufferedInputStream(is)) {
+            int total = 0;
+            int percent = 0;
+            private void display() {
+                 double loaded = ((double)total) / ((double)contentLength);
+                 int newpercent = ((int)Math.ceil(loaded * 100));
+                 if (newpercent!=percent) {
+                    percent = newpercent;
+                    
+                    //log(percent + "%");
+                    Splash.update(left, right, (double)percent);
+                 }
+            }
+            
+            public int read() throws IOException {
+                int ret = super.read();
+                if (ret != -1) {
+                    total++;
+                }
+                display();
+                return ret;
+            }
+            public int read(byte[] buf, int off, int len) throws IOException {
+                int ret = super.read(buf, off, len);
+                if (ret != -1) {
+                    total += ret;
+                }
+                display();
+                return ret;
+            }
+        };
+    }
+
+    /** check if a url falls within the permitted domains
+     * @param url a string of an absolute url, must start with 'http://' or 
'file://'
+     * @return true iff url is permitted */
+    protected boolean checkUrl(String url) {
+        if (url.startsWith("file")) {
+            return true;
+        }
+        if (url.startsWith("http")) {
+            // remove the protocol
+            url = url.substring(url.indexOf("//")+2);
+            while (true) {
+                String[] permittedDomains = getPermittedDomains();
+                for (int i=0; i<permittedDomains.length; i++) {
+                    if (url.startsWith(permittedDomains[i])) {
+                        return true;
+                    }
+                }
+                // if we are a subdomain remove leading part and recheck
+                if (url.indexOf('.')==-1) {
+                    break;
+                }
+                url = url.substring(url.indexOf('.')+1);
+            }
+        }
+        return false;
+    }
+    
+    static private String join(String[] ss) {
+        String r = "";
+        for (int i=0; i<ss.length; i++) {
+            if (i>0) {
+                r += ",";
+            }
+            r += ss[i];
+        }
+        return r;
+    }
+    
+    /** fetches a file from the distribution site, writing it to the 
appropriate place */
+    public File fetch(String url) throws IOException {
+        File localfile = dotvexi.getLocalFile(url);
+        dotvexi.fetch(url, null, url, localfile);
+        fetchIndex++;
+        return localfile;
+    }
+    
+    /** attempts to create a local file for logging vexi output to */
+    protected File createLog(String logfile) throws IOException {
+        File dir = dotvexi.findSubDir("logs");
+        dir.mkdirs();
+        File f=null;
+        String fname = logfile;
+        for (int i=0; i<100; i++) {
+            f = new File(dir,fname);
+            if ((f.exists() && !f.delete())) {
+                fname = logfile+i;
+                continue; // file occupied!
+            }
+            if (!f.createNewFile()) {
+                break;
+            }
+            return f;
+        }
+        throw new RuntimeException("Could not create log file: " + 
f.getCanonicalPath());
+    }
+    
+
+    
+    static public class Problem extends Exception {
+        public Problem(String message) { super(message); }
+    }
+    
+    
+    
+    public void go(
+               String core, 
+               String mem, 
+               List<String> fetchVexis) {
+       //log("Codebase is "+url);
+       fetchCount = ""+(fetchVexis.size()+1);
+
+
+       String coreurl = core;
+       if (!coreurl.endsWith(".signed")) {
+               permitUnsignedCore(coreurl);
+       }
+       File corefile = fetch(coreurl);
+
+       List command = new ArrayList();
+       command.add(findJvmBinary());
+       if (mem!=null) {
+               command.add("-Xmx" + mem);
+       }
+       command.add("-jar");
+       command.add(corefile.getPath());
+
+       String logfile = getParameter("logfile");
+       if (logfile!=null) {
+               createLog(logfile);
+               command.add("-l");
+               command.add("logs/"+logfile);
+       }
+       command.add("-biscuitid");
+       command.add(DotVexi.urlUniqueName(codebase));
+
+
+       for (int i = 0; i<10000;i++) {
+               if (getParameter("option" + i) == null) {
+                       break;
+               }
+               command.add(getParameter("option" + i));
+       }
+
+
+       for (int i = 0; i<fetchVexis.size();i++) {
+               File f = fetch((String)fetchVexis.get(i));
+               command.add(f.getPath());
+       }
+
+
+       // REMARK - discovering os/architecture not relevant until 
+       // native builds are working again
+       /*
+            String os_name = System.getProperty("os.name", "").toLowerCase();
+            log("os.name == " + os_name);
+            Vector command = new Vector();
+            String arch = null;
+            if (os_name.indexOf("linux") != -1) {
+                arch = new BufferedReader(new 
InputStreamReader(Runtime.getRuntime().exec("/bin/uname 
-m").getInputStream())).readLine();
+                log("arch is " + arch);
+            }*/
+
+       spawn(command);
+    }
+    
+    /** spawns the Vexi core
+     * @param command contains the full command - including java */
+    private void spawn(List<String> command) throws IOException { 
+        File dir = dotvexi.getBaseDir();
+        String[] command_vec = command.toArray(new String[command.size()]);
+        log("in directory : " + dir.getCanonicalPath());
+        log("executing    : ");
+        for (int i=0; i<command_vec.length; i++) {
+            log("    \"" + command_vec[i] + "\"");
+        }
+
+        final Process p = Runtime.getRuntime().exec(command_vec,null,dir);
+        
+        new Thread(new Runnable() {
+            public void run() {
+                // catch output
+                StreamReader errorstream = new 
StreamReader(p.getErrorStream(), "ERROR", true);
+                StreamReader outputstream = new 
StreamReader(p.getInputStream(), "OUTPUT", false);
+                // kick off output streams
+                errorstream.start();
+                outputstream.start();
+                try {
+                    updateStatus("Vexi loaded");
+                    int exitValue = p.waitFor();
+                    if (exitValue != 0) {
+                        updateError("Vexi exited abnormally with error code 
'"+exitValue+"', see log output");
+                    } else {
+                        updateStatus("Vexi has finished running");
+                    }
+                } catch (Throwable t) {
+                    log("Error exiting... " + p.exitValue());
+                    t.printStackTrace();
+                }
+                log("Exiting...");
+                logflush();
+            }
+        }).start();
+    }
+
+
+    /**
+     * @author char...@webenableit.co.uk
+     *
+     * A very simple stream reader, copied from:
+     * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
+     */
+    static class StreamReader extends Thread {
+        InputStream is;
+        String type;
+        
+        StreamReader(InputStream is, String type, boolean listenDisplay) {
+            this.is = is;
+            this.type = type;
+            this.listenDisplay = listenDisplay;
+        }
+        private boolean listenDisplay;
+        
+        public void run() {
+            try {
+                InputStreamReader isr = new InputStreamReader(is);
+                BufferedReader br = new BufferedReader(isr);
+                String line=null;
+                while ((line = br.readLine()) != null) {
+                    if (listenDisplay && line.contains("**display 
launched**")) {
+                        Splash.close();
+                    }
+                    log(type + ">" + line);
+                }
+            } catch (IOException ioe) {
+                ioe.printStackTrace();
+            }
+        }
+    }
+    
+    
+    ////////////////////
+    //// Utility
+    static private URLConnection connect(String url) throws IOException {
+        try {
+            URL u = new URL(url);
+            final URLConnection uc = u.openConnection();
+            uc.setUseCaches(false); // don't use the possibly short Java cache
+            uc.connect();
+            return uc;
+        } catch (NullPointerException npe) {
+            // WORKAROUND - sun libraries throw a NPE rather than
+            // a more informative exception.
+            throw new IOException("Invalid url: "+url);
+        }
+    }
+
+    //////////////
+    // Permission
+    void permitAlternativeURL(String url) throws Problem {
+        throw new Problem("Applet can not be run from unknown domain " + url + 
"\nPermitted domains: " + join(getPermittedDomains()));
+    }
+    void permitUnsignedCore(String file) throws Problem {
+        throw new Problem("Applet will not run unsigned core file: "+ file);
+    }
+}


Property changes on: trunk/org.vexi-launcher/Launcher.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/org.vexi-launcher/LauncherApplet.java
===================================================================
--- trunk/org.vexi-launcher/LauncherApplet.java                         (rev 0)
+++ trunk/org.vexi-launcher/LauncherApplet.java 2013-12-19 18:02:49 UTC (rev 
4616)
@@ -0,0 +1,153 @@
+package org.vexi.launcher;
+
+import java.applet.Applet;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Vector;
+
+import org.vexi.DotVexi;
+import org.vexi.launcher.Launcher.Problem;
+
+/**
+ * @author m...@webenableit.co.uk
+ * @contributor char...@webenableit.co.uk
+ *
+ * <p>The launcher is responsible for downloading the Vexi Core
+ *    and the principle application components (.vexi files)
+ *    and launching it as a separate process so that it can
+ *    exist and survive outside of the restrictive environment
+ *    that is imposed upon applets.</p>
+ *    
+ */
+abstract public class LauncherApplet extends Applet {
+       abstract protected Launcher createLauncher();
+    
+    ///////////////////////
+    // APPLET
+    ///////////////////////
+    
+       private Launcher launcher;
+       
+    /** applet entry point - this is where it all begins */
+    public final void init() {
+       launcher = createLauncher();
+       launcher.initSplash();
+       launcher.initDotVexi();
+        
+        // the rest is initialized outside of the restrictive applet sandbox
+        // as we need to download several files and start up a new process
+        // REMARK: this requires the applet to be signed, an unsigned applet
+        // will most likely fail very soon after this next thread is started
+        new Thread() {
+            public void run() {
+                java.security.AccessController.doPrivileged(new 
java.security.PrivilegedAction() {
+                    public Object run() {
+                        launcher.go();
+                        return null;
+                    }
+                });
+            }
+        }.start();
+
+    }
+    
+    void log(String s){ Launcher.log(s);}
+    
+    public void go() {
+       try{
+               log("Launcher Build : "+launcher.getVersion());
+
+               // Check codebase is permitted
+               String codebase = getCodeBase()+"";
+               updateStatus(""+codebase);
+               if (!checkUrl(codebase)) {
+                       permitAlternativeURL(codebase);
+               }
+               
+               String core = getParameter("core");
+            if (core==null) {
+                throw new Problem("Core property not set");
+            }
+            
+            List<String> fetchVexis = new ArrayList(10);
+            for (int i = 0; i<10000;i++) {
+                String fetch = getParameter("vexi" + i);
+                if (fetch == null) {
+                    break;
+                }
+                fetchVexis.add(fetch);
+            }
+            
+            go(core, fetchVexis);
+               
+       } catch (Problem e) {
+               updateError(e.getMessage());
+               log(e.getMessage());
+       } catch (Throwable e) {
+               updateError("Error; please check the Java console");
+               e.printStackTrace();
+       } finally {
+               Splash.close();
+       }        
+    }
+
+    
+    
+    final Font font = new Font("Sans-serif", Font.BOLD, 12);
+    final private Object statusLock = new Object();
+    private String statusText; 
+    private Color statusColor;
+    private Image backbuffer = null;
+    
+    protected void updateStatus(String statusText) { 
+       updateStatus(statusText, Color.black);
+    }    
+    protected void updateError(String statusText) { 
+       Launcher.log("Error: "+statusText);
+       updateStatus(statusText, Color.red); 
+    }
+    
+    void updateStatus(String statusText, Color statusColor) {
+        synchronized (statusLock) {
+            this.statusText = statusText;
+            this.statusColor = statusColor;
+        }
+        repaint();
+    }
+    
+    
+    public final void paint(Graphics g) { update(g); }
+    public final void update(Graphics g2) {
+        if (backbuffer == null || backbuffer.getWidth(null) != getSize().width 
|| backbuffer.getHeight(null) != getSize().height) {
+            backbuffer = createImage(getSize().width, getSize().height);
+        }
+        if (backbuffer == null) {
+            return;
+        }
+        Graphics2D g = (Graphics2D)backbuffer.getGraphics();
+        
+        Color color;
+        String text;
+        synchronized(statusLock) {
+            text = statusText;
+            color = statusColor;
+        }
+        
+        g.setColor(Color.white);
+        g.fillRect(0, 0, getSize().width, getSize().height);
+        
+        int centerOffset = (getSize().width-(int)font.getStringBounds(text, 
g.getFontRenderContext()).getWidth())/2;
+        g.setColor(color);
+        g.setFont(font);
+        g.drawString(text, centerOffset, 15);
+        
+        g2.setClip(0, 0, getSize().width, getSize().height);
+        g2.drawImage(backbuffer, 0, 0, null);
+    }
+}


Property changes on: trunk/org.vexi-launcher/LauncherApplet.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/org.vexi-launcher/meta/module.xml
===================================================================
--- trunk/org.vexi-launcher/meta/module.xml     2013-12-19 13:26:44 UTC (rev 
4615)
+++ trunk/org.vexi-launcher/meta/module.xml     2013-12-19 18:02:49 UTC (rev 
4616)
@@ -1,7 +1,7 @@
 <ebuild-module ebuild-version="0.8">
        <artifact name="java_classes.jar" />
        <dependencies>
-           <system name="java.jre" tag="1.4"/>
+           <system name="java.jre" tag="1.5"/>
            <dependency source="local"     name="library.crypto" />        
            <!-- Test Dependencies -->
         <dependency source="local"     name="library.testing"         
scope="test"/> 

Modified: trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Launcher.java
===================================================================
--- trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Launcher.java       
2013-12-19 13:26:44 UTC (rev 4615)
+++ trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Launcher.java       
2013-12-19 18:02:49 UTC (rev 4616)
@@ -1,11 +1,6 @@
 package org.vexi.launcher;
 
-import java.applet.Applet;
 import java.awt.Color;
-import java.awt.Font;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
 import java.io.BufferedInputStream;
 import java.io.BufferedReader;
 import java.io.File;
@@ -21,6 +16,8 @@
 import java.util.Vector;
 
 import org.vexi.DotVexi;
+import org.vexi.launcher.util.Problem;
+import org.vexi.launcher.util.StreamReader;
 
 /**
  * @author m...@webenableit.co.uk
@@ -33,7 +30,16 @@
  *    that is imposed upon applets.</p>
  *    
  */
-abstract public class Launcher extends Applet {
+abstract public class Launcher {
+       public interface Context {
+               public void flush();
+               public void log(String msg);
+               public void warn(String msg);
+               public void error(String msg);
+               public void updateStatus(String msg);
+               public String  getParameter(String s);
+               public void check() throws Problem;
+       }
 
     // REMARK although a signed applet doesn't have any known securities
     // we restrict it to running from domains that we control because in
@@ -45,26 +51,35 @@
     abstract public Color getBarColor();
     abstract public URL getSplashImageResource();
     abstract public String[] getPermittedDomains();
-    abstract public Map getCerts(); 
+    abstract public Map loadCerts() throws IOException; 
     abstract public String getVersion();
     
     ////////////////
     // INIT
-    
+    final protected Context context;
     DotVexi dotvexi;
     String fetchCount = "?";
     int fetchIndex = 1;
 
-    static protected void logflush() {
-        System.err.flush();
+
+    public Launcher(Context feedback) {
+               this.context = feedback;
+       }
+
+    protected void log(String s) {
+        context.log(s);
     }
-    
-    static protected void log(String s) {
-        System.err.println(s);
+       
+    public Map getCerts() {
+        try {
+               return loadCerts();
+        } catch (Throwable e) {
+            context.error("Error: applet unable to load root certificates");
+            throw new Error(e);
+        }
     }
     
-    
-    static private String getEnv(String name) {
+    private String getEnv(String name) {
         try {
             return System.getenv(name);
         } catch(Throwable t) {
@@ -114,7 +129,7 @@
     
     /** searches for the JVM binary in the usual places 
      * @throws Problem */
-    static private String findJvmBinary() throws Exception {
+    private String findJvmBinary() throws Exception {
         log("-- Locating the Java Binary --");
         String jvmBinary = null;
         String os_name = System.getProperty("os.name", "").toLowerCase();
@@ -161,20 +176,23 @@
     }
 
 
+    long lastDate = -1;
+    public InputStream getInputStream(String url) throws IOException{
+        final URLConnection uc = connect(url);
+        InputStream is = uc.getInputStream();
+        int contentLength = uc.getContentLength();
+        lastDate = uc.getLastModified();
+        return progressInputStream(is, url, contentLength);
+    }
 
     public void initDotVexi() {
         dotvexi = new DotVexi(getCerts()) {
-            long lastDate = -1;
             
-            protected void log(String s) { Launcher.log(s); }
+            protected void log(String s) { context.log(s); }
     
             protected InputStream getInputStream(Object fountain, Object 
principal) throws IOException {
                 String url = (String)fountain;
-                final URLConnection uc = connect(url);
-                InputStream is = uc.getInputStream();
-                int contentLength = uc.getContentLength();
-                lastDate = uc.getLastModified();
-                return progressInputStream(is, url, contentLength);
+                return Launcher.this.getInputStream(url);
             }
             protected long getRemoteDate(Object fountain, Object principal) {
                 return lastDate;
@@ -182,28 +200,12 @@
         };
     }
     
-    /** applet entry point - this is where it all begins */
-    public final void init() {
-        Splash.init(getVersion(),getSplashImageResource(), getTextColor(), 
getBorderColor(), getBarColor());
-        initDotVexi();
-        
-        // the rest is initialized outside of the restrictive applet sandbox
-        // as we need to download several files and start up a new process
-        // REMARK: this requires the applet to be signed, an unsigned applet
-        // will most likely fail very soon after this next thread is started
-        new Thread() {
-            public void run() {
-                java.security.AccessController.doPrivileged(new 
java.security.PrivilegedAction() {
-                    public Object run() {
-                        
-                        go();
-                        return null;
-                    }
-                });
-            }
-        }.start();
+    
+    public void initSplash(){
+       Splash.init(context, getVersion(),getSplashImageResource(), 
getTextColor(), getBorderColor(), getBarColor());        
+    }
 
-    }
+
     
     private InputStream progressInputStream(InputStream is, final String 
displayname, final int contentLength ) {
         final String left = "Downloading: "+displayname; 
@@ -312,23 +314,17 @@
         try {
             log("Launcher Build : "+getVersion());
             
-            // Check codebase is permitted
-            String codebase = getCodeBase()+"";
-            updateStatus(""+codebase);
-            //log("Codebase is "+url);
-            
-            if (!checkUrl(codebase)) {
-                permitAlternativeURL(codebase);
-            }
-            
-            String core = getParameter("core");
+            context.check();
+
+                        
+            String core = context.getParameter("core");
             if (core==null) {
                 throw new Problem("Core property not set");
             }
             
             ArrayList fetchVexis = new ArrayList(10);
             for (int i = 0; i<10000;i++) {
-                String fetch = getParameter("vexi" + i);
+                String fetch = context.getParameter("vexi" + i);
                 if (fetch == null) {
                     break;
                 }
@@ -345,27 +341,30 @@
             
             Vector command = new Vector();
             command.add(findJvmBinary());
-            if (getParameter("mem") != null) {
-                command.add("-Xmx" + getParameter("mem"));
+            if (context.getParameter("mem") != null) {
+                command.add("-Xmx" + context.getParameter("mem"));
             }
             command.add("-jar");
             command.add(corefile.getPath());
             
-            String logfile = getParameter("logfile");
+            String logfile = context.getParameter("logfile");
             if (logfile!=null) {
                 createLog(logfile);
                 command.add("-l");
                 command.add("logs/"+logfile);
             }
+            
+            
+            
             command.add("-biscuitid");
-            command.add(DotVexi.urlUniqueName(codebase));
+            command.add(DotVexi.urlUniqueName(context.getParameter("origin")));
             
             
             for (int i = 0; i<10000;i++) {
-                if (getParameter("option" + i) == null) {
+                if (context.getParameter("option" + i) == null) {
                     break;
                 }
-                command.add(getParameter("option" + i));
+                command.add(context.getParameter("option" + i));
             }
             
             
@@ -389,20 +388,17 @@
             
             spawn(command);
         } catch (Problem e) {
-            updateError(e.getMessage());
+            context.error(e.getMessage());
             log(e.getMessage());
         } catch (Throwable e) {
-            updateError("Error; please check the Java console");
+               context.error("Error; please check the Java console");
             e.printStackTrace();
         } finally {
             Splash.close();
         }
     }
+
     
-    static public class Problem extends Exception {
-        public Problem(String message) { super(message); }
-    }
-    
     /** spawns the Vexi core
      * @param command contains the full command - including java */
     private void spawn(Vector command) throws IOException { 
@@ -420,127 +416,32 @@
         new Thread(new Runnable() {
             public void run() {
                 // catch output
-                StreamReader errorstream = new 
StreamReader(p.getErrorStream(), "ERROR", true);
-                StreamReader outputstream = new 
StreamReader(p.getInputStream(), "OUTPUT", false);
+                StreamReader errorstream = new StreamReader(context, 
p.getErrorStream(), "ERROR", true);
+                StreamReader outputstream = new StreamReader(context, 
p.getInputStream(), "OUTPUT", false);
                 // kick off output streams
                 errorstream.start();
                 outputstream.start();
                 try {
-                    updateStatus("Vexi loaded");
+                       context.updateStatus("Vexi loaded");
                     int exitValue = p.waitFor();
                     if (exitValue != 0) {
-                        updateError("Vexi exited abnormally with error code 
'"+exitValue+"', see log output");
+                       context.error("Vexi exited abnormally with error code 
'"+exitValue+"', see log output");
                     } else {
-                        updateStatus("Vexi has finished running");
+                       context.updateStatus("Vexi has finished running");
                     }
                 } catch (Throwable t) {
-                    log("Error exiting... " + p.exitValue());
+                       context.error("Error exiting... " + p.exitValue());
                     t.printStackTrace();
                 }
-                log("Exiting...");
-                logflush();
+                context.log("Exiting...");
+                context.flush();
             }
         }).start();
     }
 
-    /**
-     * @author char...@webenableit.co.uk
-     *
-     * A very simple stream reader, copied from:
-     * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
-     */
-    static class StreamReader extends Thread {
-        InputStream is;
-        String type;
-        
-        StreamReader(InputStream is, String type, boolean listenDisplay) {
-            this.is = is;
-            this.type = type;
-            this.listenDisplay = listenDisplay;
-        }
-        private boolean listenDisplay;
-        
-        public void run() {
-            try {
-                InputStreamReader isr = new InputStreamReader(is);
-                BufferedReader br = new BufferedReader(isr);
-                String line=null;
-                while ((line = br.readLine()) != null) {
-                    if (listenDisplay && line.contains("**display 
launched**")) {
-                        Splash.close();
-                    }
-                    log(type + ">" + line);
-                }
-            } catch (IOException ioe) {
-                ioe.printStackTrace();
-            }
-        }
-    }
     
-    ///////////////////////
-    // Display/Status
-    ///////////////////////
-    
-    final Font font = new Font("Sans-serif", Font.BOLD, 12);
-    final private Object statusLock = new Object();
-    private String statusText; 
-    private Color statusColor;
-    private Image backbuffer = null;
-    
-    /** controls the visual display of the applet
-     * 
-     * @param percent
-     *   -1 causes the applet to be displayed in red
-     *   0-100 causes the applet to be a blue progress bar
-     * @param text
-     *   message or error to display in the applet
-     */
-    protected void updateStatus(String statusText) { updateStatus(statusText, 
Color.black); }    
-    protected void updateError(String statusText) { 
-        System.err.println("Error: "+statusText);
-        updateStatus(statusText, Color.red); 
-    }
-    private void updateStatus(String statusText, Color statusColor) {
-        synchronized (statusLock) {
-            this.statusText = statusText;
-            this.statusColor = statusColor;
-        }
-        repaint();
-    }
-    
-    
-    public final void paint(Graphics g) { update(g); }
-    public final void update(Graphics g2) {
-        if (backbuffer == null || backbuffer.getWidth(null) != getSize().width 
|| backbuffer.getHeight(null) != getSize().height) {
-            backbuffer = createImage(getSize().width, getSize().height);
-        }
-        if (backbuffer == null) {
-            return;
-        }
-        Graphics2D g = (Graphics2D)backbuffer.getGraphics();
-        
-        Color color;
-        String text;
-        synchronized(statusLock) {
-            text = statusText;
-            color = statusColor;
-        }
-        
-        g.setColor(Color.white);
-        g.fillRect(0, 0, getSize().width, getSize().height);
-        
-        int centerOffset = (getSize().width-(int)font.getStringBounds(text, 
g.getFontRenderContext()).getWidth())/2;
-        g.setColor(color);
-        g.setFont(font);
-        g.drawString(text, centerOffset, 15);
-        
-        g2.setClip(0, 0, getSize().width, getSize().height);
-        g2.drawImage(backbuffer, 0, 0, null);
-    }
 
-
     
-    
     ////////////////////
     //// Utility
     static private URLConnection connect(String url) throws IOException {

Added: 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherApplet.java
===================================================================
--- trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherApplet.java 
                        (rev 0)
+++ trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherApplet.java 
2013-12-19 18:02:49 UTC (rev 4616)
@@ -0,0 +1,130 @@
+package org.vexi.launcher;
+
+import java.applet.Applet;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+
+import org.vexi.launcher.Launcher.Context;
+import org.vexi.launcher.util.Problem;
+
+abstract public class LauncherApplet extends Applet implements Context{
+    
+       Launcher launcher;
+       
+       abstract protected Launcher createLauncher();
+       
+
+    /** applet entry point - this is where it all begins */
+    public final void init() {
+       launcher = createLauncher();
+       launcher.initSplash();
+        launcher.initDotVexi();
+        
+        // the rest is initialized outside of the restrictive applet sandbox
+        // as we need to download several files and start up a new process
+        // REMARK: this requires the applet to be signed, an unsigned applet
+        // will most likely fail very soon after this next thread is started
+        new Thread() {
+            public void run() {
+                java.security.AccessController.doPrivileged(new 
java.security.PrivilegedAction() {
+                    public Object run() {
+                        launcher.go();
+                        return null;
+                    }
+                });
+            }
+        }.start();
+
+    }
+    public void check() throws Problem {
+        
+        String codebase = getCodeBase()+"";
+        updateStatus(""+codebase);
+        //log("Codebase is "+url);
+
+        if (!launcher.checkUrl(codebase)) {
+               launcher.permitAlternativeURL(codebase);
+        }      
+    }
+    
+    ///////////////////////
+    // Display/Status
+    ///////////////////////
+    
+    final Font font = new Font("Sans-serif", Font.BOLD, 12);
+    final private Object statusLock = new Object();
+    private String statusText; 
+    private Color statusColor;
+    private Image backbuffer = null;
+    
+    public String getOrigin() {
+       return ""+getCodeBase();
+    }
+    
+    /** controls the visual display of the applet
+     * 
+     * @param percent
+     *   -1 causes the applet to be displayed in red
+     *   0-100 causes the applet to be a blue progress bar
+     * @param text
+     *   message or error to display in the applet
+     */
+    
+    public void log(String msg) {
+       System.err.println(msg);
+    }
+    public void flush() {
+       System.err.flush();
+    }
+    
+       public void warn(String msg) { log("[WARNING]: "+msg); }
+
+    public void error(String statusText) { 
+        log("[ERROR]: "+statusText);
+        updateStatus(statusText, Color.red); 
+    }
+    public void updateStatus(String statusText) { updateStatus(statusText, 
Color.black); }    
+    private void updateStatus(String statusText, Color statusColor) {
+        synchronized (statusLock) {
+            this.statusText = statusText;
+            this.statusColor = statusColor;
+        }
+        repaint();
+    }
+
+    
+    public final void paint(Graphics g) { update(g); }
+    public final void update(Graphics g2) {
+        if (backbuffer == null || backbuffer.getWidth(null) != getSize().width 
|| backbuffer.getHeight(null) != getSize().height) {
+            backbuffer = createImage(getSize().width, getSize().height);
+        }
+        if (backbuffer == null) {
+            return;
+        }
+        Graphics2D g = (Graphics2D)backbuffer.getGraphics();
+        
+        Color color;
+        String text;
+        synchronized(statusLock) {
+            text = statusText;
+            color = statusColor;
+        }
+        
+        g.setColor(Color.white);
+        g.fillRect(0, 0, getSize().width, getSize().height);
+        
+        int centerOffset = (getSize().width-(int)font.getStringBounds(text, 
g.getFontRenderContext()).getWidth())/2;
+        g.setColor(color);
+        g.setFont(font);
+        g.drawString(text, centerOffset, 15);
+        
+        g2.setClip(0, 0, getSize().width, getSize().height);
+        g2.drawImage(backbuffer, 0, 0, null);
+    }
+
+
+    
+}


Property changes on: 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherApplet.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherMain.java
===================================================================
--- trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherMain.java   
                        (rev 0)
+++ trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherMain.java   
2013-12-19 18:02:49 UTC (rev 4616)
@@ -0,0 +1,113 @@
+package org.vexi.launcher;
+
+import java.io.File;
+import java.io.InputStreamReader;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.vexi.launcher.Launcher.Context;
+import org.vexi.launcher.util.Problem;
+import org.vexi.util.IOUtil;
+
+/**
+ * 
+ * @author Mike Goodwin
+ *
+ */
+abstract public class LauncherMain implements Context{
+       
+       private Map<String,String> params;
+       private String application;
+       private String hash;
+
+       abstract public Launcher getLauncher();
+       
+       
+       void init(String[] args){
+               for(String s: args){
+                       String[] k_v = s.split("=");
+                       String k = k_v[0];
+                       String v = k_v[1];
+                       if("application".equals(k)){
+                               this.application = v;
+                       }else if("hash".equals(k)){
+                               this.hash = v;
+                       }
+               }
+       }
+
+       public void run(String[] args) throws Exception{
+               getLauncher().initSplash();
+               
+               File confFile = new File("conf");
+               if(confFile.isFile()){
+                       String[] conf = 
IOUtil.fileToString(confFile).split("/n");
+                       init(conf);
+               }else{
+                       warn("conf file does not exist");
+               }
+               
+               init(args);
+               if(application==null){
+                       error("'application' not specified (in conf or args)");
+                       System.exit(1);
+               }
+               
+               checkVersion();
+               fetchArgs();
+               getLauncher().initDotVexi();
+               getLauncher().go();
+
+       }
+       
+       
+       String readString(String path) throws Exception{
+               return IOUtil.readerToString(new 
InputStreamReader(getLauncher().getInputStream(path), "UTF8"));
+       }
+       
+       void checkVersion() throws Exception{
+               String hashes = readString(application+"/launcherversions");
+               if(hash==null){
+                       warn("no version check");
+               }else{
+                       for(String hash: hashes.split("\n")){
+                               if(this.hash.equals(hash)){
+                                       return;
+                               }
+                       }
+                       log("** Launcher incompatible, please download new 
version **");
+                       log("our hash: "+hash);
+                       log("allowed: ");
+                       for(String hash: hashes.split("\n")){
+                               log("   "+hash);
+                       }
+                       System.exit(0);
+               }
+       }
+       
+       void fetchArgs() throws Exception{
+               params = new LinkedHashMap();
+               String args = readString(application+"/launcherargs");
+               String[] argArr = args.split("\n");
+               for(String s: argArr){
+                       if("".equals(s.trim())) continue;
+                       
+                       String[] k_v = s.split("=", 2);
+                       if(k_v.length==2){
+                               params.put(k_v[0], k_v[1]);
+                       }else{
+                               warn("invalid arg: "+s);
+                       }
+               }               
+       }
+
+       public void flush() { System.err.flush(); }
+       public void log(String msg) { System.err.println(msg); }
+       public void warn(String msg) { log("[WARNING]: "+msg); }
+       public void error(String msg) { log("[ERROR]: "+msg); }
+       public void updateStatus(String msg) { log(msg); }
+
+       public String getParameter(String key) { return params.get(key); }
+       
+       public void check() throws Problem {}   
+}


Property changes on: 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/LauncherMain.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Splash.java
===================================================================
--- trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Splash.java 
2013-12-19 13:26:44 UTC (rev 4615)
+++ trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/Splash.java 
2013-12-19 18:02:49 UTC (rev 4616)
@@ -15,6 +15,8 @@
 import java.io.IOException;
 import java.net.URL;
 
+import org.vexi.launcher.Launcher.Context;
+
 /**
  * 
  * @author Mike Goodwin
@@ -29,24 +31,24 @@
     static private SplashWindow initFallback() {
         return new Fallback();
     }
-    static private SplashWindow initFast() {
+    static private SplashWindow initFast(Context context) {
         SplashScreen splash = SplashScreen.getSplashScreen();
         if (splash==null) {
-            System.err.println("[warning] could not find fast splash screen, 
using old style");
+            context.warn("could not find fast splash screen, using old style");
             return initFallback();
         } else {
             return new Fast(splash); 
         }
     }
 
-    static public SplashWindow init(String version, URL splashImage, Color 
textcolor, Color bordercolor, Color barcolor) {
+    static public SplashWindow init(Context context, String version, URL 
splashImage, Color textcolor, Color bordercolor, Color barcolor) {
         Splash.version = version;
         Splash.textcolor = textcolor;
         Splash.bordercolor = bordercolor;
         Splash.barcolor = barcolor;
         try {
             Splash.class.getClassLoader().loadClass("java.awt.SplashScreen");
-            instance = initFast();
+            instance = initFast(context);
         } catch (ClassNotFoundException e) {
             instance = initFallback();
         }

Added: trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/Problem.java
===================================================================
--- trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/Problem.java   
                        (rev 0)
+++ trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/Problem.java   
2013-12-19 18:02:49 UTC (rev 4616)
@@ -0,0 +1,5 @@
+package org.vexi.launcher.util;
+
+public class Problem extends Exception {
+    public Problem(String message) { super(message); }
+}
\ No newline at end of file


Property changes on: 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/Problem.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Added: 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/StreamReader.java
===================================================================
--- 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/StreamReader.java  
                            (rev 0)
+++ 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/StreamReader.java  
    2013-12-19 18:02:49 UTC (rev 4616)
@@ -0,0 +1,45 @@
+package org.vexi.launcher.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import org.vexi.launcher.Launcher.Context;
+import org.vexi.launcher.Splash;
+
+/**
+ * @author char...@webenableit.co.uk
+ *
+ * A very simple stream reader, copied from:
+ * http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html
+ */
+public class StreamReader extends Thread {
+       final Context feedback;
+    final InputStream is;
+    final String type;
+    
+    public StreamReader(Context feedback, InputStream is, String type, boolean 
listenDisplay) {
+       this.feedback = feedback;
+        this.is = is;
+        this.type = type;
+        this.listenDisplay = listenDisplay;
+    }
+    private boolean listenDisplay;
+    
+    public void run() {
+        try {
+            InputStreamReader isr = new InputStreamReader(is);
+            BufferedReader br = new BufferedReader(isr);
+            String line=null;
+            while ((line = br.readLine()) != null) {
+                if (listenDisplay && line.contains("**display launched**")) {
+                    Splash.close();
+                }
+                feedback.log(type + ">" + line);
+            }
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+    }
+}
\ No newline at end of file


Property changes on: 
trunk/org.vexi-launcher/src/main/java/org/vexi/launcher/util/StreamReader.java
___________________________________________________________________
Added: svn:mime-type
## -0,0 +1 ##
+text/plain
\ No newline at end of property
Modified: 
trunk/org.vexi-launcher/src/test/java/org/vexi/launcher/TestLauncher.java
===================================================================
--- trunk/org.vexi-launcher/src/test/java/org/vexi/launcher/TestLauncher.java   
2013-12-19 13:26:44 UTC (rev 4615)
+++ trunk/org.vexi-launcher/src/test/java/org/vexi/launcher/TestLauncher.java   
2013-12-19 18:02:49 UTC (rev 4616)
@@ -10,13 +10,13 @@
 
        
        public void testCheckUrls(){
-               Launcher l = new Launcher(){
+               Launcher l = new Launcher(null){
                    public Color getTextColor() { return null; }
                    public Color getBorderColor() { return null; }
                    public Color getBarColor() { return null; }
                    public URL getSplashImageResource() { return null; }
             public String[] getPermittedDomains() {    return new 
String[]{"localhost"}; }
-                       public Map getCerts() { return null; }
+                       public Map loadCerts() { return null; }
                        public String getVersion() { return null; }
                };
                boolean ok = l.checkUrl("http://localhost:7070/";);
@@ -24,13 +24,13 @@
        }
        
        public void testCheckUrlsSubdomain(){
-               Launcher l = new Launcher(){
+               Launcher l = new Launcher(null){
             public Color getTextColor() { return null; }
             public Color getBorderColor() { return null; }
             public Color getBarColor() { return null; }
                    public URL getSplashImageResource() { return null; }
                        public String[] getPermittedDomains() { return new 
String[]{"emanate5.com"}; }
-                       public Map getCerts() { return null; }
+                       public Map loadCerts() { return null; }
                        public String getVersion() { return null; }
                };
                boolean ok = l.checkUrl("http://blah.emanate5.com/foo/x";);

This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.


------------------------------------------------------------------------------
Rapidly troubleshoot problems before they affect your business. Most IT 
organizations don't have a clear picture of how application performance 
affects their revenue. With AppDynamics, you get 100% visibility into your 
Java,.NET, & PHP application. Start your 15-day FREE TRIAL of AppDynamics Pro!
http://pubads.g.doubleclick.net/gampad/clk?id=84349831&iu=/4140/ostg.clktrk
_______________________________________________
Vexi-svn mailing list
Vexi-svn@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/vexi-svn

Reply via email to