6742159: (launcher) improve the java launching mechanism
authorksrini
Wed, 24 Sep 2008 15:07:41 -0700
changeset 1323 e14a3b3536cd
parent 1322 d038148778cc
child 1324 f1f7222489a4
6742159: (launcher) improve the java launching mechanism Summary: improves launching, also addresses 6694671 and 6749732 Reviewed-by: darcy, dholmes
jdk/src/share/bin/emessages.h
jdk/src/share/bin/java.c
jdk/src/share/bin/java.h
jdk/src/share/classes/sun/launcher/LauncherHelp.java
jdk/src/share/classes/sun/launcher/LauncherHelper.java
jdk/src/share/classes/sun/launcher/resources/launcher.properties
jdk/src/solaris/bin/java_md.c
jdk/src/windows/bin/java_md.c
jdk/test/tools/launcher/Arrrghs.java
jdk/test/tools/launcher/Arrrghs.sh
jdk/test/tools/launcher/TestHelper.java
--- a/jdk/src/share/bin/emessages.h	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/src/share/bin/emessages.h	Wed Sep 24 15:07:41 2008 -0700
@@ -50,11 +50,6 @@
 #define JAR_ERROR2      "Error: Unable to access jarfile %s"
 #define JAR_ERROR3      "Error: Invalid or corrupt jarfile %s"
 
-#define CLS_ERROR1      "Error: Could not find the main class %s.\n" JNI_ERROR
-#define CLS_ERROR2      "Error: Failed to load Main Class: %s\n%s"
-#define CLS_ERROR3      "Error: No main method found in specified class.\n" GEN_ERROR
-#define CLS_ERROR4      "Error: Main method not public\n" GEN_ERROR
-
 #define CFG_WARN1       "Warning: %s VM not supported; %s VM will be used"
 #define CFG_WARN2       "Warning: No leading - on line %d of `%s'"
 #define CFG_WARN3       "Warning: Missing VM type on line %d of `%s'"
--- a/jdk/src/share/bin/java.c	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/src/share/bin/java.c	Wed Sep 24 15:07:41 2008 -0700
@@ -102,8 +102,7 @@
                               InvocationFunctions *ifn);
 static jstring NewPlatformString(JNIEnv *env, char *s);
 static jobjectArray NewPlatformStringArray(JNIEnv *env, char **strv, int strc);
-static jclass LoadClass(JNIEnv *env, char *name);
-static jstring GetMainClassName(JNIEnv *env, char *jarname);
+static jclass LoadMainClass(JNIEnv *env, jboolean isJar, char *name);
 
 static void TranslateApplicationArgs(int jargc, const char **jargv, int *pargc, char ***pargv);
 static jboolean AddApplicationOptions(int cpathc, const char **cpathv);
@@ -301,6 +300,22 @@
 
 }
 
+#define CHECK_EXCEPTION_NULL_LEAVE(e) \
+    if ((*env)->ExceptionOccurred(env)) { \
+        JLI_ReportExceptionDescription(env); \
+        goto leave; \
+    } \
+    if ((e) == NULL) { \
+        JLI_ReportErrorMessage(JNI_ERROR); \
+        goto leave; \
+    }
+
+#define CHECK_EXCEPTION_LEAVE(rv) \
+    if ((*env)->ExceptionOccurred(env)) { \
+        JLI_ReportExceptionDescription(env); \
+        ret = (rv); \
+        goto leave; \
+    }
 
 int JNICALL
 JavaMain(void * _args)
@@ -321,9 +336,7 @@
     int ret = 0;
     jlong start, end;
 
-
     /* Initialize the virtual machine */
-
     start = CounterGet();
     if (!InitializeJVM(&vm, &env, &ifn)) {
         JLI_ReportErrorMessage(JVM_ERROR1);
@@ -332,11 +345,7 @@
 
     if (printVersion || showVersion) {
         PrintJavaVersion(env, showVersion);
-        if ((*env)->ExceptionOccurred(env)) {
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-            goto leave;
-        }
+        CHECK_EXCEPTION_LEAVE(0);
         if (printVersion) {
             ret = 0;
             goto leave;
@@ -346,11 +355,7 @@
     /* If the user specified neither a class name nor a JAR file */
     if (printXUsage || printUsage || (jarfile == 0 && classname == 0)) {
         PrintUsage(env, printXUsage);
-        if ((*env)->ExceptionOccurred(env)) {
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-            ret=1;
-        }
+        CHECK_EXCEPTION_LEAVE(1);
         goto leave;
     }
 
@@ -395,99 +400,25 @@
      *          the environment (and remove these comments).
      */
     if (jarfile != 0) {
-        mainClassName = GetMainClassName(env, jarfile);
-        if ((*env)->ExceptionOccurred(env)) {
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-            goto leave;
-        }
-        if (mainClassName == NULL) {
-          JLI_ReportErrorMessage(JAR_ERROR1,jarfile, GEN_ERROR);
-          goto leave;
-        }
-        classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
-        if (classname == NULL) {
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-            goto leave;
-        }
-        mainClass = LoadClass(env, classname);
-        if(mainClass == NULL) { /* exception occured */
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(CLS_ERROR1, classname);
-            goto leave;
-        }
-        (*env)->ReleaseStringUTFChars(env, mainClassName, classname);
+        mainClass = LoadMainClass(env, JNI_TRUE, jarfile);
     } else {
-      mainClassName = NewPlatformString(env, classname);
-      if (mainClassName == NULL) {
-        JLI_ReportErrorMessage(CLS_ERROR2, classname, GEN_ERROR);
-        goto leave;
-      }
-      classname = (char *)(*env)->GetStringUTFChars(env, mainClassName, 0);
-      if (classname == NULL) {
-        JLI_ReportExceptionDescription(env);
-        JLI_ReportErrorMessage(JNI_ERROR);
-        goto leave;
-      }
-      mainClass = LoadClass(env, classname);
-      if(mainClass == NULL) { /* exception occured */
-        JLI_ReportExceptionDescription(env);
-        JLI_ReportErrorMessage(CLS_ERROR1, classname);
-        goto leave;
-      }
-      (*env)->ReleaseStringUTFChars(env, mainClassName, classname);
+        mainClass = LoadMainClass(env, JNI_FALSE, classname);
     }
+    CHECK_EXCEPTION_NULL_LEAVE(mainClass);
 
-    /* Get the application's main method */
+    /*
+     * The LoadMainClass not only loads the main class, it will also ensure
+     * that the main method's signature is correct, therefore further checking
+     * is not required. The main method is invoked here so that extraneous java
+     * stacks are not in the application stack trace.
+     */
     mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                        "([Ljava/lang/String;)V");
-    if (mainID == NULL) {
-        if ((*env)->ExceptionOccurred(env)) {
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-        } else {
-          JLI_ReportErrorMessage(CLS_ERROR3);
-        }
-        goto leave;
-    }
-
-    {    /* Make sure the main method is public */
-        jint mods;
-        jmethodID mid;
-        jobject obj = (*env)->ToReflectedMethod(env, mainClass,
-                                                mainID, JNI_TRUE);
-
-        if( obj == NULL) { /* exception occurred */
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-            goto leave;
-        }
-
-        mid =
-          (*env)->GetMethodID(env,
-                              (*env)->GetObjectClass(env, obj),
-                              "getModifiers", "()I");
-        if ((*env)->ExceptionOccurred(env)) {
-            JLI_ReportExceptionDescription(env);
-            JLI_ReportErrorMessage(JNI_ERROR);
-            goto leave;
-        }
-
-        mods = (*env)->CallIntMethod(env, obj, mid);
-        if ((mods & 1) == 0) { /* if (!Modifier.isPublic(mods)) ... */
-            JLI_ReportErrorMessage(CLS_ERROR4);
-            goto leave;
-        }
-    }
+    CHECK_EXCEPTION_NULL_LEAVE(mainID);
 
     /* Build argument array */
     mainArgs = NewPlatformStringArray(env, argv, argc);
-    if (mainArgs == NULL) {
-        JLI_ReportExceptionDescription(env);
-        JLI_ReportErrorMessage(JNI_ERROR);
-        goto leave;
-    }
+    CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
 
     /* Invoke main method. */
     (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
@@ -498,8 +429,9 @@
      */
     ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
 
+leave:
     /*
-     * Detach the main thread so that it appears to have ended when
+     * Always detach the main thread so that it appears to have ended when
      * the application's main method exits.  This will invoke the
      * uncaught exception handler machinery if main threw an
      * exception.  An uncaught exception handler cannot change the
@@ -508,10 +440,7 @@
     if ((*vm)->DetachCurrentThread(vm) != 0) {
         JLI_ReportErrorMessage(JVM_ERROR2);
         ret = 1;
-        goto leave;
     }
-
- leave:
     /*
      * Wait for all non-daemon threads to end, then destroy the VM.
      * This will actually create a trivial new Java waiter thread
@@ -525,7 +454,6 @@
     return ret;
 }
 
-
 /*
  * Checks the command line options to find which JVM type was
  * specified.  If no command line option was given for the JVM type,
@@ -1159,7 +1087,7 @@
         if (propname) {
             jclass cls;
             jmethodID mid;
-            NULL_CHECK0 (cls = (*env)->FindClass(env, "java/lang/System"));
+            NULL_CHECK0 (cls = FindBootStrapClass(env, "java/lang/System"));
             NULL_CHECK0 (mid = (*env)->GetStaticMethodID(
                                    env, cls,
                                    "getProperty",
@@ -1174,7 +1102,7 @@
 static jboolean isEncodingSupported(JNIEnv *env, jstring enc) {
     jclass cls;
     jmethodID mid;
-    NULL_CHECK0 (cls = (*env)->FindClass(env, "java/nio/charset/Charset"));
+    NULL_CHECK0 (cls = FindBootStrapClass(env, "java/nio/charset/Charset"));
     NULL_CHECK0 (mid = (*env)->GetStaticMethodID(
                            env, cls,
                            "isSupported",
@@ -1203,8 +1131,8 @@
         jstring str = 0;
         (*env)->SetByteArrayRegion(env, ary, 0, len, (jbyte *)s);
         if (!(*env)->ExceptionOccurred(env)) {
+            NULL_CHECK0(cls = FindBootStrapClass(env, "java/lang/String"));
             if (isEncodingSupported(env, enc) == JNI_TRUE) {
-                NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String"));
                 NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "<init>",
                                           "([BLjava/lang/String;)V"));
                 str = (*env)->NewObject(env, cls, mid, ary, enc);
@@ -1215,7 +1143,6 @@
                   the encoding name, in which the StringCoding class will
                   pickup the iso-8859-1 as the fallback converter for us.
                 */
-                NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String"));
                 NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "<init>",
                                           "([B)V"));
                 str = (*env)->NewObject(env, cls, mid, ary);
@@ -1238,7 +1165,7 @@
     jarray ary;
     int i;
 
-    NULL_CHECK0(cls = (*env)->FindClass(env, "java/lang/String"));
+    NULL_CHECK0(cls = FindBootStrapClass(env, "java/lang/String"));
     NULL_CHECK0(ary = (*env)->NewObjectArray(env, strc, cls, 0));
     for (i = 0; i < strc; i++) {
         jstring str = NewPlatformString(env, *strv++);
@@ -1250,25 +1177,26 @@
 }
 
 /*
- * Loads a class, convert the '.' to '/'.
+ * Loads a class and verifies that the main class is present and it is ok to
+ * call it for more details refer to the java implementation.
  */
 static jclass
-LoadClass(JNIEnv *env, char *name)
+LoadMainClass(JNIEnv *env, jboolean isJar, char *name)
 {
-    char *buf = JLI_MemAlloc(JLI_StrLen(name) + 1);
-    char *s = buf, *t = name, c;
     jclass cls;
+    jmethodID mid;
+    jstring str;
+    jobject result;
     jlong start, end;
 
-    if (JLI_IsTraceLauncher())
+    if (JLI_IsTraceLauncher()) {
         start = CounterGet();
-
-    do {
-        c = *t++;
-        *s++ = (c == '.') ? '/' : c;
-    } while (c != '\0');
-    cls = (*env)->FindClass(env, buf);
-    JLI_MemFree(buf);
+    }
+    NULL_CHECK0(cls = FindBootStrapClass(env, "sun/launcher/LauncherHelper"));
+    NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls, "checkAndLoadMain",
+                                          "(ZZLjava/lang/String;)Ljava/lang/Object;"));
+    str = (*env)->NewStringUTF(env, name);
+    result = (*env)->CallStaticObjectMethod(env, cls, mid, JNI_TRUE, isJar, str);
 
     if (JLI_IsTraceLauncher()) {
         end   = CounterGet();
@@ -1277,49 +1205,9 @@
         printf("----_JAVA_LAUNCHER_DEBUG----\n");
     }
 
-    return cls;
+    return (jclass)result;
 }
 
-
-/*
- * Returns the main class name for the specified jar file.
- */
-static jstring
-GetMainClassName(JNIEnv *env, char *jarname)
-{
-#define MAIN_CLASS "Main-Class"
-    jclass cls;
-    jmethodID mid;
-    jobject jar, man, attr;
-    jstring str, result = 0;
-
-    NULL_CHECK0(cls = (*env)->FindClass(env, "java/util/jar/JarFile"));
-    NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "<init>",
-                                          "(Ljava/lang/String;)V"));
-    NULL_CHECK0(str = NewPlatformString(env, jarname));
-    NULL_CHECK0(jar = (*env)->NewObject(env, cls, mid, str));
-    NULL_CHECK0(mid = (*env)->GetMethodID(env, cls, "getManifest",
-                                          "()Ljava/util/jar/Manifest;"));
-    man = (*env)->CallObjectMethod(env, jar, mid);
-    if (man != 0) {
-        NULL_CHECK0(mid = (*env)->GetMethodID(env,
-                                    (*env)->GetObjectClass(env, man),
-                                    "getMainAttributes",
-                                    "()Ljava/util/jar/Attributes;"));
-        attr = (*env)->CallObjectMethod(env, man, mid);
-        if (attr != 0) {
-            NULL_CHECK0(mid = (*env)->GetMethodID(env,
-                                    (*env)->GetObjectClass(env, attr),
-                                    "getValue",
-                                    "(Ljava/lang/String;)Ljava/lang/String;"));
-            NULL_CHECK0(str = NewPlatformString(env, MAIN_CLASS));
-            result = (*env)->CallObjectMethod(env, attr, mid, str);
-        }
-    }
-    return result;
-}
-
-
 /*
  * For tools, convert command line args thus:
  *   javac -cp foo:foo/"*" -J-ms32m ...
@@ -1522,7 +1410,7 @@
     jclass ver;
     jmethodID print;
 
-    NULL_CHECK(ver = (*env)->FindClass(env, "sun/misc/Version"));
+    NULL_CHECK(ver = FindBootStrapClass(env, "sun/misc/Version"));
     NULL_CHECK(print = (*env)->GetStaticMethodID(env,
                                                  ver,
                                                  (extraLF == JNI_TRUE) ? "println" : "print",
@@ -1534,7 +1422,7 @@
 }
 
 /*
- * Prints default usage or the Xusage message, see sun.launcher.LauncherHelp.java
+ * Prints default usage or the Xusage message, see sun.launcher.LauncherHelper.java
  */
 static void
 PrintUsage(JNIEnv* env, jboolean doXUsage)
@@ -1544,7 +1432,7 @@
   jstring jprogname, vm1, vm2;
   int i;
 
-  NULL_CHECK(cls = (*env)->FindClass(env, "sun/launcher/LauncherHelp"));
+  NULL_CHECK(cls = FindBootStrapClass(env, "sun/launcher/LauncherHelper"));
 
 
   if (doXUsage) {
--- a/jdk/src/share/bin/java.h	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/src/share/bin/java.h	Wed Sep 24 15:07:41 2008 -0700
@@ -180,4 +180,15 @@
  */
 void InitLauncher(jboolean javaw);
 
+/*
+ * This allows for finding classes from the VM's bootstrap class loader directly,
+ * FindClass uses the application class loader internally, this will cause
+ * unnecessary searching of the classpath for the required classes.
+ *
+ */
+typedef jclass (JNICALL FindClassFromBootLoader_t(JNIEnv *env,
+                                                const char *name,
+                                                jboolean throwError));
+
+jclass FindBootStrapClass(JNIEnv *env, const char *classname);
 #endif /* _JAVA_H_ */
--- a/jdk/src/share/classes/sun/launcher/LauncherHelp.java	Wed Sep 24 15:19:07 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-
-/*
- * Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.  Sun designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Sun in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
- * CA 95054 USA or visit www.sun.com if you need additional information or
- * have any questions.
- */
-
-package sun.launcher;
-
-/*
- *
- *  <p><b>This is NOT part of any API supported by Sun Microsystems.
- *  If you write code that depends on this, you do so at your own
- *  risk.  This code and its internal interfaces are subject to change
- *  or deletion without notice.</b>
- *
- */
-
-/**
- * A utility package for the java(1), javaw(1) launchers.
- */
-import java.io.File;
-import java.io.PrintStream;
-import java.util.ResourceBundle;
-import java.text.MessageFormat;
-
-public class LauncherHelp {
-
-    private static final String defaultBundleName = "sun.launcher.resources.launcher";
-    private static ResourceBundle javarb = ResourceBundle.getBundle(defaultBundleName);
-
-    private static StringBuilder outBuf = new StringBuilder();
-
-    /** Creates a new instance of LauncherHelp, keep it a singleton */
-    private LauncherHelp(){}
-
-
-    /**
-     * A private helper method to get a localized message and also
-     * apply any arguments that we might pass.
-     */
-    private static String getLocalizedMessage(String key, Object... args) {
-        String msg = javarb.getString(key);
-        return (args != null) ? MessageFormat.format(msg, args) : msg;
-    }
-
-    /**
-     * The java -help message is split into 3 parts, an invariant, followed
-     * by a set of platform dependent variant messages, finally an invariant
-     * set of lines.
-     * This method initializes the help message for the first time, and also
-     * assembles the invariant header part of the message.
-     */
-    static void initHelpMessage(String progname) {
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.header", (progname == null) ? "java" : progname ));
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.datamodel", 32));
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.datamodel", 64));
-    }
-
-    /**
-     * Appends the vm selection messages to the header, already created.
-     * initHelpSystem must already be called.
-     */
-    static void appendVmSelectMessage(String vm1, String vm2) {
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.vmselect", vm1, vm2));
-    }
-
-    /**
-     * Appends the vm synoym message to the header, already created.
-     * initHelpSystem must be called before using this method.
-     */
-    static void appendVmSynonymMessage(String vm1, String vm2) {
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.hotspot", vm1, vm2));
-    }
-
-    /**
-     * Appends the vm Ergo message to the header, already created.
-     * initHelpSystem must be called before using this method.
-     */
-    static void appendVmErgoMessage(boolean isServerClass, String vm) {
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.ergo.message1", vm));
-        outBuf = (isServerClass)
-             ? outBuf.append(",\n" + getLocalizedMessage("java.launcher.ergo.message2") + "\n\n")
-             : outBuf.append(".\n\n");
-    }
-
-    /**
-     * Appends the last invariant part to the previously created messages,
-     * and finishes up the printing to the desired output stream.
-     * initHelpSystem must be called before using this method.
-     */
-    static void printHelpMessage(boolean printToStderr) {
-        PrintStream ostream = (printToStderr) ? System.err : System.out;
-        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.footer",  File.pathSeparator));
-        ostream.println(outBuf.toString());
-    }
-
-    /**
-     * Prints the Xusage text to the desired output stream.
-     */
-    static void printXUsageMessage(boolean printToStderr) {
-        PrintStream ostream =  (printToStderr) ? System.err : System.out;
-        ostream.println(getLocalizedMessage("java.launcher.X.usage",  File.pathSeparator));
-    }
-
-    /* Test code */
-    public static void main(String[] args) {
-        initHelpMessage("java");
-        appendVmSelectMessage("-client", "client");
-        appendVmSelectMessage("-server", "server");
-        appendVmSynonymMessage("-hotspot", "client");
-        appendVmErgoMessage(true, "server");
-        printHelpMessage(true);
-
-        System.err.println("------------------------------------");
-
-        printXUsageMessage(true);
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/launcher/LauncherHelper.java	Wed Sep 24 15:07:41 2008 -0700
@@ -0,0 +1,238 @@
+
+/*
+ * Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.launcher;
+
+/*
+ *
+ *  <p><b>This is NOT part of any API supported by Sun Microsystems.
+ *  If you write code that depends on this, you do so at your own
+ *  risk.  This code and its internal interfaces are subject to change
+ *  or deletion without notice.</b>
+ *
+ */
+
+/**
+ * A utility package for the java(1), javaw(1) launchers.
+ * The following are helper methods that the native launcher uses
+ * to perform checks etc. using JNI, see src/share/bin/java.c
+ */
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ResourceBundle;
+import java.text.MessageFormat;
+import java.util.jar.Attributes;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+
+public enum LauncherHelper {
+    INSTANCE;
+    private static final String defaultBundleName =
+            "sun.launcher.resources.launcher";
+    private static ResourceBundle javarb =
+            ResourceBundle.getBundle(defaultBundleName);
+    private static final String MAIN_CLASS = "Main-Class";
+
+    private static StringBuilder outBuf = new StringBuilder();
+
+    /**
+     * A private helper method to get a localized message and also
+     * apply any arguments that we might pass.
+     */
+    private static String getLocalizedMessage(String key, Object... args) {
+        String msg = javarb.getString(key);
+        return (args != null) ? MessageFormat.format(msg, args) : msg;
+    }
+
+    /**
+     * The java -help message is split into 3 parts, an invariant, followed
+     * by a set of platform dependent variant messages, finally an invariant
+     * set of lines.
+     * This method initializes the help message for the first time, and also
+     * assembles the invariant header part of the message.
+     */
+    static void initHelpMessage(String progname) {
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.header",
+                (progname == null) ? "java" : progname ));
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.datamodel",
+                32));
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.datamodel",
+                64));
+    }
+
+    /**
+     * Appends the vm selection messages to the header, already created.
+     * initHelpSystem must already be called.
+     */
+    static void appendVmSelectMessage(String vm1, String vm2) {
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.vmselect",
+                vm1, vm2));
+    }
+
+    /**
+     * Appends the vm synoym message to the header, already created.
+     * initHelpSystem must be called before using this method.
+     */
+    static void appendVmSynonymMessage(String vm1, String vm2) {
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.hotspot",
+                vm1, vm2));
+    }
+
+    /**
+     * Appends the vm Ergo message to the header, already created.
+     * initHelpSystem must be called before using this method.
+     */
+    static void appendVmErgoMessage(boolean isServerClass, String vm) {
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.ergo.message1",
+                vm));
+        outBuf = (isServerClass)
+             ? outBuf.append(",\n" +
+                getLocalizedMessage("java.launcher.ergo.message2") + "\n\n")
+             : outBuf.append(".\n\n");
+    }
+
+    /**
+     * Appends the last invariant part to the previously created messages,
+     * and finishes up the printing to the desired output stream.
+     * initHelpSystem must be called before using this method.
+     */
+    static void printHelpMessage(boolean printToStderr) {
+        PrintStream ostream = (printToStderr) ? System.err : System.out;
+        outBuf = outBuf.append(getLocalizedMessage("java.launcher.opt.footer",
+                File.pathSeparator));
+        ostream.println(outBuf.toString());
+    }
+
+    /**
+     * Prints the Xusage text to the desired output stream.
+     */
+    static void printXUsageMessage(boolean printToStderr) {
+        PrintStream ostream =  (printToStderr) ? System.err : System.out;
+        ostream.println(getLocalizedMessage("java.launcher.X.usage",
+                File.pathSeparator));
+    }
+
+    static String getMainClassFromJar(String jarname) throws IOException {
+        JarFile jarFile = null;
+        try {
+            jarFile = new JarFile(jarname);
+            Manifest manifest = jarFile.getManifest();
+            if (manifest == null) {
+                throw new IOException("manifest not found in " + jarname);
+            }
+            Attributes mainAttrs = manifest.getMainAttributes();
+            if (mainAttrs == null) {
+                throw new IOException("no main mainifest attributes, in " +
+                        jarname);
+            }
+            return mainAttrs.getValue(MAIN_CLASS);
+        } finally {
+            if (jarFile != null) {
+                jarFile.close();
+            }
+        }
+    }
+
+    /**
+     * This method does the following:
+     * 1. gets the classname from a Jar's manifest, if necessary
+     * 2. loads the class using the System ClassLoader
+     * 3. ensures the availability and accessibility of the main method,
+     *    using signatureDiagnostic method.
+     *    a. does the class exist
+     *    b. is there a main
+     *    c. is the main public
+     *    d. is the main static
+     *    c. does the main take a String array for args
+     * 4. and off we go......
+     *
+     * @param printToStderr
+     * @param isJar
+     * @param name
+     * @return
+     * @throws java.lang.Exception
+     */
+    public static Object checkAndLoadMain(boolean printToStderr,
+            boolean isJar, String name) throws Exception {
+        // get the class name
+        String classname = (isJar) ? getMainClassFromJar(name) : name;
+        classname = classname.replace('/', '.');
+        ClassLoader loader = ClassLoader.getSystemClassLoader();
+        Class<?> clazz = null;
+        PrintStream ostream = (printToStderr) ? System.err : System.out;
+        try {
+            clazz = loader.loadClass(classname);
+        } catch (ClassNotFoundException cnfe) {
+            ostream.println(getLocalizedMessage("java.launcher.cls.error1", classname));
+            throw new RuntimeException("Could not find the main class " + classname);
+        }
+        signatureDiagnostic(ostream, clazz);
+        return clazz;
+    }
+
+    static void signatureDiagnostic(PrintStream ostream, Class<?> clazz) {
+        String classname = clazz.getName();
+        Method method = null;
+        try {
+            method = clazz.getMethod("main", String[].class);
+        } catch (Exception e) {
+            ostream.println(getLocalizedMessage("java.launcher.cls.error4",
+                    classname));
+            throw new RuntimeException("Main method not found in " + classname);
+        }
+        /*
+         * Usually the getMethod (above) will choose the correct method, based
+         * on its modifiers and parameter types, the only check required is the
+         * getReturnType check as getMethod does not check for this, all the
+         * other modifier tests are redundant, and are simply here for safety.
+         */
+        int mod = method.getModifiers();
+        if (!Modifier.isStatic(mod)) {
+            ostream.println(getLocalizedMessage("java.launcher.cls.error2",
+                    "static", classname));
+            throw new RuntimeException("Main method is not static in class " +
+                    classname);
+        }
+        if (!Modifier.isPublic(mod)) {
+            ostream.println(getLocalizedMessage("java.launcher.cls.error2",
+                    "public", classname));
+            throw new RuntimeException("Main method is not public in class " +
+                    classname);
+        }
+        Class<?> rType = method.getReturnType();
+        if (!rType.isPrimitive() || !rType.getName().equals("void")) {
+            ostream.println(getLocalizedMessage("java.launcher.cls.error3",
+                    classname));
+            throw new RuntimeException("Main method must return a value" +
+                    " of type void in class " +
+                    classname);
+        }
+        return;
+    }
+}
--- a/jdk/src/share/classes/sun/launcher/resources/launcher.properties	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/src/share/classes/sun/launcher/resources/launcher.properties	Wed Sep 24 15:07:41 2008 -0700
@@ -1,5 +1,5 @@
 #
-# Copyright 2007 Sun Microsystems, Inc.  All Rights Reserved.
+# Copyright 2007-2008 Sun Microsystems, Inc.  All Rights Reserved.
 # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 #
 # This code is free software; you can redistribute it and/or modify it
@@ -99,3 +99,18 @@
 \    -Xshare:auto      use shared class data if possible (default)\n\
 \    -Xshare:on        require using shared class data, otherwise fail.\n\n\
 The -X options are non-standard and subject to change without notice.\n
+
+java.launcher.cls.error1=\
+    Error: Could not find main class {0}
+java.launcher.cls.error2=\
+    Error: Main method is not {0} in class {1}, please define the main method as:\n\
+\   public static void main(String[] args)
+java.launcher.cls.error3=\
+    Error: Main method must return a value of type void in class {0}, please \n\
+    define the main method as:\n\
+\   public static void main(String[] args)
+java.launcher.cls.error4=\
+    Error: Main method not found in class {0}, please define the main method as:\n\
+\   public static void main(String[] args)
+
+
--- a/jdk/src/solaris/bin/java_md.c	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/src/solaris/bin/java_md.c	Wed Sep 24 15:07:41 2008 -0700
@@ -1312,3 +1312,24 @@
 {
     JLI_SetTraceLauncher();
 }
+
+/*
+ * The implementation for finding classes from the bootstrap
+ * class loader, refer to java.h
+ */
+static FindClassFromBootLoader_t *findBootClass = NULL;
+
+jclass
+FindBootStrapClass(JNIEnv *env, const char* classname)
+{
+   if (findBootClass == NULL) {
+       findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
+          "JVM_FindClassFromBootLoader");
+       if (findBootClass == NULL) {
+           JLI_ReportErrorMessage(DLL_ERROR4,
+               "JVM_FindClassFromBootLoader");
+           return NULL;
+       }
+   }
+   return findBootClass(env, classname, JNI_FALSE);
+}
--- a/jdk/src/windows/bin/java_md.c	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/src/windows/bin/java_md.c	Wed Sep 24 15:07:41 2008 -0700
@@ -993,9 +993,34 @@
     return rslt;
 }
 
-/* Linux only, empty on windows. */
+/* Unix only, empty on windows. */
 void SetJavaLauncherPlatformProps() {}
 
+/*
+ * The implementation for finding classes from the bootstrap
+ * class loader, refer to java.h
+ */
+static FindClassFromBootLoader_t *findBootClass = NULL;
+
+jclass FindBootStrapClass(JNIEnv *env, const char *classname)
+{
+   HMODULE hJvm;
+
+   if (findBootClass == NULL) {
+       hJvm = GetModuleHandle(JVM_DLL);
+       if (hJvm == NULL) return NULL;
+       /* need to use the demangled entry point */
+       findBootClass = (FindClassFromBootLoader_t *)GetProcAddress(hJvm,
+            "JVM_FindClassFromBootLoader");
+       if (findBootClass == NULL) {
+          JLI_ReportErrorMessage(DLL_ERROR4,
+              "JVM_FindClassBootLoader");
+          return NULL;
+       }
+   }
+   return findBootClass(env, classname, JNI_FALSE);
+}
+
 void
 InitLauncher(boolean javaw)
 {
--- a/jdk/test/tools/launcher/Arrrghs.java	Wed Sep 24 15:19:07 2008 +0200
+++ b/jdk/test/tools/launcher/Arrrghs.java	Wed Sep 24 15:07:41 2008 -0700
@@ -21,57 +21,47 @@
  * have any questions.
  */
 
+/**
+ * @test
+ * @compile  -XDignore.symbol.file Arrrghs.java TestHelper.java
+ * @bug 5030233 6214916 6356475 6571029 6684582
+ * @run main Arrrghs
+ * @summary Argument parsing validation.
+ */
+
 import java.io.BufferedReader;
 import java.io.File;
+import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
 import java.util.Map;
-import java.util.StringTokenizer;
 
 public class Arrrghs {
-
+    private Arrrghs(){}
     /**
+     * This class provides various tests for arguments processing.
      * A group of tests to ensure that arguments are passed correctly to
      * a child java process upon a re-exec, this typically happens when
      * a version other than the one being executed is requested by the user.
      *
      * History: these set of tests  were part of Arrrghs.sh. The MKS shell
-     * implementations are notoriously buggy. Implementing these tests purely
+     * implementations were notoriously buggy. Implementing these tests purely
      * in Java is not only portable but also robust.
      *
      */
 
-    /* Do not instantiate */
-    private Arrrghs() {}
-
-    static String javaCmd;
-
     // The version string to force a re-exec
     final static String VersionStr = "-version:1.1+";
 
     // The Cookie or the pattern we match in the debug output.
     final static String Cookie = "ReExec Args: ";
 
-    private static boolean _debug = Boolean.getBoolean("Arrrghs.Debug");
-    private static boolean isWindows = System.getProperty("os.name", "unknown").startsWith("Windows");
-    private static int exitValue = 0;
-
-    private static void doUsage(String message) {
-        if (message != null) System.out.println("Error: " + message);
-        System.out.println("Usage: Arrrghs path_to_java");
-        System.exit(1);
-    }
-
     /*
      * SIGH, On Windows all strings are quoted, we need to unwrap it
      */
     private static String removeExtraQuotes(String in) {
-        if (isWindows) {
+        if (TestHelper.isWindows) {
             // Trim the string and remove the enclosed quotes if any.
             in = in.trim();
             if (in.startsWith("\"") && in.endsWith("\"")) {
@@ -81,27 +71,29 @@
         return in;
     }
 
-
     /*
      * This method detects the cookie in the output stream of the process.
      */
-    private static boolean detectCookie(InputStream istream, String expectedArguments) throws IOException {
+    private static boolean detectCookie(InputStream istream,
+            String expectedArguments) throws IOException {
         BufferedReader rd = new BufferedReader(new InputStreamReader(istream));
         boolean retval = false;
 
         String in = rd.readLine();
         while (in != null) {
-            if (_debug) System.out.println(in);
+            if (TestHelper.debug) System.out.println(in);
             if (in.startsWith(Cookie)) {
                 String detectedArgument = removeExtraQuotes(in.substring(Cookie.length()));
                 if (expectedArguments.equals(detectedArgument)) {
                     retval = true;
                 } else {
-                    System.out.println("Error: Expected Arguments\t:'" + expectedArguments + "'");
-                    System.out.println(" Detected Arguments\t:'" + detectedArgument + "'");
+                    System.out.println("Error: Expected Arguments\t:'" +
+                            expectedArguments + "'");
+                    System.out.println(" Detected Arguments\t:'" +
+                            detectedArgument + "'");
                 }
                 // Return the value asap if not in debug mode.
-                if (!_debug) {
+                if (!TestHelper.debug) {
                     rd.close();
                     istream.close();
                     return retval;
@@ -112,7 +104,7 @@
         return retval;
     }
 
-    private static boolean doExec0(ProcessBuilder pb, String expectedArguments) {
+    private static boolean doTest0(ProcessBuilder pb, String expectedArguments) {
         boolean retval = false;
         try {
             pb.redirectErrorStream(true);
@@ -131,72 +123,199 @@
      * This method return true  if the expected and detected arguments are the same.
      * Quoting could cause dissimilar testArguments and expected arguments.
      */
-    static boolean doExec(String testArguments, String expectedPattern) {
-        ProcessBuilder pb = new ProcessBuilder(javaCmd, VersionStr, testArguments);
+    static int doTest(String testArguments, String expectedPattern) {
+        ProcessBuilder pb = new ProcessBuilder(TestHelper.javaCmd,
+                VersionStr, testArguments);
 
         Map<String, String> env = pb.environment();
         env.put("_JAVA_LAUNCHER_DEBUG", "true");
-        return doExec0(pb, testArguments);
+        return doTest0(pb, testArguments) ? 0 : 1;
     }
 
     /**
      * A convenience method for identical test pattern and expected arguments
      */
-    static boolean doExec(String testPattern) {
-        return doExec(testPattern, testPattern);
+    static int doTest(String testPattern) {
+        return doTest(testPattern, testPattern);
+    }
+
+    static void quoteParsingTests() {
+        /*
+         * Tests for 6214916
+         * These tests require that a JVM (any JVM) be installed in the system registry.
+         * If none is installed, skip this test.
+         */
+        TestHelper.TestResult tr =
+                TestHelper.doExec(TestHelper.javaCmd, VersionStr, "-version");
+        if (!tr.isOK()) {
+            System.err.println("Warning:Argument Passing Tests were skipped, " +
+                    "no java found in system registry.");
+            return;
+        }
+
+        // Basic test
+        TestHelper.testExitValue += doTest("-a -b -c -d");
+
+        // Basic test with many spaces
+        TestHelper.testExitValue += doTest("-a    -b      -c       -d");
+
+        // Quoted whitespace does matter ?
+        TestHelper.testExitValue += doTest("-a \"\"-b      -c\"\" -d");
+
+
+        // Escaped quotes outside of quotes as literals
+        TestHelper.testExitValue += doTest("-a \\\"-b -c\\\" -d");
+
+        // Check for escaped quotes inside of quotes as literal
+        TestHelper.testExitValue += doTest("-a \"-b \\\"stuff\\\"\" -c -d");
+
+        // A quote preceeded by an odd number of slashes is a literal quote
+        TestHelper.testExitValue += doTest("-a -b\\\\\\\" -c -d");
+
+        // A quote preceeded by an even number of slashes is a literal quote
+        // see 6214916.
+        TestHelper.testExitValue += doTest("-a -b\\\\\\\\\" -c -d");
+
+        // Make sure that whitespace doesn't interfere with the removal of the
+        // appropriate tokens. (space-tab-space preceeds -jre-restict-search).
+        TestHelper.testExitValue += doTest("-a -b  \t -jre-restrict-search -c -d","-a -b -c -d");
+
+        // Make sure that the mJRE tokens being stripped, aren't stripped if
+        // they happen to appear as arguments to the main class.
+        TestHelper.testExitValue += doTest("foo -version:1.1+");
+
+        System.out.println("Completed arguments quoting tests with " +
+                TestHelper.testExitValue + " errors");
+    }
+
+    /*
+     * These tests are usually run on non-existent targets to check error results
+     */
+    static void runBasicErrorMessageTests() {
+        // Tests for 5030233
+        TestHelper.TestResult tr = TestHelper.doExec(TestHelper.javaCmd, "-cp");
+        tr.checkNegative();
+        tr.isNotZeroOutput();
+        System.out.println(tr);
+
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-classpath");
+        tr.checkNegative();
+        tr.isNotZeroOutput();
+        System.out.println(tr);
+
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar");
+        tr.checkNegative();
+        tr.isNotZeroOutput();
+        System.out.println(tr);
+
+        tr = TestHelper.doExec(TestHelper.javacCmd, "-cp");
+        tr.checkNegative();
+        tr.isNotZeroOutput();
+        System.out.println(tr);
+
+        // Test for 6356475 "REGRESSION:"java -X" from cmdline fails"
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-X");
+        tr.checkPositive();
+        tr.isNotZeroOutput();
+        System.out.println(tr);
+
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-help");
+        tr.checkPositive();
+        tr.isNotZeroOutput();
+        System.out.println(tr);
+    }
+
+    /*
+     * A set of tests which tests various dispositions of the main method.
+     */
+    static void runMainMethodTests() throws FileNotFoundException {
+        TestHelper.TestResult tr = null;
+
+        // a missing class
+        TestHelper.createJar(new File("some.jar"), new File("Foo"), (String[])null);
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar");
+        tr.contains("MIA");
+        System.out.println(tr);
+        // use classpath to check
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "MIA");
+        tr.contains("Error: Could not find main class MIA");
+        System.out.println(tr);
+
+        // incorrect method access
+        TestHelper.createJar(new File("some.jar"), new File("Foo"),
+                "private static void main(String[] args){}");
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar");
+        tr.contains("Error: Main method not found in class Foo");
+        System.out.println(tr);
+        // use classpath to check
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo");
+        tr.contains("Error: Main method not found in class Foo");
+        System.out.println(tr);
+
+        // incorrect return type
+        TestHelper.createJar(new File("some.jar"), new File("Foo"),
+                "public static int main(String[] args){return 1;}");
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar");
+        tr.contains("Error: Main method must return a value of type void in class Foo");
+        System.out.println(tr);
+        // use classpath to check
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo");
+        tr.contains("Error: Main method must return a value of type void in class Foo");
+        System.out.println(tr);
+
+        // incorrect parameter type
+        TestHelper.createJar(new File("some.jar"), new File("Foo"),
+                "public static void main(Object[] args){}");
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar");
+        tr.contains("Error: Main method not found in class Foo");
+        System.out.println(tr);
+        // use classpath to check
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo");
+        tr.contains("Error: Main method not found in class Foo");
+        System.out.println(tr);
+
+        // incorrect method type - non-static
+         TestHelper.createJar(new File("some.jar"), new File("Foo"),
+                "public void main(Object[] args){}");
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar");
+        tr.contains("Error: Main method not found in class Foo");
+        System.out.println(tr);
+        // use classpath to check
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo");
+        tr.contains("Error: Main method not found in class Foo");
+        System.out.println(tr);
+
+        // amongst a potpourri of kindred main methods, is the right one chosen ?
+        TestHelper.createJar(new File("some.jar"), new File("Foo"),
+        "void main(Object[] args){}",
+        "int  main(Float[] args){return 1;}",
+        "private void main() {}",
+        "private static void main(int x) {}",
+        "public int main(int argc, String[] argv) {return 1;}",
+        "public static void main(String[] args) {System.out.println(\"THE_CHOSEN_ONE\");}");
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-jar", "some.jar");
+        tr.contains("THE_CHOSEN_ONE");
+        System.out.println(tr);
+        // use classpath to check
+        tr = TestHelper.doExec(TestHelper.javaCmd, "-cp", "some.jar", "Foo");
+        tr.contains("THE_CHOSEN_ONE");
+        System.out.println(tr);
     }
 
     /**
      * @param args the command line arguments
+     * @throws java.io.FileNotFoundException
      */
-    public static void main(String[] args) {
-        if (args.length < 1 && args[0] == null) {
-            doUsage("Invalid number of arguments");
-        }
-
-        javaCmd = args[0];
-
-        if (!new File(javaCmd).canExecute()) {
-            if (isWindows && new File(javaCmd + ".exe").canExecute()) {
-                javaCmd = javaCmd + ".exe";
-            } else {
-                doUsage("The java executable must exist");
-            }
+    public static void main(String[] args) throws FileNotFoundException {
+        if (TestHelper.debug) System.out.println("Starting Arrrghs tests");
+        quoteParsingTests();
+        runBasicErrorMessageTests();
+        runMainMethodTests();
+        if (TestHelper.testExitValue > 0) {
+            System.out.println("Total of " + TestHelper.testExitValue + " failed");
+            System.exit(1);
+        } else {
+            System.out.println("All tests pass");
         }
-
-        if (_debug) System.out.println("Starting Arrrghs tests");
-        // Basic test
-        if (!doExec("-a -b -c -d")) exitValue++;
-
-        // Basic test with many spaces
-        if (!doExec("-a    -b      -c       -d")) exitValue++;
-
-        // Quoted whitespace does matter ?
-        if (!doExec("-a \"\"-b      -c\"\" -d")) exitValue++;
-
-        // Escaped quotes outside of quotes as literals
-        if (!doExec("-a \\\"-b -c\\\" -d")) exitValue++;
-
-        // Check for escaped quotes inside of quotes as literal
-        if (!doExec("-a \"-b \\\"stuff\\\"\" -c -d")) exitValue++;
-
-        // A quote preceeded by an odd number of slashes is a literal quote
-        if (!doExec("-a -b\\\\\\\" -c -d")) exitValue++;
-
-        // A quote preceeded by an even number of slashes is a literal quote
-        // see 6214916.
-        if (!doExec("-a -b\\\\\\\\\" -c -d")) exitValue++;
-
-        // Make sure that whitespace doesn't interfere with the removal of the
-        // appropriate tokens. (space-tab-space preceeds -jre-restict-search).
-        if (!doExec("-a -b  \t -jre-restrict-search -c -d","-a -b -c -d")) exitValue++;
-
-        // Make sure that the mJRE tokens being stripped, aren't stripped if
-        // they happen to appear as arguments to the main class.
-        if (!doExec("foo -version:1.1+")) exitValue++;
-
-        System.out.println("Completed Arrrghs arguments quoting/matching tests with " + exitValue + " errors");
-        System.exit(exitValue);
     }
-
 }
--- a/jdk/test/tools/launcher/Arrrghs.sh	Wed Sep 24 15:19:07 2008 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,176 +0,0 @@
-#!/bin/sh
-# @test Arrrghs.sh
-# @bug 5030233 6214916 6356475 6571029 6684582
-# @build Arrrghs
-# @run shell Arrrghs.sh
-# @summary Argument parsing validation.
-# @author Joseph E. Kowalski
-
-#
-# Copyright 2004-2008 Sun Microsystems, Inc.  All Rights Reserved.
-# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
-#
-# This code is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License version 2 only, as
-# published by the Free Software Foundation.
-#
-# This code is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
-# version 2 for more details (a copy is included in the LICENSE file that
-# accompanied this code).
-#
-# You should have received a copy of the GNU General Public License version
-# 2 along with this work; if not, write to the Free Software Foundation,
-# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
-# CA 95054 USA or visit www.sun.com if you need additional information or
-# have any questions.
-#
-
-#
-# This test is intended to validate generic argument parsing and
-# handling.
-#
-# Oh yes, since the response to argument parsing errors is often
-# a visceral one, the name Arrrghs (pronounced "args") seems rather
-# appropriate.
-#
-
-# Verify directory context variables are set
-if [ "${TESTJAVA}" = "" ]
-then
-  echo "TESTJAVA not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-if [ "${TESTSRC}" = "" ]
-then
-  echo "TESTSRC not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-if [ "${TESTCLASSES}" = "" ]
-then
-  echo "TESTCLASSES not set.  Test cannot execute.  Failed."
-  exit 1
-fi
-
-#
-# Shell routine to test for the proper handling of the cp/classpath 
-# option is correct (see 5030233).  This option is unique in that it
-# is the only option to the java command (and friends) which is
-# separated from its option argument by a space, rather than an
-# equals sign.
-#
-# Parameters:
-#	$1	cmd	utility name to be tested (java, javac, ...)
-#	$2	option	either the -cp or -classpath option to be
-#			tested.
-#
-TestCP() {
-	mess="`$TESTJAVA/bin/$1 $2 2>&1 1>/dev/null`"
-	if [ $? -eq 0 ]; then
-		echo "Invalid $1 $2 syntax accepted"
-		exit 1
-	fi
-	if [ -z "$mess" ]; then
-		echo "No Usage message from invalid $1 $2 syntax"
-		exit 1
-	fi
-}
-
-#
-# Test for 6356475 "REGRESSION:"java -X" from cmdline fails"
-#
-TestXUsage() {
-	$TESTJAVA/bin/java -X > /dev/null 2>&1
-	if [ $? -ne 0 ]; then
-		echo "-X option failed"
-		exit 1
-	fi
-}
-
-#
-# Test if java -help works
-#
-TestHelp() {
-	$TESTJAVA/bin/java -help > /dev/null 2>&1
-	if [ $? -ne 0 ]; then
-		echo "-help option failed"
-		exit 1
-	fi
-}
-
-#
-# Test to ensure that a missing main class is indicated in the error message
-#
-TestMissingMainClass() {
-	# First create a small jar file with no main
-        printf "public class Foo {}\n" > Foo.java
-	$TESTJAVA/bin/javac Foo.java
-	if [ $? -ne 0 ]; then
-		printf "Error: compilation of Foo.java failed\n" 
- 		exit 1
-	fi
-	printf "Main-Class: Bar\n" > manifest
-	$TESTJAVA/bin/jar -cvfm some.jar manifest Foo.class
-	if [ ! -f some.jar ]; then
-		printf "Error: did not find some.jar\n" 
- 		exit 1
-	fi
-
-	# test a non-existence main-class using -jar 
-	mess="`$TESTJAVA/bin/java -jar some.jar 2>&1 1>/dev/null`"
-	echo $mess | grep 'Bar' 2>&1 > /dev/null
-	if [ $? -ne 0 ]; then
-		printf "Error: did not find main class missing message\n"
-		exit 1
-	fi
-
-	# test a non-existent main-class using classpath
-	mess="`$TESTJAVA/bin/java -cp some.jar Bar 2>&1 1>/dev/null`"
-	echo $mess | grep 'Bar' 2>&1 > /dev/null
-	if [ $? -ne 0 ]; then
-		printf "Error: did not find main class missing message\n"
-		exit 1
-	fi
-
-	# cleanup
-	rm -f some.jar Foo.* manifest
-}
-
-#
-# Main processing:
-#
-
-#
-# Tests for 5030233
-#
-TestCP java -cp
-TestCP java -classpath
-TestCP java -jar
-TestCP javac -cp
-TestCP javac -classpath
-TestXUsage
-TestHelp
-TestMissingMainClass
-
-#
-# Tests for 6214916
-#
-#
-# These tests require that a JVM (any JVM) be installed in the system registry.
-# If none is installed, skip this test.
-$TESTJAVA/bin/java -version:1.1+ -version >/dev/null 2>&1
-if [ $? -eq 0 ]; then
-   $TESTJAVA/bin/java -classpath $TESTCLASSES Arrrghs $TESTJAVA/bin/java
-   if [ $? -ne 0 ]; then
-      echo "Argument Passing Tests failed"
-      exit 1
-   fi
-else
-   printf "Warning:Argument Passing Tests were skipped, no java found in system registry."
-fi
-exit 0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/tools/launcher/TestHelper.java	Wed Sep 24 15:07:41 2008 -0700
@@ -0,0 +1,228 @@
+
+/*
+ * Copyright 2008 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+import javax.tools.ToolProvider;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import javax.tools.JavaCompiler;
+
+/**
+ * This class provides some common utilites for the launcher tests.
+ */
+public enum TestHelper {
+    INSTANCE;
+    static final String JAVAHOME = System.getProperty("java.home", ".");
+    static final boolean isSDK = JAVAHOME.endsWith("jre");
+    static final String javaCmd;
+    static final String javacCmd;
+    static final JavaCompiler compiler;
+
+    static final boolean debug = Boolean.getBoolean("Arrrghs.Debug");
+    static final boolean isWindows =
+            System.getProperty("os.name", "unknown").startsWith("Windows");
+    static int testExitValue = 0;
+
+    static {
+        compiler = ToolProvider.getSystemJavaCompiler();
+        File binDir = (isSDK) ? new File((new File(JAVAHOME)).getParentFile(), "bin")
+            : new File(JAVAHOME, "bin");
+        File javaCmdFile = (isWindows)
+                ? new File(binDir, "java.exe")
+                : new File(binDir, "java");
+        javaCmd = javaCmdFile.getAbsolutePath();
+        if (!javaCmdFile.canExecute()) {
+            throw new RuntimeException("java <" + TestHelper.javaCmd + "> must exist");
+        }
+
+        File javacCmdFile = (isWindows)
+                ? new File(binDir, "javac.exe")
+                : new File(binDir, "javac");
+        javacCmd = javacCmdFile.getAbsolutePath();
+        if (!javacCmdFile.canExecute()) {
+            throw new RuntimeException("java <" + javacCmd + "> must exist");
+        }
+    }
+
+    /*
+     * A generic jar file creator which creates the java file, compiles it
+     * and jar's it up for use.
+     */
+    static void createJar(File jarName, File mainClass, String... mainDefs)
+            throws FileNotFoundException {
+            createJar(null, jarName, mainClass, mainDefs);
+    }
+
+    /*
+     * A method which takes manifest entry to specify a specific manifest
+     * Main-Class name.
+     */
+    static void createJar(String mEntry, File jarName, File mainClass, String... mainDefs)
+            throws FileNotFoundException {
+        if (jarName.exists()) {
+            jarName.delete();
+        }
+        PrintStream ps = new PrintStream(new FileOutputStream(mainClass + ".java"));
+        ps.println("public class Foo {");
+        if (mainDefs != null) {
+            for (String x : mainDefs) {
+                ps.println(x);
+            }
+        }
+        ps.println("}");
+        ps.close();
+
+        String compileArgs[] = {
+            mainClass + ".java"
+        };
+        if (compiler.run(null, null, null, compileArgs) != 0) {
+            throw new RuntimeException("compilation failed " + mainClass + ".java");
+        }
+
+        if (mEntry == null && mainDefs == null) {
+            mEntry = "MIA";
+        } else {
+            mEntry = mainClass.getName();
+        }
+        String jarArgs[] = {
+            (debug) ? "cvfe" : "cfe",
+            jarName.getAbsolutePath(),
+            mEntry,
+            mainClass.getName() + ".class"
+        };
+        sun.tools.jar.Main jarTool =
+                new sun.tools.jar.Main(System.out, System.err, "JarCreator");
+        if (!jarTool.run(jarArgs)) {
+            throw new RuntimeException("jar creation failed " + jarName);
+        }
+    }
+
+    /*
+     * A method which executes a java cmd and returs the results in a container
+     */
+    static TestResult doExec(String...cmds) {
+        String cmdStr = "";
+        for (String x : cmds) {
+            cmdStr = cmdStr.concat(x + " ");
+        }
+        ProcessBuilder pb = new ProcessBuilder(cmds);
+        Map<String, String> env = pb.environment();
+        BufferedReader rdr = null;
+        try {
+            List<String> outputList = new ArrayList<String>();
+            pb.redirectErrorStream(true);
+            Process p = pb.start();
+            rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
+            String in = rdr.readLine();
+            while (in != null) {
+                outputList.add(in);
+                in = rdr.readLine();
+            }
+            p.waitFor();
+            p.destroy();
+            return new TestHelper.TestResult(cmdStr, p.exitValue(), outputList);
+        } catch (Exception ex) {
+            ex.printStackTrace();
+            throw new RuntimeException(ex.getMessage());
+        }
+    }
+
+    /*
+     * A class to encapsulate the test results and stuff, with some ease
+     * of use methods to check the test results.
+     */
+    static class TestResult {
+        StringBuilder status;
+        int exitValue;
+        List<String> testOutput;
+
+        public TestResult(String str, int rv, List<String> oList) {
+            status = new StringBuilder(str);
+            exitValue = rv;
+            testOutput = oList;
+        }
+
+        void checkNegative() {
+            if (exitValue == 0) {
+                status = status.append("  Error: test must not return 0 exit value");
+                testExitValue++;
+            }
+        }
+
+        void checkPositive() {
+            if (exitValue != 0) {
+                status = status.append("  Error: test did not return 0 exit value");
+                testExitValue++;
+            }
+        }
+
+        boolean isOK() {
+            return exitValue == 0;
+        }
+
+        boolean isZeroOutput() {
+            if (!testOutput.isEmpty()) {
+                status = status.append("  Error: No message from cmd please");
+                testExitValue++;
+                return false;
+            }
+            return true;
+        }
+
+        boolean isNotZeroOutput() {
+            if (testOutput.isEmpty()) {
+                status = status.append("  Error: Missing message");
+                testExitValue++;
+                return false;
+            }
+            return true;
+        }
+
+        public String toString() {
+            if (debug) {
+                for (String x : testOutput) {
+                    status = status.append(x + "\n");
+                }
+            }
+            return status.toString();
+        }
+
+        boolean contains(String str) {
+            for (String x : testOutput) {
+                if (x.contains(str)) {
+                    return true;
+                }
+            }
+            status = status.append("   Error: string <" + str + "> not found ");
+            testExitValue++;
+            return false;
+        }
+    }
+}