changeset 4070:b8d829dbef43

RT-31144 Fix problems with passing jvmuserargs to packaged application [dcherepanov]
author mhowe
date Fri, 21 Jun 2013 17:43:06 -0700
parents b1d681005b05
children dcb53a5d04ea
files deploy/packager/native/linux/launcher.c deploy/packager/native/macosx/main.m deploy/packager/native/windows/WinLauncher.cpp deploy/packager/src/com/sun/javafx/tools/packager/bundlers/LinuxAppBundler.java deploy/packager/src/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java
diffstat 5 files changed, 394 insertions(+), 236 deletions(-) [+]
line wrap: on
line diff
--- a/deploy/packager/native/linux/launcher.c	Wed Jun 19 13:57:16 2013 -0700
+++ b/deploy/packager/native/linux/launcher.c	Fri Jun 21 17:43:06 2013 -0700
@@ -276,63 +276,102 @@
     JVMUserArg *args;
     int maxSize;
     int currentSize;
-    int initialElements;
+    int initialElements;           
 } JVMUserArgs;
 
 /**
  * Creates an array of string pointer where each non null entry is malloced and
  * needs to be freed
- *
+ * 
  * @param basedir
  * @param keys
  * @param size
  */
 void JVMUserArgs_initializeDefaults(JVMUserArgs *this, char* basedir) {
-    //void getArray(char* basedir, char** keys, int maxSize, int *actualSize) {
-    char jvmArgID[40];
-    char argvalue[MAX_ARGUMENT_LEN];
-    char name[MAX_ARGUMENT_LEN];
-    char value[MAX_ARGUMENT_LEN];
+    char jvmArgID[40 + 1];
+    char argvalue[MAX_ARGUMENT_LEN + 1] = {0};
     JVMUserArg* keys = this->args;
-
+    
     int index = 0;
     int found = 0;
     do {
-        sprintf(jvmArgID, "jvmuserarg.%d", (index + 1));
-        found = getConfigValue(basedir, jvmArgID, argvalue, sizeof (argvalue));
+        snprintf(jvmArgID, 40, "jvmuserarg.%d.name", (index+1));
+        found = getConfigValue(basedir, jvmArgID, argvalue, MAX_ARGUMENT_LEN);
         if (found) {
-            index++;
-            if (splitOptionIntoNameValue(argvalue, name, sizeof (name), value, sizeof (value))) {
-                keys->name = strdup(name);
-                keys->value = strdup(value);
-                keys++;
-                this->initialElements++;
-                this->currentSize++;
-            } else {
-                printf("Failed to split jvmuserarg \"%s\" into name, value pair\n", argvalue);
-            }
-        }
-    } while (found && index <= this->maxSize);
+          keys->name = strdup(argvalue);
+          snprintf(jvmArgID, 40, "jvmuserarg.%d.value", (index+1));
+          found = getConfigValue(basedir, jvmArgID, argvalue, MAX_ARGUMENT_LEN);
+          if (found) {
+              //allow use to specify everything in name only
+              keys->value = strdup(argvalue);
+          }
+          else {
+              keys->value = strdup("");
+          }
+          index++;
+          keys++;
+          this->initialElements++;
+          this->currentSize++;
+       }
+    } while (found && index < this->maxSize);
 }
 
+int makeDirRecursively(char *path, mode_t mode) {
+    char parent[MAX_PATH], *p;
+
+    if (fileExists(path)) {
+        return 0;
+    }
+    
+    /* make a parent directory path */
+    strncpy(parent, path, sizeof (parent));
+    parent[sizeof (parent) - 1] = '\0';
+
+    for (p = parent + strlen(parent); *p != '/' && p != parent; p--) {
+    }
+    *p = '\0';
+
+    /* try make parent directory */
+    if (p != parent && makeDirRecursively(parent, mode) != 0) {
+        return -1;
+    }
+
+    //If we got here the parent has already been made so make this one
+    //or if it already exists that is ok as well
+    if (mkdir(path, mode) == 0 || errno == EEXIST) {
+        return 0;
+    }
+    return -1;
+}
+
+
 /*
- * Assumes that userPref can hold the size of this path
+ * Assumes that userPref can hold the size of this path 
  */
 int getUserPrefFile(char* userPref, char* appid) {
     userPref[0] = 0;
     struct passwd *pw = getpwuid(getuid());
-    const char *homedir = pw->pw_dir;
-
+    const char *homedir = pw->pw_dir;    
+    
     strcat(userPref, homedir);
     strcat(userPref, "/.java/.userPrefs/");
     strcat(userPref, appid);
-    strcat(userPref, "/JVMOptions");
+    strcat(userPref, "/JVMUserOptions");
+    if (fileExists(userPref) == FALSE) {
+        makeDirRecursively(userPref,  0777);
+    }
+    
     strcat(userPref, "/prefs.xml");
     return fileExists(userPref);
-}
+}       
 
 void addModifyArgs(JVMUserArgs* this, char* name, char* value) {
     int i;
+    
+    if (name == NULL || value == NULL) {
+        return;
+    }
+    
     JVMUserArg* arg = this->args;
     for (i = 0; i < this->initialElements; i++) {
         if (strcmp(arg[i].name, name) == 0) {
@@ -342,8 +381,8 @@
             return; //Replaced
         }
     }
-
-    //Add new jvm arg from name value
+    
+    //Add new jvm arg from name value 
     int newIndex = this->currentSize;
     arg[newIndex].name = malloc((strlen(name) + 1) * sizeof (char));
     strcpy(arg[newIndex].name, name);
@@ -353,17 +392,17 @@
 }
 
 void findAndModifyNode(XMLNode* node, JVMUserArgs* args) {
-    XMLNode* keyNode = NULL;
-    char* key;
-    char* value;
-    keyNode = FindXMLChild(node->_sub, "entry");
-
+        XMLNode* keyNode = NULL;
+        char* key;
+        char* value;
+        keyNode = FindXMLChild(node->_sub, "entry");
+        
     while (keyNode != NULL && args->currentSize < args->maxSize) {
-        key = FindXMLAttribute(keyNode->_attributes, "key");
-        value = FindXMLAttribute(keyNode->_attributes, "value");
-        addModifyArgs(args, key, value);
-        keyNode = keyNode->_next;
-    }
+            key = FindXMLAttribute(keyNode->_attributes, "key");
+            value = FindXMLAttribute(keyNode->_attributes, "value");
+            addModifyArgs(args, key, value);
+            keyNode = keyNode->_next;
+        }
 }
 
 int getJvmUserArgs(JVMUserArgs* args, char* userPrefsPath) {
@@ -373,7 +412,7 @@
         //scan file for the key
         fp = fopen(userPrefsPath, "r");
         if (fp == NULL) {
-            return;
+             return;
         }
 
         fseek(fp, 0, SEEK_END);
@@ -383,7 +422,7 @@
         fread(buf, fsize, 1, fp);
         fclose(fp);
         buf[fsize] = 0;
-
+        
         XMLNode* node = NULL;
         XMLNode* doc = ParseXMLDocument(buf);
         if (doc != NULL) {
@@ -394,64 +433,41 @@
         }
         free(buf);
     }
-    return args->currentSize;
-}
-
-//Assumes are values passed in are big enough
-
-int splitOptionIntoNameValue(char* argValue,
-        char* optionName,
-        int nameSize,
-        char* optionValue,
-        int valueSize) {
-    char* ptr;
-
-    ptr = strstr(argValue, "##");
-    if (ptr != NULL) {
-        int len = strlen(ptr);
-        if (len > 0 && len < valueSize) {
-            ptr++;
-            ptr++;
-            strcpy(optionValue, ptr);
-
-            int argLen = strlen(argValue);
-            if ((argLen - len + 1) < nameSize) {
-                memcpy(optionName, argValue, argLen - len);
-                optionName[argLen - len] = '\0';
-                return TRUE;
-            }
-        }
-    }
-    return FALSE;
+    return args->currentSize; 
 }
 
 /*
  * Converts JVMUserArg to a single jvm argument.
- *
+ * 
  * This is used to convert to the actual string passed into the jvm, so has
- * option to free memory of the sub strings
- *
+ * option to free memory of the sub strings 
+ * 
  * Returned string can be freed
  */
-char* JVMUserArg_toString(JVMUserArg arg, int freeMemory) {
+char* JVMUserArg_toString(char* basedir, JVMUserArg arg, int freeMemory) {
     int len = strlen(arg.name);
     len += strlen(arg.value);
     char* newString = calloc(len + 1, sizeof (char));
-    strcat(newString, arg.name);
-    strcat(newString, arg.value);
-    if (freeMemory == TRUE) {
-        free(arg.name);
-        free(arg.value);
+    if (newString != NULL) {
+        strcat(newString, arg.name);
+        strcat(newString, arg.value);
+        if (freeMemory == TRUE) {
+            free(arg.name);
+            free(arg.value);
+        }
+        char* jvmOption = dupAndReplacePattern(newString, "$APPDIR", basedir);
+        free(newString);
+        return jvmOption;
     }
-    return newString;
+    return NULL;
 }
 
 int addUserOptions(char* basedir, JavaVMOption* options, int cnt) {
     JVMUserArg args[MAX_OPTIONS - cnt];
     JVMUserArgs jvmUserArgs;
-    char appid[MAX_ARGUMENT_LEN] = {0};
-    char userPref[MAX_PATH] = {0};
-    char argvalue[MAX_ARGUMENT_LEN];
+    char appid[MAX_ARGUMENT_LEN + 1] = {0};
+    char userPref[MAX_ARGUMENT_LEN + 1] = {0};
+    char argvalue[MAX_ARGUMENT_LEN + 1] = {0};
 
 
     jvmUserArgs.args = args;
@@ -460,23 +476,46 @@
     jvmUserArgs.maxSize = MAX_OPTIONS - cnt;
     memset(&args, 0, sizeof (JVMUserArg)*(MAX_OPTIONS - cnt + 1));
 
-    JVMUserArgs_initializeDefaults(&jvmUserArgs, basedir);
-
     //Add property to command line for preferences id
-    if (getConfigValue(basedir, CONFIG_APP_ID_KEY, appid, sizeof (appid))) {
-        sprintf(argvalue, "-D%s=%s", CONFIG_APP_ID_KEY, appid);
+    if (getConfigValue(basedir, CONFIG_APP_ID_KEY, appid, MAX_ARGUMENT_LEN)) {
+        snprintf(argvalue, MAX_ARGUMENT_LEN, "-D%s=%s", CONFIG_APP_ID_KEY, appid);
         options[cnt].optionString = strdup(argvalue);
         cnt++;
 
+        JVMUserArgs_initializeDefaults(&jvmUserArgs, basedir);
         if (getUserPrefFile(userPref, appid)) {
             getJvmUserArgs(&jvmUserArgs, userPref);
-            int i;
-            for (i = 0; i < jvmUserArgs.currentSize; i++) {
-                options[cnt].optionString = JVMUserArg_toString(args[i], TRUE);
+        }
+        else {
+            //If file doesn't exist create it and populate with the default values
+            printf("MESSAGE: Creating user preferences file exist: %s", userPref);
+            FILE *fp = fopen(userPref, "w");
+            if (fp == NULL) {
+                printf("MESSAGE: Can not create user preferences: %s", userPref);
+            }
+            else {
+                fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n");
+                fprintf(fp, "<!DOCTYPE map SYSTEM \"http://java.sun.com/dtd/preferences.dtd\">\n");
+                fprintf(fp, "<map MAP_XML_VERSION=\"1.0\">\n");   
+                int i;
+                for (i; i < jvmUserArgs.currentSize; i++) {
+                    fprintf(fp, "    <entry key=\"%s\" value=\"%s\"/>\n", 
+                            jvmUserArgs.args[i].name,
+                            jvmUserArgs.args[i].value);
+                }
+                fprintf(fp, "</map>\n");
+            }
+            fclose(fp);
+        }
+
+        //Copy all user args to options
+        int i;
+        for (i = 0; i < jvmUserArgs.currentSize; i++) {
+            char* jvmOption = JVMUserArg_toString(basedir, args[i], TRUE);
+            if (jvmOption != NULL) {
+                options[cnt].optionString = jvmOption;
                 cnt++;
             }
-        } else {
-            printf("MESSAGE: User preferences file doesn't exist: %s", userPref);
         }
     } else {
         printf("WARNING: %s not defined:", CONFIG_APP_ID_KEY);
@@ -494,13 +533,12 @@
     JNIEnv* env;
     JavaVM* jvm = NULL;
     char classpath[MAX_PATH*2] = {0};
-    size_t outlen = 0;
     jclass cls;
     jmethodID mid;
     char argname[20];
-    char argvalue[1000];
+    char argvalue[MAX_ARGUMENT_LEN];
     char mainclass[MAX_PATH];
-
+    
     memset(&options, 0, sizeof(JavaVMOption)*(MAX_OPTIONS + 1));
     memset(&jvmArgs, 0, sizeof(JavaVMInitArgs));
 
@@ -554,7 +592,7 @@
             strcat(classpath, argvalue);
         }
     }
-
+    
     // Set up the VM init args
     jvmArgs.version = JNI_VERSION_1_2;
 
@@ -576,14 +614,16 @@
        if (found) {
           //jvmOption is always a new string via strdup
           char* jvmOption = dupAndReplacePattern(argvalue, "$APPDIR", basedir);
-          options[cnt].optionString = jvmOption;
+          if (jvmOption != NULL) {
+            options[cnt].optionString = jvmOption;
+            cnt++;
+          }
           idx++;
-          cnt++;
        }
     } while (found && cnt < MAX_OPTIONS);
-
+    
     cnt = addUserOptions(basedir, options, cnt);
-
+    
     jvmArgs.version = 0x00010002;
     jvmArgs.options = options;
     jvmArgs.nOptions = cnt;
@@ -606,7 +646,7 @@
         printf("Packaging error: no main class specified.\n");
         return FALSE;
     }
-
+    
     cls = (*env)->FindClass(env, mainclass);
     if (cls != NULL) {
         mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
@@ -689,3 +729,4 @@
 
     return 1;
 }
+
--- a/deploy/packager/native/macosx/main.m	Wed Jun 19 13:57:16 2013 -0700
+++ b/deploy/packager/native/macosx/main.m	Fri Jun 21 17:43:06 2013 -0700
@@ -317,7 +317,9 @@
         else {
             newOption = [key stringByAppendingString:[defaultOverrides valueForKey:key]];
         }
-        [expandedOptions addObject: newOption];
+        NSString *expandedOption =
+                [newOption stringByReplacingOccurrencesOfString:@"$APPDIR" withString:contentsPath];
+        [expandedOptions addObject: expandedOption];
     }
 
     //Loop through all user override keys again looking for ones we haven't already uses
@@ -325,7 +327,9 @@
         //If the default object for key is nil, this is an option the user added so include
         if ([defaultOverrides objectForKey: key] == nil) {
             NSString *newOption = [key stringByAppendingString:[userOverrides valueForKey:key]];
-            [expandedOptions addObject: newOption];
+            NSString *expandedOption =
+                    [newOption stringByReplacingOccurrencesOfString:@"$APPDIR" withString:contentsPath];
+            [expandedOptions addObject: expandedOption];
         }
     }
 
--- a/deploy/packager/native/windows/WinLauncher.cpp	Wed Jun 19 13:57:16 2013 -0700
+++ b/deploy/packager/native/windows/WinLauncher.cpp	Fri Jun 21 17:43:06 2013 -0700
@@ -29,9 +29,7 @@
 #define _WIN32_WINNT 0x0501
 
 #include <Windows.h>
-
 #include <shellapi.h> //DO NOT REMOVE, necessary for Gradle builds
-
 #include <tchar.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -131,7 +129,7 @@
 #define CONFIG_MAINJAR_KEY    _T("app.mainjar")
 #define CONFIG_MAINCLASS_KEY  _T("app.mainclass")
 #define CONFIG_CLASSPATH_KEY  _T("app.classpath")
-#define CONFIG_APP_ID_KEY     _T("app.preferences.id");
+#define CONFIG_APP_ID_KEY     _T("app.preferences.id")
 
 //remove trailing end of line character
 //modifies buffer in place
@@ -311,12 +309,12 @@
  *         pattern with the replaceWith string
  */
 TCHAR *replaceStr(TCHAR *str, TCHAR *pattern, TCHAR *replaceWith) {
-	TCHAR buffer[MAX_PATH*2] = {0};
+    TCHAR buffer[MAX_PATH*2] = {0};
     TCHAR *p;
 
     //Return orig if str is not in orig.
     if(!(p = wcsstr(str, pattern))) {
-		return wcsdup(str);
+        return wcsdup(str);
     }
 
     int loc = p-str;
@@ -325,7 +323,7 @@
     }
 
     wcsncpy(buffer, str, loc); // Copy characters from 'str' start to 'orig' st$
-    buffer[loc] = '\0';
+    buffer[loc] = 0x0000;
 
     int remaingBufferSize = sizeof(buffer) - loc;
     int len = _snwprintf(buffer+(loc), remaingBufferSize, _T("%s%s"), replaceWith, p + wcslen(pattern));
@@ -336,80 +334,117 @@
 }
 
 
-//Assumes are values passed in are big enough
 
-int splitOptionIntoNameValue(TCHAR* argValue, TCHAR* optionName, int nameSize,
-        TCHAR* optionValue, int valueSize) {
-    TCHAR* ptr;
+#define MAX_OPTIONS 100
+#define MAX_ARGUMENT_LEN 1000
+#define MAX_OPTION_NAME 50
 
-    ptr = wcsstr(argValue, _T("##"));
-    if (ptr != NULL) {
-        int len = wcslen(ptr);
-        if (len > 0 && len < valueSize) {
-            ptr++;
-            ptr++;
-            wcscpy(optionValue, ptr);
+typedef struct {
+    TCHAR* name;
+    TCHAR* value;
+} JVMUserArg;
 
-            int argLen = wcslen(argValue);
-            if ((argLen - len + 1) < nameSize) {
-                wmemcpy(optionName, argValue, argLen - len); //Does this work correctly for wide chars
-                optionName[argLen - len] = '\0';
-                return TRUE;
+typedef struct {
+    JVMUserArg *args;
+    int maxSize;
+    int currentSize;
+    int initialElements;
+} JVMUserArgs;
+
+/**
+ * Creates an array of string pointer where each non null entry is malloced and
+ * needs to be freed
+ *
+ *
+ * @param basedir*
+ * @param JVMUserArgs* initialize JVMUserArgs with values from developer config file
+ */
+void JVMUserArgs_initializeDefaults(JVMUserArgs *userArgs, TCHAR* basedir) {
+    TCHAR jvmArgID[MAX_OPTION_NAME + 1] = {0};
+    TCHAR argvalue[LAUNCHER_MAXPATH + 1] ={0};
+    JVMUserArg* keys = userArgs->args;
+
+    int index = 0;
+    int found = 0;
+    do {
+        _stprintf_s(jvmArgID, MAX_OPTION_NAME, _T("jvmuserarg.%d.name"), (index+1));
+        found = getConfigValue(basedir, jvmArgID, argvalue, LAUNCHER_MAXPATH);
+        if (found) {
+            keys->name = wcsdup(argvalue);
+            _stprintf_s(jvmArgID, MAX_OPTION_NAME, _T("jvmuserarg.%d.value"), (index+1));
+            found = getConfigValue(basedir, jvmArgID, argvalue, LAUNCHER_MAXPATH);
+            if (found) {
+                //allow use to specify everything in name only
+                keys->value = wcsdup(argvalue);
             }
+            else {
+                keys->value = wcsdup(_T(""));
+            }
+            index++;
+            keys++;
+            userArgs->initialElements++;
+            userArgs->currentSize++;
+        }
+    } while (found && index < userArgs->maxSize);
+}
+
+/*
+ * Always returns a duped *char pointer - use only when assigned to jvm options
+ */
+char* convertToDupedChar(TCHAR* source) {
+    size_t outlen = 0;
+    CHAR  argvalueASCII[LAUNCHER_MAXPATH] = {0};
+
+    if (wcstombs_s(&outlen, argvalueASCII, sizeof (argvalueASCII), source, wcslen(source) + 1) != 0) {
+        showError(_T("Failed converting JVM Argument to ASCII"), source);
+        return NULL;
+    }
+    return strdup(argvalueASCII);
+}
+
+/* 
+ * Concatenate the JVMUserArg into a single string
+ */
+char* JVMUserArg_toString(TCHAR* basedir, JVMUserArg arg, int freeMemory) {
+    int len = wcslen(arg.name);
+    len += wcslen(arg.value);
+    TCHAR* newString = (TCHAR*) calloc(len + 1, sizeof (TCHAR));
+    if (newString != NULL) {
+        wcscat(newString, arg.name);
+        wcscat(newString, arg.value);
+        if (freeMemory == TRUE) {
+            free(arg.name);
+            free(arg.value);
+        }
+        TCHAR* option = replaceStr(newString, _T("$APPDIR"), basedir);
+        if (option != NULL) {
+            return convertToDupedChar(option);
         }
     }
-    return FALSE;
+    return NULL;
 }
 
+
 #define MAX_KEY_LENGTH  80
 #define MAX_VALUE_LENGTH  8192
 
-TCHAR* convertIdToPath(TCHAR* id) {
-    int len = (wcslen(id) + 1) * sizeof (TCHAR);
-    TCHAR* path = (TCHAR*) calloc(len, sizeof (TCHAR));
-    *path = '\0';
-    TCHAR *returnValue = path;
-
-    TCHAR ch = *id;
-    int index = 0;
-    while (ch != 0) {
-        if (ch == '.') {
-            *path = '\\';
-        } else {
-            *path = ch;
-        }
-        id++;
-        ch = *id;
-        path++;
-    }
-    *path = '\0';
-    return returnValue;
-}
-
-//assume regKey big enough to hold largest key
-
-LONG createRegKey(TCHAR* appid, HKEY *hKey) {
-    TCHAR buf[MAX_PATH] = {0};
-    wcscat(buf, _T("SOFTWARE\\JavaSoft\\Prefs\\"));
-    TCHAR* path = convertIdToPath(appid);
-    wcscat(buf, path);
-    wcscat(buf, _T("\\JVMOptions"));
-    free(path);
-    LONG success = RegOpenKeyEx(HKEY_CURRENT_USER, buf, 0, KEY_READ | KEY_WOW64_64KEY, hKey);
-    return success;
-}
-
+/*
+ * Java Preferences API encodes it's strings, so we need to match to make work with Java
+ * CAVEAT: Java also does unicode encoding which this doesn't do yet. Should be sufficient for jvm args.
+ * See WindowsPreferences.java toWindowsName()
+ */
 TCHAR* convertKeyToWinReg(TCHAR* key) {
     TCHAR* windowsName = (TCHAR*) calloc((wcslen(key) + 1)*2, sizeof (TCHAR)); //All caps could double size
-    *windowsName = '\0';
     TCHAR *returnValue = windowsName;
 
     TCHAR ch = *key;
-    int index = 0;
+    //Not handling UNICODE
     while (ch != 0) {
         if (ch == '\\') {
-            *windowsName = '//';
-        } else if (ch == '/') {
+            *windowsName++ = '/';
+            *windowsName = '/';
+        }
+        else if (ch == '/') {
             *windowsName = '\\';
         } else if ((ch >= 'A') && (ch <= 'Z')) {
             *windowsName++ = '/';
@@ -421,48 +456,150 @@
         ch = *key;
         windowsName++;
     }
-    *windowsName = '\0';
+    *windowsName = 0x0000;
     return returnValue;
 }
 
-TCHAR* getJvmUserArg(TCHAR* appid, TCHAR* argvalue) {
-    TCHAR optionName[MAX_PATH] = {0};
-    TCHAR optionValue[MAX_PATH] = {0};
+
+/*
+ * Java Preferences API encodes it's strings, so we need to match to make work with Java
+ * CAVEAT: Java also does unicode encoding which this doesn't do yet. Should be sufficient for jvm args.
+  * See WindowsPreferences.java toJavaName()
+  */
+TCHAR* convertWinRegToJava(TCHAR* key) {
+    TCHAR* windowsName = (TCHAR*) calloc((wcslen(key) + 1), sizeof (TCHAR)); //All caps could double size
+    TCHAR *returnValue = windowsName;
+
+    TCHAR ch;
+    int index = 0;
+    int len = wcslen(key);
+    for (; index < len; index++) {
+        ch = key[index];
+        if (ch == '/') {
+            if ((index + 1) < len &&
+                (key[index+1] >= 'A' && key[index+1] <= 'Z')) { 
+                    *windowsName = key[index+1];
+                    index++;
+            }
+            else if ((index + 1) < len && (key[index+1] == '/')) { 
+                *windowsName = '\\';
+                index++;
+            }
+        }
+        else if (ch == '\\') {
+            *windowsName = '/';
+        }
+        else {
+            *windowsName = ch;
+        }
+        windowsName++;
+    }
+    *windowsName = 0x0000;
+    return returnValue;
+}
+
+/*
+ * open the JVMUserOptions key if exists, otherwise create it and return hKey.
+ * Does weird Java Preferences Encoding
+ */
+LONG createRegKey(TCHAR* appid, HKEY *hKey) {
+    TCHAR buf[MAX_PATH] = {0};
+    TCHAR *p;
+
+    wcscat(buf, _T("SOFTWARE\\JavaSoft\\Prefs\\"));
+    p = convertKeyToWinReg(appid);
+    wcscat(buf, p);
+    free(p);
+    p = _T("\\/J/V/M/User/Options");
+    wcscat(buf, p);
+    LONG success = RegOpenKeyEx(HKEY_CURRENT_USER, buf, 0, KEY_READ | KEY_WRITE, hKey);
+    if (success == ERROR_FILE_NOT_FOUND) {
+        success = RegCreateKeyEx(HKEY_CURRENT_USER, buf, 0L, NULL, REG_OPTION_NON_VOLATILE, 
+            KEY_QUERY_VALUE | KEY_SET_VALUE , NULL, hKey, NULL);
+    }
+    return success;
+}
+
+/*
+ * Use arg->key to get user preference, if no user preference use arg key and value to create entry
+ */
+void getJvmUserArg(TCHAR* appid, JVMUserArg *arg) {
     HKEY hKey = 0;
-    HKEY vKey = 0;
     DWORD dwType, dwCount = MAX_VALUE_LENGTH * sizeof (TCHAR);
     TCHAR userValue[MAX_VALUE_LENGTH] = {0};
-    TCHAR* result = NULL;
 
-    if (splitOptionIntoNameValue(argvalue, optionName, sizeof (optionName), optionValue, sizeof (optionValue))) {
-        LONG success = createRegKey(appid, &hKey);
-        TCHAR* regOptionName = convertKeyToWinReg(optionName);
-        if (success == ERROR_SUCCESS) {
+    LONG success = createRegKey(appid, &hKey);
+    if (success == ERROR_SUCCESS) {
+        TCHAR* regOptionName = convertKeyToWinReg(arg->name);
+        if (regOptionName != NULL) {
             success = RegQueryValueEx(hKey, regOptionName, NULL, &dwType, (LPBYTE) userValue, &dwCount);
             if (success == ERROR_SUCCESS) {
-                wcscat(optionName, userValue);
-                result = wcsdup(optionName);
+                TCHAR *regValueName = convertWinRegToJava(userValue);
+                if (regValueName != NULL) {
+                    arg->value = wcsdup(regValueName);
+                   //Don't free regValueName arg->value requires malloc'ed pointer
+                }
+            }
+            else if (success == ERROR_FILE_NOT_FOUND) {
+                TCHAR *regValueName = convertKeyToWinReg(arg->value);
+                if (regValueName != NULL) {
+                    success = RegSetValueEx(hKey, regOptionName, NULL, 
+                        REG_SZ, (LPBYTE) regValueName, (wcslen(regValueName)+1)*sizeof(TCHAR));
+                    free(regValueName);
+                }
+            }
+            free(regOptionName);
+        }
+        RegCloseKey(hKey);
+    }
+}
+
+/*
+ * Get use options from either defaults or from user preferences in registry
+ */
+int addUserOptions(TCHAR* basedir, JavaVMOption* options, int cnt) {
+    JVMUserArg args[MAX_OPTIONS]; //Make sure we don't go over
+    JVMUserArgs jvmUserArgs;
+    TCHAR appid[LAUNCHER_MAXPATH + 1] = {0};
+    TCHAR argvalue[LAUNCHER_MAXPATH + 1] = {0};
+
+    jvmUserArgs.args = args;
+    jvmUserArgs.currentSize = 0;
+    jvmUserArgs.initialElements = 0;
+    jvmUserArgs.maxSize = MAX_OPTIONS - cnt;
+    memset(&args, 0, sizeof (JVMUserArg)*(MAX_OPTIONS - cnt));
+
+    //Add property to command line for preferences id
+    if (getConfigValue(basedir, CONFIG_APP_ID_KEY, appid, LAUNCHER_MAXPATH)) {
+        _stprintf_s(argvalue, LAUNCHER_MAXPATH, _T("-D%s=%s"), CONFIG_APP_ID_KEY, appid);
+
+        char* result = convertToDupedChar(argvalue);
+        if (result != NULL) {
+            //optionString is malloced
+            options[cnt].optionString = result;
+            cnt++;
+        }
+
+        JVMUserArgs_initializeDefaults(&jvmUserArgs, basedir);
+        //Copy all user args to options
+        int i;
+        for (i = 0; i < jvmUserArgs.currentSize; i++) {
+            getJvmUserArg(appid, &args[i]);
+
+            //optionString needs to be malloced - JVMUserArg_toString returns malloced string
+            char* jvmOption = JVMUserArg_toString(basedir, args[i], TRUE);
+            if (jvmOption != NULL) {
+                options[cnt].optionString = jvmOption;
+                cnt++;
             }
         }
-        free(regOptionName);
-
-        //If not found in registry just combine the two and return it
-        if (result == NULL) {
-            TCHAR* concatenated = (TCHAR*) calloc((wcslen(optionName) + wcslen(optionValue) + 1), sizeof (TCHAR));
-            wcscat(concatenated, optionName);
-            wcscat(concatenated, optionValue);
-            result = concatenated;
-        }
-    }        //This should not occur, but if there is no delimeter just treat as complete option
-    else {
-        result = wcsdup(argvalue);
+    } else {
+        printf("WARNING: %s not defined:", CONFIG_APP_ID_KEY);
     }
-    return result;
+    return cnt;
 }
 
 
-#define MAX_OPTIONS 100
-#define MAX_OPTION_NAME 50
 
 bool startJVM(TCHAR* basedir, TCHAR* appFolder, TCHAR* jar, int argCount, LPTSTR *szArgList) {
     TCHAR jvmPath[LAUNCHER_MAXPATH+1] = {0};
@@ -581,51 +718,17 @@
        _stprintf_s(argname, MAX_OPTION_NAME, _T("jvmarg.%d"), idx);
        found = getConfigValue(basedir, argname, argvalue, LAUNCHER_MAXPATH);
        if (found) {
-
             TCHAR* option = replaceStr(argvalue, _T("$APPDIR"), basedir);
-            if (wcstombs_s(&outlen, argvalueASCII, sizeof (argvalueASCII), option, wcslen(option) + 1) != 0) {
-                showError(_T("Failed converting JVM Argument to ASCII"), argname);
-                free(option);
-                return false;
+            char* jvmOption = convertToDupedChar(option);
+            if (jvmOption != NULL) {
+                options[cnt].optionString = jvmOption;
+                cnt++;
             }
-            free(option);
-            char* jvmOption = _strdup(argvalueASCII);
-            options[cnt].optionString = jvmOption;
             idx++;
-            cnt++;
         }
     } while (found && idx < MAX_OPTIONS);
 
-    found = getConfigValue(basedir, _T("app.id"), appid, LAUNCHER_MAXPATH);
-    if (found) {
-        _stprintf_s(argvalue, _T("-Dapp.id=%s"), appid);
-        wcstombs_s(&outlen, argvalueASCII, sizeof (argvalueASCII),
-                argvalue, wcslen(argvalue) + 1);
-        options[cnt].optionString = _strdup(argvalueASCII);
-        cnt++;
-
-        idx = 1;
-        do {
-            _stprintf_s(argname, MAX_OPTION_NAME, _T("jvmuserarg.%d"), idx);
-            found = getConfigValue(basedir, argname, argvalue, LAUNCHER_MAXPATH);
-            if (found) {
-
-                TCHAR* option = getJvmUserArg(appid, argvalue);
-                if (option != NULL) {
-                    if (wcstombs_s(&outlen, argvalueASCII, sizeof (argvalueASCII), option, wcslen(option) + 1) != 0) {
-                        showError(_T("Failed converting JVM Argument to ASCII"), argname);
-                        free(option);
-                        return false;
-                    }
-                    free(option);
-                    char* jvmOption = _strdup(argvalueASCII);
-                    options[cnt].optionString = jvmOption;
-                    cnt++;
-                }
-                idx++;
-            }
-        } while (found && idx < MAX_OPTIONS);
-    }
+	cnt = addUserOptions(basedir, options, cnt);
 
     jvmArgs.version = 0x00010002;
     jvmArgs.options = options;
--- a/deploy/packager/src/com/sun/javafx/tools/packager/bundlers/LinuxAppBundler.java	Wed Jun 19 13:57:16 2013 -0700
+++ b/deploy/packager/src/com/sun/javafx/tools/packager/bundlers/LinuxAppBundler.java	Fri Jun 21 17:43:06 2013 -0700
@@ -150,7 +150,7 @@
             System.out.println("Exception: "+ex);
             ex.printStackTrace();
             return false;
-        }
+        } 
         return true;
     }
 
@@ -198,11 +198,15 @@
         out.println("app.preferences.id="+ params.getPreferencesID());
 
         Map<String, String> jvmUserOptions = params.getAllJvmUserOptions();
-        //Only create jvmuserargs, if app.id (params.identifier) is set, otherwise we don't know where
-        //to create the jvm.cfg file - warning logged above
         idx = 1;
         for (Map.Entry<String, String> arg: jvmUserOptions.entrySet()) {
-            out.println("jvmuserarg."+idx+"="+arg.getKey()+"##"+arg.getValue());
+            if (arg.getKey() == null || arg.getValue() == null) {
+                Log.info("WARNING: a jvmuserarg has a null name or value");
+            }
+            else {
+                out.println("jvmuserarg."+idx+".name="+arg.getKey());
+                out.println("jvmuserarg."+idx+".value="+arg.getValue());
+            }
             idx++;
         }
        out.close();
--- a/deploy/packager/src/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java	Wed Jun 19 13:57:16 2013 -0700
+++ b/deploy/packager/src/com/sun/javafx/tools/packager/bundlers/WinAppBundler.java	Fri Jun 21 17:43:06 2013 -0700
@@ -273,7 +273,13 @@
         Map<String, String> overridableJVMOptions = params.getAllJvmUserOptions();
         idx = 1;
         for (Map.Entry<String, String> arg: overridableJVMOptions.entrySet()) {
-            out.println("jvmuserarg."+idx+"="+arg.getKey()+"##"+arg.getValue());
+            if (arg.getKey() == null || arg.getValue() == null) {
+                Log.info("WARNING: a jvmuserarg has a null name or value");
+            }
+            else {
+                out.println("jvmuserarg."+idx+".name="+arg.getKey());
+                out.println("jvmuserarg."+idx+".value="+arg.getValue());
+            }
             idx++;
         }
         out.close();