changeset 1595:0b4d2e77cf97

2009-06-10 Omair Majid <omajid@redhat.com> * rt/net/sourceforge/jnlp/JNLPFile.java (needsNewJVM): New function. Returns true if the JNLP file requires creating a new JVM. (getNewVMArgs): New function. Returns arguments to pass to the new JVM. * rt/net/sourceforge/jnlp/JREDesc.java (getVMArgs): Fix javadoc to reflect that return value can be null. * rt/net/sourceforge/jnlp/Launcher.java (launchExternal): Modify to take in arguments to pass to the JVM and arguments to pass to Netx. Try to use the local file to launch this instance if possible. (launchExternal): Delegate to new launchExternal. (launchExternal): New method. Take in arguments to pass to the JVM and arguments to pass to Netx. Launch Netx with the with the appropriate arguments. (launchApplication): If needed, launch a new JVM and pass along the Netx arguments. * rt/net/sourceforge/jnlp/Parser.java: (getJRE): Call checkVMArgs to check 'java-vm-args' for security. If that fails, assume 'java-vm-args' is null. (checkVMArgs): New method. Check that the vmArgs variable contains safe arguments only. (getValidVMArguments): New method. (getValidStartingVMArguments): New method. * rt/net/sourceforge/jnlp/resources/Messages.properties: Add BXnofork. * rt/net/sourceforge/jnlp/runtime/Boot/java: Add -Xnofork to helpMessage (main): Check for '-Xnofork'. Set initial arguments. * rt/net/sourceforge/jnlp/runtime/JNLPRuntime.java: Add forksAllowed to store whether creating a new JVM is allowed. Add initialArguments to store the arguments to Netx. (getForksAllowed): New function. Check if creating a new JVM is allowed. (setForksAllowed): New function. Set whether creating a JVM is allowed. (setInitialArguments): New function. Store the arguments passed to Netx. (getInitialArguments): New function. Return the arguments passed to Netx.
author Omair Majid <omajid@redhat.com>
date Wed, 10 Jun 2009 12:00:53 -0400
parents c68aaf028ad2
children 0a1a8cd8eda3
files ChangeLog rt/net/sourceforge/jnlp/JNLPFile.java rt/net/sourceforge/jnlp/JREDesc.java rt/net/sourceforge/jnlp/Launcher.java rt/net/sourceforge/jnlp/Parser.java rt/net/sourceforge/jnlp/resources/Messages.properties rt/net/sourceforge/jnlp/runtime/Boot.java rt/net/sourceforge/jnlp/runtime/JNLPRuntime.java
diffstat 8 files changed, 321 insertions(+), 36 deletions(-) [+]
line wrap: on
line diff
--- a/ChangeLog	Wed Jun 10 10:53:03 2009 -0400
+++ b/ChangeLog	Wed Jun 10 12:00:53 2009 -0400
@@ -1,3 +1,40 @@
+2009-06-10  Omair Majid  <omajid@redhat.com>
+
+	* rt/net/sourceforge/jnlp/JNLPFile.java
+	(needsNewJVM): New function. Returns true if the JNLP file requires 
+	creating a new JVM.
+	(getNewVMArgs): New function. Returns arguments to pass to the new JVM.
+	* rt/net/sourceforge/jnlp/JREDesc.java
+	(getVMArgs): Fix javadoc to reflect that return value can be null.
+	* rt/net/sourceforge/jnlp/Launcher.java
+	(launchExternal): Modify to take in arguments to pass to the JVM and 
+	arguments to pass to Netx. Try to use the local file to launch this 
+	instance if possible.
+	(launchExternal): Delegate to new launchExternal.
+	(launchExternal): New method. Take in arguments to pass to the JVM and 
+	arguments to pass to Netx. Launch Netx with the with the appropriate 
+	arguments.
+	(launchApplication): If needed, launch a new JVM and pass along the Netx 
+	arguments.
+	* rt/net/sourceforge/jnlp/Parser.java:
+	(getJRE): Call checkVMArgs to check 'java-vm-args' for security. If that 
+	fails, assume 'java-vm-args' is null.
+	(checkVMArgs): New method. Check that the vmArgs variable contains safe 
+	arguments only.
+	(getValidVMArguments): New method.
+	(getValidStartingVMArguments): New method.
+	* rt/net/sourceforge/jnlp/resources/Messages.properties: Add BXnofork.
+	* rt/net/sourceforge/jnlp/runtime/Boot/java:
+	Add -Xnofork to helpMessage
+	(main): Check for '-Xnofork'. Set initial arguments.
+	* rt/net/sourceforge/jnlp/runtime/JNLPRuntime.java:
+	Add forksAllowed to store whether creating a new JVM is allowed. Add
+	initialArguments to store the arguments to Netx.
+	(getForksAllowed): New function. Check if creating a new JVM is allowed.
+	(setForksAllowed): New function. Set whether creating a JVM is allowed.
+	(setInitialArguments): New function. Store the arguments passed to Netx.
+	(getInitialArguments): New function. Return the arguments passed to Netx. 
+
 2009-06-10  Omair Majid  <omajid@redhat.com>
 
 	* rt/net/sourceforge/jnlp/services/XBasicService.java
--- a/rt/net/sourceforge/jnlp/JNLPFile.java	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/JNLPFile.java	Wed Jun 10 12:00:53 2009 -0400
@@ -17,12 +17,19 @@
 
 package net.sourceforge.jnlp;
 
-import java.io.*;
-import java.net.*;
-import java.util.*;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
 
-import net.sourceforge.jnlp.cache.*;
-import net.sourceforge.jnlp.runtime.*;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
 
 /**
  * Provides methods to access the information in a Java Network
@@ -520,4 +527,48 @@
         }
     }
 
+    /**
+     * 
+     * @return true if the JNLP file specifies things that can only be 
+     * applied on a new vm (eg: different max heap memory)
+     */
+    public boolean needsNewVM() {
+        
+        if (getNewVMArgs().size() == 0) {
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    /**
+     *  @return a list of args to pass to the new 
+     *  JVM based on this JNLP file
+     */
+    public List<String> getNewVMArgs() {
+        
+        List<String> newVMArgs = new LinkedList<String>();
+        
+        JREDesc[] jres = getResources().getJREs();
+        for (int jreIndex = 0; jreIndex < jres.length; jreIndex++) {
+            String initialHeapSize = jres[jreIndex].getInitialHeapSize();
+            if (initialHeapSize != null) {
+                newVMArgs.add("-Xms" + initialHeapSize);
+            }
+            
+            String maxHeapSize = jres[jreIndex].getMaximumHeapSize();
+            if (maxHeapSize != null) {
+                newVMArgs.add("-Xmx" + maxHeapSize);
+            }
+            
+            String vmArgsFromJre = jres[jreIndex].getVMArgs();
+            if (vmArgsFromJre != null) {
+                String[] args = vmArgsFromJre.split(" ");
+                newVMArgs.addAll(Arrays.asList(args));
+            }
+        }
+        
+        return newVMArgs;
+    }
+
 }
--- a/rt/net/sourceforge/jnlp/JREDesc.java	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/JREDesc.java	Wed Jun 10 12:00:53 2009 -0400
@@ -121,6 +121,7 @@
 
     /**
      * Returns the additional arguments to pass to the Java VM
+     * Can be null
      */
     public String getVMArgs() {
         return vmArgs;
--- a/rt/net/sourceforge/jnlp/Launcher.java	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/Launcher.java	Wed Jun 10 12:00:53 2009 -0400
@@ -17,16 +17,27 @@
 
 package net.sourceforge.jnlp;
 
-import java.applet.*;
+import java.applet.Applet;
 import java.awt.Container;
-import java.io.*;
-import java.net.*;
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.jar.JarFile;
-import java.lang.reflect.*;
 
-import net.sourceforge.jnlp.cache.*;
-import net.sourceforge.jnlp.runtime.*;
-import net.sourceforge.jnlp.util.*;
+import net.sourceforge.jnlp.cache.CacheUtil;
+import net.sourceforge.jnlp.cache.ResourceTracker;
+import net.sourceforge.jnlp.cache.UpdatePolicy;
+import net.sourceforge.jnlp.runtime.AppThreadGroup;
+import net.sourceforge.jnlp.runtime.AppletInstance;
+import net.sourceforge.jnlp.runtime.ApplicationInstance;
+import net.sourceforge.jnlp.runtime.JNLPClassLoader;
+import net.sourceforge.jnlp.runtime.JNLPRuntime;
+import net.sourceforge.jnlp.util.Reflect;
 
 /**
  * Launches JNLPFiles either in the foreground or background.<p>
@@ -238,44 +249,67 @@
      * application's output is sent to the system out and it's
      * standard input channel is closed.
      *
+     * @param vmArgs the arguments to pass to the new JVM. Can be empty but
+     *        must not be null.
      * @param file the JNLP file to launch
+     * @param javawsArgs the arguments to pass to the javaws command. Can be
+     *        an empty list but must not be null.
      * @throws LaunchException if there was an exception 
      */
-    public void launchExternal(JNLPFile file) throws LaunchException {
-        if (file.getSourceLocation() != null)
-            launchExternal(file.getSourceLocation());
-        else if (file.getFileLocation() != null)
-            launchExternal(file.getFileLocation());
+    public void launchExternal(List<String> vmArgs, JNLPFile file, List<String> javawsArgs) throws LaunchException {
+        List<String> updatedArgs = new LinkedList<String>(javawsArgs);
+        
+        if (file.getFileLocation() != null)
+            updatedArgs.add(file.getFileLocation().toString());
+        else if (file.getSourceLocation() != null)
+            updatedArgs.add(file.getFileLocation().toString());
         else
             launchError(new LaunchException(file, null, R("LSFatal"), R("LCExternalLaunch"), R("LNullLocation"), R("LNullLocationInfo")));
+        
+        launchExternal(vmArgs, updatedArgs);
+        
+    }
+    
+    /**
+     * Launches the JNLP file in a new JVM instance.  The launched
+     * application's output is sent to the system out and it's
+     * standard input channel is closed.
+     *
+     * @param url the URL of the JNLP file to launch
+     * @throws LaunchException if there was an exception 
+     */
+    public void launchExternal(URL url) throws LaunchException {
+        List<String> javawsArgs = new LinkedList<String>();
+        javawsArgs.add(url.toString());
+        launchExternal(new LinkedList<String>(), javawsArgs);       
     }
 
     /**
      * Launches the JNLP file at the specified location in a new JVM
      * instance.  The launched application's output is sent to the
      * system out and it's standard input channel is closed.
-     *
-     * @param location the URL of the JNLP file to launch
+     * @param vmArgs the arguments to pass to the jvm
+     * @param javawsArgs the arguments to pass to javaws (aka Netx)
      * @throws LaunchException if there was an exception 
      */
-    public void launchExternal(URL location) throws LaunchException {
+    public void launchExternal(List<String> vmArgs, List<String> javawsArgs) throws LaunchException {
         try {
-            URL cs = Launcher.class.getProtectionDomain().getCodeSource().getLocation();
-            if (JNLPRuntime.isDebug())
-                System.out.println("netx.jar path: "+cs.getPath());
 
-            File netxFile = new File(cs.getPath());
-            if (!netxFile.exists())
-                throw launchError(new LaunchException(null, null, R("LSFatal"), R("LCExternalLaunch"), R("LNetxJarMissing"), R("LNetxJarMissingInfo")));
-
-            String command[] = {
-                "javaw",
-                "-jar",
-                netxFile.toString(),
-                "-jnlp",
-                location.toString(),
-                "-verbose",
-            };
+            List<String> commands = new LinkedList<String>();
+            
+            String pathToWebstartBinary = System.getProperty("java.home") + 
+                                      File.separatorChar + 
+                                      "bin" + 
+                                      File.separatorChar + 
+                                      "javaws";
+            commands.add(pathToWebstartBinary);
+            // use -Jargument format to pass arguments to the JVM through the launcher
+            for (String arg: vmArgs) {
+                commands.add("-J" + arg);
+            }
+            commands.addAll(javawsArgs);
+            
+            String[] command = commands.toArray(new String[] {});
 
             Process p = Runtime.getRuntime().exec(command);
             new StreamEater(p.getErrorStream()).start();
@@ -331,6 +365,15 @@
             throw launchError(new LaunchException(file, null, R("LSFatal"), R("LCClient"), R("LNotApplication"), R("LNotApplicationInfo")));
 
         try {
+            
+            if (JNLPRuntime.getForksAllowed() && file.needsNewVM()) {
+                List<String> netxArguments = new LinkedList<String>();
+                netxArguments.add("-Xnofork");
+                netxArguments.addAll(JNLPRuntime.getInitialArguments());
+                launchExternal(file.getNewVMArgs(), netxArguments);
+                return null;
+            }
+            
             final int preferredWidth = 500;
             final int preferredHeight = 400;
             JNLPSplashScreen splashScreen = null;
--- a/rt/net/sourceforge/jnlp/Parser.java	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/Parser.java	Wed Jun 10 12:00:53 2009 -0400
@@ -281,6 +281,11 @@
         Version version = getVersion(node, "version", null);
         URL location = getURL(node, "href", base);
         String vmArgs = getAttribute(node, "java-vm-args",null);
+        try {
+            checkVMArgs(vmArgs);
+        } catch (IllegalArgumentException argumentException) {
+            vmArgs = null;
+        }
         String initialHeap = getAttribute(node, "initial-heap-size", null);
         String maxHeap = getAttribute(node, "max-heap-size", null);
         List resources = getResources(node, true);
@@ -291,6 +296,8 @@
         return new JREDesc(version, location, vmArgs, initialHeap, maxHeap, resources);
     }
 
+
+
     /**
      * Returns the JAR element at the specified node.
      *
@@ -993,6 +1000,111 @@
         else
             return new Version(version);
     }
+    
+    /**
+     * Check that the VM args are valid and safe
+     * @param vmArgs a string containing the args
+     * @throws ParseException if the VM arguments are invalid or dangerous
+     */
+    private void checkVMArgs(String vmArgs) throws IllegalArgumentException {
+        if (vmArgs == null) {
+            return;
+        }
+        
+        List<String> validArguments = Arrays.asList(getValidVMArguments());
+        List<String> validStartingArguments = Arrays.asList(getValidStartingVMArguments());
+        
+        String[] arguments = vmArgs.split(" ");
+        boolean argumentIsValid = false;
+        for (String argument: arguments) {
+            argumentIsValid = false;
+            
+            if (validArguments.contains(argument)) {
+                argumentIsValid = true;
+            } else {
+                for (String validStartingArgument: validStartingArguments) {
+                    if (argument.startsWith(validStartingArgument)) {
+                        argumentIsValid = true;
+                        break;
+                    }
+                }
+            }
+            
+            if (!argumentIsValid) {
+                throw new IllegalArgumentException(argument);
+            }
+        }
+        
+    }
+    
+    /**
+     * Returns an array of valid (ie safe and supported) arguments for the JVM
+     * 
+     * Based on http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/syntax.html
+     */
+    private String[] getValidVMArguments() {
+        return new String[] {
+        "-d32",                             /* use a 32-bit data model if available */
+        "-client",                          /* to select the client VM */
+        "-server",                          /* to select the server VM */
+        "-verbose",                         /* enable verbose output */
+        "-version",                         /* print product version and exit */
+        "-showversion",                     /* print product version and continue */                              
+        "-help",                            /* print this help message */
+        "-X",                               /* print help on non-standard options */
+        "-ea",                              /* enable assertions */
+        "-enableassertions",                /* enable assertions */
+        "-da",                              /* disable assertions */
+        "-disableassertions",               /* disable assertions */
+        "-esa",                             /* enable system assertions */
+        "-enablesystemassertions",          /* enable system assertions */
+        "-dsa",                             /* disable system assertione */
+        "-disablesystemassertions",         /* disable system assertione */
+        "-Xmixed",                          /* mixed mode execution (default) */
+        "-Xint",                            /* interpreted mode execution only */
+        "-Xnoclassgc",                      /* disable class garbage collection */
+        "-Xincgc",                          /* enable incremental garbage collection */
+        "-Xbatch",                          /* disable background compilation */
+        "-Xprof",                           /* output cpu profiling data */
+        "-Xdebug",                          /* enable remote debugging */
+        "-Xfuture",                         /* enable strictest checks, anticipating future default */
+        "-Xrs",                             /* reduce use of OS signals by Java/VM (see documentation) */
+        "-XX:+ForceTimeHighResolution",     /* use high resolution timer */
+        "-XX:-ForceTimeHighResolution",     /* use low resolution (default) */
+        };
+    }
+
+    /**
+     * Returns an array containing the starts of valid (ie safe and supported)
+     * arguments for the JVM
+     * 
+     * Based on http://java.sun.com/javase/6/docs/technotes/guides/javaws/developersguide/syntax.html
+     */
+    private String[] getValidStartingVMArguments() {
+        return new String[] {
+        "-ea",                          /* enable assertions for classes */
+        "-enableassertions",            /* enable assertions for classes */
+        "-da",                          /* disable assertions for classes */
+        "-disableassertions",           /* disable assertions for classes */
+        "-verbose",                     /* enable verbose output */
+        "-Xms",                         /* set initial Java heap size */
+        "-Xmx",                         /* set maximum Java heap size */
+        "-Xss",                         /* set java thread stack size */
+        "-XX:NewRatio",                 /* set Ratio of new/old gen sizes */
+        "-XX:NewSize",                  /* set initial size of new generation */
+        "-XX:MaxNewSize",               /* set max size of new generation */
+        "-XX:PermSize",                 /* set initial size of permanent gen */
+        "-XX:MaxPermSize",              /* set max size of permanent gen */
+        "-XX:MaxHeapFreeRatio",         /* heap free percentage (default 70) */
+        "-XX:MinHeapFreeRatio",         /* heap free percentage (default 40) */
+        "-XX:UseSerialGC",              /* use serial garbage collection */
+        "-XX:ThreadStackSize",          /* thread stack size (in KB) */
+        "-XX:MaxInlineSize",            /* set max num of bytecodes to inline */
+        "-XX:ReservedCodeCacheSize",    /* Reserved code cache size (bytes) */
+        "-XX:MaxDirectMemorySize",
+
+        };
+    }
 
     /**
      * Returns the same result as getAttribute except that if strict
--- a/rt/net/sourceforge/jnlp/resources/Messages.properties	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/resources/Messages.properties	Wed Jun 10 12:00:53 2009 -0400
@@ -130,6 +130,7 @@
 BOStrict    = Enables strict checking of JNLP file format.
 BOViewer    = Shows the trusted certificate viewer.
 BOUmask     = Sets the umask for files created by an application.
+BXnofork    = Do not create another JVM.
 BOHelp      = Print this message and exit.
 
 # Cache
--- a/rt/net/sourceforge/jnlp/runtime/Boot.java	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/runtime/Boot.java	Wed Jun 10 12:00:53 2009 -0400
@@ -24,6 +24,7 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 
 import javax.net.ssl.HttpsURLConnection;
@@ -116,6 +117,7 @@
         + "  -headless             "+R("BOHeadless")+"\n"
         + "  -strict               "+R("BOStrict")+"\n"
         + "  -umask=value          "+R("BOUmask")+"\n"
+        + "  -Xnofork              "+R("BXnofork")+"\n"
         + "  -help                 "+R("BOHelp")+"\n";
 
     private static final String doubleArgs = "-basedir -jnlp -arg -param -property -update";
@@ -169,6 +171,10 @@
         if (null != getOption("-noupdate"))
             JNLPRuntime.setDefaultUpdatePolicy(UpdatePolicy.NEVER);
         
+        if (null != getOption("-Xnofork")) {
+            JNLPRuntime.setForksAllowed(false);
+        }
+        
         // wire in custom authenticator
         try {
             SSLSocketFactory sslSocketFactory;
@@ -183,12 +189,13 @@
             e.printStackTrace();
         }
 
+        JNLPRuntime.setInitialArgments(Arrays.asList(argsIn));
+        
         // do in a privileged action to clear the security context of
         // the Boot13 class, which doesn't have any privileges in
         // JRE1.3; JRE1.4 works without Boot13 or this PrivilegedAction.
         AccessController.doPrivileged(new Boot());
 
-        args = null; // might save a couple bytes...
     }
 
     /**
--- a/rt/net/sourceforge/jnlp/runtime/JNLPRuntime.java	Wed Jun 10 10:53:03 2009 -0400
+++ b/rt/net/sourceforge/jnlp/runtime/JNLPRuntime.java	Wed Jun 10 12:00:53 2009 -0400
@@ -21,6 +21,7 @@
 import java.awt.*;
 import java.text.*;
 import java.util.*;
+import java.util.List;
 import java.security.*;
 import javax.jnlp.*;
 
@@ -96,7 +97,14 @@
 
     /** set to true if this is a webstart application. */
     private static boolean isWebstartApplication; 
+    
+    /** set to false to indicate another JVM should not be spawned, even if necessary */
+    private static boolean forksAllowed = true;
 
+    /** contains the arguments passed to the jnlp runtime */
+    private static List<String> initialArguments;
+
+    
     /**
      * Returns whether the JNLP runtime environment has been
      * initialized.  Once initialized, some properties such as the
@@ -429,6 +437,18 @@
     }
 
     /**
+     * Returns true if the current runtime will fork
+     */
+    public static boolean getForksAllowed() {
+        return forksAllowed;
+    }
+    
+    public static void setForksAllowed(boolean value) {
+        checkInitialized();
+        forksAllowed = value;
+    }
+    
+    /**
      * Throws an exception if called when the runtime is
      * already initialized.
      */
@@ -491,6 +511,19 @@
         }
     }
 
+
+    public static void setInitialArgments(List<String> args) {
+        checkInitialized();
+        SecurityManager securityManager = System.getSecurityManager();
+        if (securityManager != null)
+            securityManager.checkPermission(new AllPermission());
+        initialArguments = args;
+    }
+    
+    public static List<String> getInitialArguments() {
+        return initialArguments;
+    }
+    
 }